summaryrefslogtreecommitdiff
path: root/appl/lib/readjpg.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/lib/readjpg.b')
-rw-r--r--appl/lib/readjpg.b973
1 files changed, 973 insertions, 0 deletions
diff --git a/appl/lib/readjpg.b b/appl/lib/readjpg.b
new file mode 100644
index 00000000..d47c15d7
--- /dev/null
+++ b/appl/lib/readjpg.b
@@ -0,0 +1,973 @@
+implement RImagefile;
+
+include "sys.m";
+ sys: Sys;
+
+include "draw.m";
+
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+
+include "imagefile.m";
+
+# Constants, all preceded by byte 16rFF
+SOF: con byte 16rC0; # Start of Frame
+SOF2: con byte 16rC2; # Start of Frame; progressive Huffman
+JPG: con byte 16rC8; # Reserved for JPEG extensions
+DHT: con byte 16rC4; # Define Huffman Tables
+DAC: con byte 16rCC; # Arithmetic coding conditioning
+RST: con byte 16rD0; # Restart interval termination
+RST7: con byte 16rD7; # Restart interval termination (highest value)
+SOI: con byte 16rD8; # Start of Image
+EOI: con byte 16rD9; # End of Image
+SOS: con byte 16rDA; # Start of Scan
+DQT: con byte 16rDB; # Define quantization tables
+DNL: con byte 16rDC; # Define number of lines
+DRI: con byte 16rDD; # Define restart interval
+DHP: con byte 16rDE; # Define hierarchical progression
+EXP: con byte 16rDF; # Expand reference components
+APPn: con byte 16rE0; # Reserved for application segments
+JPGn: con byte 16rF0; # Reserved for JPEG extensions
+COM: con byte 16rFE; # Comment
+
+Header: adt
+{
+ fd: ref Iobuf;
+ ch: chan of (ref Rawimage, string);
+ # variables in i/o routines
+ sr: int; # shift register, right aligned
+ cnt: int; # # bits in right part of sr
+ buf: array of byte;
+ bufi: int;
+ nbuf: int;
+
+ Nf: int;
+ comp: array of Framecomp;
+ mode: byte;
+ X,Y: int;
+ qt: array of array of int; # quantization tables
+ dcht: array of ref Huffman;
+ acht: array of ref Huffman;
+ sf: array of byte; # start of frame; do better later
+ ss: array of byte; # start of scan; do better later
+ ri: int;
+};
+
+NBUF: con 16*1024;
+
+Huffman: adt
+{
+ bits: array of int;
+ size: array of int;
+ code: array of int;
+ val: array of int;
+ mincode: array of int;
+ maxcode: array of int;
+ valptr: array of int;
+ # fast lookup
+ value: array of int;
+ shift: array of int;
+};
+
+Framecomp: adt # Frame component specifier from SOF marker
+{
+ C: int;
+ H: int;
+ V: int;
+ Tq: int;
+};
+
+zerobytes: array of byte;
+zeroints: array of int;
+zeroreals: array of real;
+clamp: array of byte;
+NCLAMP: con 1000;
+CLAMPOFF: con 300;
+
+init(iomod: Bufio)
+{
+ if(sys == nil)
+ sys = load Sys Sys->PATH;
+ bufio = iomod;
+ zerobytes = array[8*8] of byte;
+ zeroints = array[8*8] of int;
+ zeroreals = array[8*8] of real;
+ for(k:=0; k<8*8; k++){
+ zerobytes[k] = byte 0;
+ zeroints[k] = 0;
+ zeroreals[k] = 0.0;
+ }
+ clamp = array[NCLAMP] of byte;
+ for(k=0; k<CLAMPOFF; k++)
+ clamp[k] = byte 0;
+ for(; k<CLAMPOFF+256; k++)
+ clamp[k] = byte(k-CLAMPOFF);
+ for(; k<NCLAMP; k++)
+ clamp[k] = byte 255;
+}
+
+read(fd: ref Iobuf): (ref Rawimage, string)
+{
+ # spawn a subprocess so I/O errors can clean up easily
+
+ ch := chan of (ref Rawimage, string);
+ spawn readslave(fd, ch);
+
+ return <-ch;
+}
+
+readmulti(fd: ref Iobuf): (array of ref Rawimage, string)
+{
+ (i, err) := read(fd);
+ if(i != nil){
+ a := array[1] of { i };
+ return (a, err);
+ }
+ return (nil, err);
+}
+
+readslave(fd: ref Iobuf, ch: chan of (ref Rawimage, string))
+{
+ image: ref Rawimage;
+
+ (header, err) := soiheader(fd, ch);
+ if(header == nil){
+ ch <-= (nil, err);
+ exit;
+ }
+ buf := header.buf;
+ nseg := 0;
+
+ Loop:
+ while(err == ""){
+ m: int;
+ b: array of byte;
+ nseg++;
+ (m, b, err) = readsegment(header);
+ case m{
+ -1 =>
+ break Loop;
+
+ int APPn+0 =>
+ if(nseg==1 && string b[0:4]=="JFIF"){ # JFIF header; check version
+ vers0 := int b[5];
+ vers1 := int b[6];
+ if(vers0>1 || vers1>2)
+ err = sys->sprint("ReadJPG: can't handle JFIF version %d.%2d", vers0, vers1);
+ }
+
+ int APPn+1 to int APPn+15 =>
+ ;
+
+ int DQT =>
+ err = quanttables(header, b);
+
+ int SOF =>
+ header.Y = int2(b, 1);
+ header.X = int2(b, 3);
+ header.Nf = int b[5];
+ header.comp = array[header.Nf] of Framecomp;
+ for(i:=0; i<header.Nf; i++){
+ header.comp[i].C = int b[6+3*i+0];
+ (H, V) := nibbles(b[6+3*i+1]);
+ header.comp[i].H = H;
+ header.comp[i].V = V;
+ header.comp[i].Tq = int b[6+3*i+2];
+ }
+ header.mode = SOF;
+ header.sf = b;
+
+ int SOF2 =>
+ err = sys->sprint("ReadJPG: can't handle progressive Huffman mode");
+ break Loop;
+
+ int SOS =>
+ header.ss = b;
+ (image, err) = decodescan(header);
+ if(err != "")
+ break Loop;
+
+ # BUG: THIS SHOULD USE THE LOOP TO FINISH UP
+ x := nextbyte(header, 1);
+ if(x != 16rFF)
+ err = sys->sprint("ReadJPG: didn't see marker at end of scan; saw %x", x);
+ else{
+ x = nextbyte(header, 1);
+ if(x != int EOI)
+ err = sys->sprint("ReadJPG: expected EOI saw %x", x);
+ }
+ break Loop;
+
+ int DHT =>
+ err = huffmantables(header, b);
+
+ int DRI =>
+ header.ri = int2(b, 0);
+
+ int COM =>
+ ;
+
+ int EOI =>
+ break Loop;
+
+ * =>
+ err = sys->sprint("ReadJPG: unknown marker %.2x", m);
+ }
+ }
+ ch <-= (image, err);
+}
+
+readerror(): string
+{
+ return sys->sprint("ReadJPG: read error: %r");
+}
+
+marker(buf: array of byte, n: int): byte
+{
+ if(buf[n] != byte 16rFF)
+ return byte 0;
+ return buf[n+1];
+}
+
+int2(buf: array of byte, n: int): int
+{
+ return (int buf[n]<<8)+(int buf[n+1]);
+}
+
+nibbles(b: byte): (int, int)
+{
+ i := int b;
+ return (i>>4, i&15);
+}
+
+soiheader(fd: ref Iobuf, ch: chan of (ref Rawimage, string)): (ref Header, string)
+{
+ # 1+ for restart preamble (see nextbyte), +1 for sentinel
+ buf := array[1+NBUF+1] of byte;
+ if(fd.read(buf, 2) != 2)
+ return (nil, sys->sprint("ReadJPG: can't read header: %r"));
+ if(marker(buf, 0) != SOI)
+ return (nil, sys->sprint("ReadJPG: unrecognized marker in header"));
+ h := ref Header;
+ h.buf = buf;
+ h.bufi = 0;
+ h.nbuf = 0;
+ h.fd = fd;
+ h.ri = 0;
+ h.ch = ch;
+ return (h, nil);
+}
+
+readsegment(h: ref Header): (int, array of byte, string)
+{
+ if(h.fd.read(h.buf, 2) != 2)
+ return (-1, nil, readerror());
+ m := int marker(h.buf, 0);
+ case m{
+ int EOI =>
+ return (m, nil, nil);
+ 0 =>
+ err := sys->sprint("ReadJPG: expecting marker; saw %.2x%.2x)",
+ int h.buf[0], int h.buf[1]);
+ return (-1, nil, err);
+ }
+ if(h.fd.read(h.buf, 2) != 2)
+ return (-1, nil, readerror());
+ n := int2(h.buf, 0);
+ if(n < 2)
+ return (-1, nil, readerror());
+ n -= 2;
+# if(n > len h.buf){
+# h.buf = array[n+1] of byte; # +1 for sentinel
+# #h.nbuf = n;
+# }
+ b := array[n] of byte;
+ if(h.fd.read(b, n) != n)
+ return (-1, nil, readerror());
+ return (m, b, nil);
+}
+
+huffmantables(h: ref Header, b: array of byte): string
+{
+ if(h.dcht == nil){
+ h.dcht = array[4] of ref Huffman;
+ h.acht = array[4] of ref Huffman;
+ }
+ err: string;
+ mt: int;
+ for(l:=0; l<len b; l+=17+mt){
+ (mt, err) = huffmantable(h, b[l:]);
+ if(err != nil)
+ return err;
+ }
+ return nil;
+}
+
+huffmantable(h: ref Header, b: array of byte): (int, string)
+{
+ t := ref Huffman;
+ (Tc, th) := nibbles(b[0]);
+ if(Tc > 1)
+ return (0, sys->sprint("ReadJPG: unknown Huffman table class %d", Tc));
+ if(th>3 || (h.mode==SOF && th>1))
+ return (0, sys->sprint("ReadJPG: unknown Huffman table index %d", th));
+ if(Tc == 0)
+ h.dcht[th] = t;
+ else
+ h.acht[th] = t;
+
+ # flow chart C-2
+ nsize := 0;
+ for(i:=0; i<16; i++)
+ nsize += int b[1+i];
+ t.size = array[nsize+1] of int;
+ k := 0;
+ for(i=1; i<=16; i++){
+ n := int b[i];
+ for(j:=0; j<n; j++)
+ t.size[k++] = i;
+ }
+ t.size[k] = 0;
+
+ # initialize HUFFVAL
+ t.val = array[nsize] of int;
+ for(i=0; i<nsize; i++){
+ t.val[i] = int b[17+i];
+ }
+
+ # flow chart C-3
+ t.code = array[nsize+1] of int;
+ k = 0;
+ code := 0;
+ si := t.size[0];
+ for(;;){
+ do
+ t.code[k++] = code++;
+ while(t.size[k] == si);
+ if(t.size[k] == 0)
+ break;
+ do{
+ code <<= 1;
+ si++;
+ }while(t.size[k] != si);
+ }
+
+ # flow chart F-25
+ t.mincode = array[17] of int;
+ t.maxcode = array[17] of int;
+ t.valptr = array[17] of int;
+ i = 0;
+ j := 0;
+ F25:
+ for(;;){
+ for(;;){
+ i++;
+ if(i > 16)
+ break F25;
+ if(int b[i] != 0)
+ break;
+ t.maxcode[i] = -1;
+ }
+ t.valptr[i] = j;
+ t.mincode[i] = t.code[j];
+ j += int b[i]-1;
+ t.maxcode[i] = t.code[j];
+ j++;
+ }
+
+ # create byte-indexed fast path tables
+ t.value = array[256] of int;
+ t.shift = array[256] of int;
+ maxcode := t.maxcode;
+ # stupid startup algorithm: just run machine for each byte value
+ Bytes:
+ for(v:=0; v<256; v++){
+ cnt := 7;
+ m := 1<<7;
+ code = 0;
+ sr := v;
+ i = 1;
+ for(;;i++){
+ if(sr & m)
+ code |= 1;
+ if(code <= maxcode[i])
+ break;
+ code <<= 1;
+ m >>= 1;
+ if(m == 0){
+ t.shift[v] = 0;
+ t.value[v] = -1;
+ continue Bytes;
+ }
+ cnt--;
+ }
+ t.shift[v] = 8-cnt;
+ t.value[v] = t.val[t.valptr[i]+(code-t.mincode[i])];
+ }
+
+ return (nsize, nil);
+}
+
+quanttables(h: ref Header, b: array of byte): string
+{
+ if(h.qt == nil)
+ h.qt = array[4] of array of int;
+ err: string;
+ n: int;
+ for(l:=0; l<len b; l+=1+n){
+ (n, err) = quanttable(h, b[l:]);
+ if(err != nil)
+ return err;
+ }
+ return nil;
+}
+
+quanttable(h: ref Header, b: array of byte): (int, string)
+{
+ (pq, tq) := nibbles(b[0]);
+ if(pq > 1)
+ return (0, sys->sprint("ReadJPG: unknown quantization table class %d", pq));
+ if(tq > 3)
+ return (0, sys->sprint("ReadJPG: unknown quantization table index %d", tq));
+ q := array[64] of int;
+ h.qt[tq] = q;
+ for(i:=0; i<64; i++){
+ if(pq == 0)
+ q[i] = int b[1+i];
+ else
+ q[i] = int2(b, 1+2*i);
+ }
+ return (64*(1+pq), nil);
+}
+
+zig := array[64] of {
+ 0, 1, 8, 16, 9, 2, 3, 10, 17, # 0-7
+ 24, 32, 25, 18, 11, 4, 5, # 8-15
+ 12, 19, 26, 33, 40, 48, 41, 34, # 16-23
+ 27, 20, 13, 6, 7, 14, 21, 28, # 24-31
+ 35, 42, 49, 56, 57, 50, 43, 36, # 32-39
+ 29, 22, 15, 23, 30, 37, 44, 51, # 40-47
+ 58, 59, 52, 45, 38, 31, 39, 46, # 48-55
+ 53, 60, 61, 54, 47, 55, 62, 63 # 56-63
+};
+
+decodescan(h: ref Header): (ref Rawimage, string)
+{
+ ss := h.ss;
+ Ns := int ss[0];
+ if((Ns!=3 && Ns!=1) || Ns!=h.Nf)
+ return (nil, "ReadJPG: can't handle scan not 3 components");
+
+ image := ref Rawimage;
+ image.r = ((0, 0), (h.X, h.Y));
+ image.cmap = nil;
+ image.transp = 0;
+ image.trindex = byte 0;
+ image.fields = 0;
+ image.chans = array[h.Nf] of array of byte;
+ if(Ns == 3)
+ image.chandesc = CRGB;
+ else
+ image.chandesc = CY;
+ image.nchans = h.Nf;
+ for(k:=0; k<h.Nf; k++)
+ image.chans[k] = array[h.X*h.Y] of byte;
+
+ # build per-component arrays
+ Td := array[Ns] of int;
+ Ta := array[Ns] of int;
+ data := array[Ns] of array of array of real;
+ H := array[Ns] of int;
+ V := array[Ns] of int;
+ DC := array[Ns] of int;
+
+ # compute maximum H and V
+ Hmax := 0;
+ Vmax := 0;
+ for(comp:=0; comp<Ns; comp++){
+ if(h.comp[comp].H > Hmax)
+ Hmax = h.comp[comp].H;
+ if(h.comp[comp].V > Vmax)
+ Vmax = h.comp[comp].V;
+ }
+
+ # initialize data structures
+ allHV1 := 1;
+ for(comp=0; comp<Ns; comp++){
+ # JPEG requires scan components to be in same order as in frame,
+ # so if both have 3 we know scan is Y Cb Cr and there's no need to
+ # reorder
+ cs := int ss[1+2*comp];
+ (Td[comp], Ta[comp]) = nibbles(ss[2+2*comp]);
+ H[comp] = h.comp[comp].H;
+ V[comp] = h.comp[comp].V;
+ nblock := H[comp]*V[comp];
+ if(nblock != 1)
+ allHV1 = 0;
+ data[comp] = array[nblock] of array of real;
+ DC[comp] = 0;
+ for(m:=0; m<nblock; m++)
+ data[comp][m] = array[8*8] of real;
+ }
+
+ ri := h.ri;
+
+ h.buf[0] = byte 16rFF; # see nextbyte()
+ h.cnt = 0;
+ h.sr = 0;
+ nacross := ((h.X+(8*Hmax-1))/(8*Hmax));
+ nmcu := ((h.Y+(8*Vmax-1))/(8*Vmax))*nacross;
+ zz := array[64] of real;
+ err := "";
+ for(mcu:=0; mcu<nmcu; ){
+ for(comp=0; comp<Ns; comp++){
+ dcht := h.dcht[Td[comp]];
+ acht := h.acht[Ta[comp]];
+ qt := h.qt[h.comp[comp].Tq];
+
+ for(block:=0; block<H[comp]*V[comp]; block++){
+ # F-22
+ t := decode(h, dcht);
+ diff := receive(h, t);
+ DC[comp] += diff;
+
+ # F-23
+ zz[0:] = zeroreals;
+ zz[0] = real (qt[0]*DC[comp]);
+ k = 1;
+ for(;;){
+ rs := decode(h, acht);
+ (rrrr, ssss) := nibbles(byte rs);
+ if(ssss == 0){
+ if(rrrr != 15)
+ break;
+ k += 16;
+ }else{
+ k += rrrr;
+ z := receive(h, ssss);
+ zz[zig[k]] = real (z*qt[k]);
+ if(k == 63)
+ break;
+ k++;
+ }
+ }
+
+ idct(zz, data[comp][block]);
+ }
+ }
+
+ # rotate colors to RGB and assign to bytes
+ if(Ns == 1) # very easy
+ colormap1(h, image, data[0][0], mcu, nacross);
+ else if(allHV1) # fairly easy
+ colormapall1(h, image, data[0][0], data[1][0], data[2][0], mcu, nacross);
+ else # miserable general case
+ colormap(h, image, data[0], data[1], data[2], mcu, nacross, Hmax, Vmax, H, V);
+
+ # process restart marker, if present
+ mcu++;
+ if(ri>0 && mcu<nmcu-1 && mcu%ri==0){
+ restart := mcu/ri-1;
+ rst, nskip: int;
+ nskip = 0;
+ do{
+ do{
+ rst = nextbyte(h, 1);
+ nskip++;
+ }while(rst>=0 && rst!=16rFF);
+ if(rst == 16rFF){
+ rst = nextbyte(h, 1);
+ nskip++;
+ }
+ }while(rst>=0 && (rst&~7)!=int RST);
+ if(nskip != 2)
+ err = sys->sprint("skipped %d bytes at restart %d\n", nskip-2, restart);
+ if(rst < 0)
+ return (nil, readerror());
+ if((rst&7) != (restart&7))
+ return (nil, sys->sprint("ReadJPG: expected RST%d got %d", restart&7, int rst&7));
+ h.cnt = 0;
+ h.sr = 0;
+ for(comp=0; comp<Ns; comp++)
+ DC[comp] = 0;
+ }
+ }
+ return (image, err);
+}
+
+colormap1(h: ref Header, image: ref Rawimage, data: array of real, mcu, nacross: int)
+{
+ pic := image.chans[0];
+ minx := 8*(mcu%nacross);
+ dx := 8;
+ if(minx+dx > h.X)
+ dx = h.X-minx;
+ miny := 8*(mcu/nacross);
+ dy := 8;
+ if(miny+dy > h.Y)
+ dy = h.Y-miny;
+ pici := miny*h.X+minx;
+ k := 0;
+ for(y:=0; y<dy; y++){
+ for(x:=0; x<dx; x++){
+ r := clamp[int (data[k+x]+128.)+CLAMPOFF];
+ pic[pici+x] = r;
+ }
+ pici += h.X;
+ k += 8;
+ }
+}
+
+colormapall1(h: ref Header, image: ref Rawimage, data0, data1, data2: array of real, mcu, nacross: int)
+{
+ rpic := image.chans[0];
+ gpic := image.chans[1];
+ bpic := image.chans[2];
+ minx := 8*(mcu%nacross);
+ dx := 8;
+ if(minx+dx > h.X)
+ dx = h.X-minx;
+ miny := 8*(mcu/nacross);
+ dy := 8;
+ if(miny+dy > h.Y)
+ dy = h.Y-miny;
+ pici := miny*h.X+minx;
+ k := 0;
+ for(y:=0; y<dy; y++){
+ for(x:=0; x<dx; x++){
+ Y := data0[k+x]+128.;
+ Cb := data1[k+x];
+ Cr := data2[k+x];
+ r := int (Y+1.402*Cr);
+ g := int (Y-0.34414*Cb-0.71414*Cr);
+ b := int (Y+1.772*Cb);
+ rpic[pici+x] = clamp[r+CLAMPOFF];
+ gpic[pici+x] = clamp[g+CLAMPOFF];
+ bpic[pici+x] = clamp[b+CLAMPOFF];
+ }
+ pici += h.X;
+ k += 8;
+ }
+}
+
+colormap(h: ref Header, image: ref Rawimage, data0, data1, data2: array of array of real, mcu, nacross, Hmax, Vmax: int, H, V: array of int)
+{
+ rpic := image.chans[0];
+ gpic := image.chans[1];
+ bpic := image.chans[2];
+ minx := 8*Hmax*(mcu%nacross);
+ dx := 8*Hmax;
+ if(minx+dx > h.X)
+ dx = h.X-minx;
+ miny := 8*Vmax*(mcu/nacross);
+ dy := 8*Vmax;
+ if(miny+dy > h.Y)
+ dy = h.Y-miny;
+ pici := miny*h.X+minx;
+ H0 := H[0];
+ H1 := H[1];
+ H2 := H[2];
+ for(y:=0; y<dy; y++){
+ t := y*V[0];
+ b0 := H0*(t/(8*Vmax));
+ y0 := 8*((t/Vmax)&7);
+ t = y*V[1];
+ b1 := H1*(t/(8*Vmax));
+ y1 := 8*((t/Vmax)&7);
+ t = y*V[2];
+ b2 := H2*(t/(8*Vmax));
+ y2 := 8*((t/Vmax)&7);
+ x0 := 0;
+ x1 := 0;
+ x2 := 0;
+ for(x:=0; x<dx; x++){
+ Y := data0[b0][y0+x0++*H0/Hmax]+128.;
+ Cb := data1[b1][y1+x1++*H1/Hmax];
+ Cr := data2[b2][y2+x2++*H2/Hmax];
+ if(x0*H0/Hmax >= 8){
+ x0 = 0;
+ b0++;
+ }
+ if(x1*H1/Hmax >= 8){
+ x1 = 0;
+ b1++;
+ }
+ if(x2*H2/Hmax >= 8){
+ x2 = 0;
+ b2++;
+ }
+ r := int (Y+1.402*Cr);
+ g := int (Y-0.34414*Cb-0.71414*Cr);
+ b := int (Y+1.772*Cb);
+ rpic[pici+x] = clamp[r+CLAMPOFF];
+ gpic[pici+x] = clamp[g+CLAMPOFF];
+ bpic[pici+x] = clamp[b+CLAMPOFF];
+ }
+ pici += h.X;
+ }
+}
+
+# decode next 8-bit value from entropy-coded input. chart F-26
+decode(h: ref Header, t: ref Huffman): int
+{
+ maxcode := t.maxcode;
+ if(h.cnt < 8)
+ nextbyte(h, 0);
+ # fast lookup
+ code := (h.sr>>(h.cnt-8))&16rFF;
+ v := t.value[code];
+ if(v >= 0){
+ h.cnt -= t.shift[code];
+ return v;
+ }
+
+ h.cnt -= 8;
+ if(h.cnt == 0)
+ nextbyte(h, 0);
+ h.cnt--;
+ cnt := h.cnt;
+ m := 1<<cnt;
+ sr := h.sr;
+ code <<= 1;
+ i := 9;
+ for(;;i++){
+ if(sr & m)
+ code |= 1;
+ if(code <= maxcode[i])
+ break;
+ code <<= 1;
+ m >>= 1;
+ if(m == 0){
+ sr = nextbyte(h, 0);
+ m = 16r80;
+ cnt = 8;
+ }
+ cnt--;
+ }
+ h.cnt = cnt;
+ return t.val[t.valptr[i]+(code-t.mincode[i])];
+}
+
+#
+# load next byte of input
+# we should really just call h.fd.getb(), but it's faster just to use Bufio
+# to load big chunks and manage our own byte-at-a-time input.
+#
+nextbyte(h: ref Header, marker: int): int
+{
+ b := int h.buf[h.bufi++];
+ if(b == 16rFF){
+ # check for sentinel at end of buffer
+ if(h.bufi >= h.nbuf){
+ underflow := (h.bufi > h.nbuf);
+ h.nbuf = h.fd.read(h.buf, NBUF);
+ if(h.nbuf <= 0){
+ h.ch <-= (nil, readerror());
+ exit;
+ }
+ h.buf[h.nbuf] = byte 16rFF;
+ h.bufi = 0;
+ if(underflow) # if ran off end of buffer, just restart
+ return nextbyte(h, marker);
+ }
+ if(marker)
+ return b;
+ b2 := h.buf[h.bufi++];
+ if(b2 != byte 0){
+ if(b2 == DNL){
+ h.ch <-= (nil, "ReadJPG: DNL marker unimplemented");
+ exit;
+ }else if(b2<RST && RST7<b2){
+ h.ch <-= (nil, sys->sprint("ReadJPG: unrecognized marker %x", int b2));
+ exit;
+ }
+ # decode is reading into restart marker; satisfy it and restore state
+ if(h.bufi < 2){
+ # misery: must shift up buffer
+ h.buf[1:] = h.buf[0:h.nbuf+1];
+ h.nbuf++;
+ h.buf[0] = byte 16rFF;
+ h.bufi -= 1;
+ }else
+ h.bufi -= 2;
+ b = 16rFF;
+ }
+ }
+ h.cnt += 8;
+ h.sr = (h.sr<<8)|b;
+ return b;
+}
+
+# return next s bits of input, MSB first, and level shift it
+receive(h: ref Header, s: int): int
+{
+ while(h.cnt < s)
+ nextbyte(h, 0);
+ v := h.sr >> (h.cnt-s);
+ m := (1<<s);
+ v &= m-1;
+ h.cnt -= s;
+ # level shift
+ if(v < (m>>1))
+ v += ~(m-1)+1;
+ return v;
+}
+
+# IDCT based on Arai, Agui, and Nakajima, using flow chart Figure 4.8
+# of Pennebaker & Mitchell, JPEG: Still Image Data Compression Standard.
+# Remember IDCT is reverse of flow of DCT.
+
+a0: con 1.414;
+a1: con 0.707;
+a2: con 0.541;
+a3: con 0.707;
+a4: con 1.307;
+a5: con -0.383;
+
+# scaling factors from eqn 4-35 of P&M
+s1: con 1.0196;
+s2: con 1.0823;
+s3: con 1.2026;
+s4: con 1.4142;
+s5: con 1.8000;
+s6: con 2.6131;
+s7: con 5.1258;
+
+# overall normalization of 1/16, folded into premultiplication on vertical pass
+scale: con 0.0625;
+
+idct(zin: array of real, zout: array of real)
+{
+ x, y: int;
+
+ r := array[8*8] of real;
+
+ # transform horizontally
+ for(y=0; y<8; y++){
+ eighty := y<<3;
+ # if all non-DC components are zero, just propagate the DC term
+ if(zin[eighty+1]==0.)
+ if(zin[eighty+2]==0. && zin[eighty+3]==0.)
+ if(zin[eighty+4]==0. && zin[eighty+5]==0.)
+ if(zin[eighty+6]==0. && zin[eighty+7]==0.){
+ v := zin[eighty]*a0;
+ r[eighty+0] = v;
+ r[eighty+1] = v;
+ r[eighty+2] = v;
+ r[eighty+3] = v;
+ r[eighty+4] = v;
+ r[eighty+5] = v;
+ r[eighty+6] = v;
+ r[eighty+7] = v;
+ continue;
+ }
+
+ # step 5
+ in1 := s1*zin[eighty+1];
+ in3 := s3*zin[eighty+3];
+ in5 := s5*zin[eighty+5];
+ in7 := s7*zin[eighty+7];
+ f2 := s2*zin[eighty+2];
+ f3 := s6*zin[eighty+6];
+ f5 := (in1+in7);
+ f7 := (in5+in3);
+
+ # step 4
+ g2 := f2-f3;
+ g4 := (in5-in3);
+ g6 := (in1-in7);
+ g7 := f5+f7;
+
+ # step 3.5
+ t := (g4+g6)*a5;
+
+ # step 3
+ f0 := a0*zin[eighty+0];
+ f1 := s4*zin[eighty+4];
+ f3 += f2;
+ f2 = a1*g2;
+
+ # step 2
+ g0 := f0+f1;
+ g1 := f0-f1;
+ g3 := f2+f3;
+ g4 = t-a2*g4;
+ g5 := a3*(f5-f7);
+ g6 = a4*g6+t;
+
+ # step 1
+ f0 = g0+g3;
+ f1 = g1+f2;
+ f2 = g1-f2;
+ f3 = g0-g3;
+ f5 = g5-g4;
+ f6 := g5+g6;
+ f7 = g6+g7;
+
+ # step 6
+ r[eighty+0] = (f0+f7);
+ r[eighty+1] = (f1+f6);
+ r[eighty+2] = (f2+f5);
+ r[eighty+3] = (f3-g4);
+ r[eighty+4] = (f3+g4);
+ r[eighty+5] = (f2-f5);
+ r[eighty+6] = (f1-f6);
+ r[eighty+7] = (f0-f7);
+ }
+
+ # transform vertically
+ for(x=0; x<8; x++){
+ # step 5
+ in1 := scale*s1*r[x+8];
+ in3 := scale*s3*r[x+24];
+ in5 := scale*s5*r[x+40];
+ in7 := scale*s7*r[x+56];
+ f2 := scale*s2*r[x+16];
+ f3 := scale*s6*r[x+48];
+ f5 := (in1+in7);
+ f7 := (in5+in3);
+
+ # step 4
+ g2 := f2-f3;
+ g4 := (in5-in3);
+ g6 := (in1-in7);
+ g7 := f5+f7;
+
+ # step 3.5
+ t := (g4+g6)*a5;
+
+ # step 3
+ f0 := scale*a0*r[x];
+ f1 := scale*s4*r[x+32];
+ f3 += f2;
+ f2 = a1*g2;
+
+ # step 2
+ g0 := f0+f1;
+ g1 := f0-f1;
+ g3 := f2+f3;
+ g4 = t-a2*g4;
+ g5 := a3*(f5-f7);
+ g6 = a4*g6+t;
+
+ # step 1
+ f0 = g0+g3;
+ f1 = g1+f2;
+ f2 = g1-f2;
+ f3 = g0-g3;
+ f5 = g5-g4;
+ f6 := g5+g6;
+ f7 = g6+g7;
+
+ # step 6
+ zout[x] = (f0+f7);
+ zout[x+8] = (f1+f6);
+ zout[x+16] = (f2+f5);
+ zout[x+24] = (f3-g4);
+ zout[x+32] = (f3+g4);
+ zout[x+40] = (f2-f5);
+ zout[x+48] = (f1-f6);
+ zout[x+56] = (f0-f7);
+ }
+}