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 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 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) return (-1, nil, "ReadJPG: segment too large"); 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 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 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 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 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; comp0 && mcu=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 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 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 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= 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<>= 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(b2sprint("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<>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); } }