1*37da2899SCharles.Forsythimplement WImagefile; 2*37da2899SCharles.Forsyth 3*37da2899SCharles.Forsythinclude "sys.m"; 4*37da2899SCharles.Forsyth sys: Sys; 5*37da2899SCharles.Forsyth 6*37da2899SCharles.Forsythinclude "draw.m"; 7*37da2899SCharles.Forsyth draw: Draw; 8*37da2899SCharles.Forsyth Chans, Display, Image, Rect: import draw; 9*37da2899SCharles.Forsyth 10*37da2899SCharles.Forsythinclude "bufio.m"; 11*37da2899SCharles.Forsyth bufio: Bufio; 12*37da2899SCharles.Forsyth Iobuf: import bufio; 13*37da2899SCharles.Forsyth 14*37da2899SCharles.Forsythinclude "imagefile.m"; 15*37da2899SCharles.Forsyth 16*37da2899SCharles.ForsythNhash: con 4001; 17*37da2899SCharles.Forsyth 18*37da2899SCharles.ForsythEntry: adt 19*37da2899SCharles.Forsyth{ 20*37da2899SCharles.Forsyth index: int; 21*37da2899SCharles.Forsyth prefix: int; 22*37da2899SCharles.Forsyth exten: int; 23*37da2899SCharles.Forsyth next: cyclic ref Entry; 24*37da2899SCharles.Forsyth}; 25*37da2899SCharles.Forsyth 26*37da2899SCharles.ForsythIO: adt 27*37da2899SCharles.Forsyth{ 28*37da2899SCharles.Forsyth fd: ref Iobuf; 29*37da2899SCharles.Forsyth buf: array of byte; 30*37da2899SCharles.Forsyth i: int; 31*37da2899SCharles.Forsyth nbits: int; # bits in right side of shift register 32*37da2899SCharles.Forsyth sreg: int; # shift register 33*37da2899SCharles.Forsyth}; 34*37da2899SCharles.Forsyth 35*37da2899SCharles.Forsythtbl: array of ref Entry; 36*37da2899SCharles.Forsyth 37*37da2899SCharles.Forsythcolormap: array of array of byte; 38*37da2899SCharles.Forsythlog2 := array[] of {1 => 0, 2 => 1, 4 => 2, 8 => 3, * => -1}; 39*37da2899SCharles.Forsyth 40*37da2899SCharles.Forsythinit(iomod: Bufio) 41*37da2899SCharles.Forsyth{ 42*37da2899SCharles.Forsyth if(sys == nil){ 43*37da2899SCharles.Forsyth sys = load Sys Sys->PATH; 44*37da2899SCharles.Forsyth draw = load Draw Draw->PATH; 45*37da2899SCharles.Forsyth } 46*37da2899SCharles.Forsyth bufio = iomod; 47*37da2899SCharles.Forsyth} 48*37da2899SCharles.Forsyth 49*37da2899SCharles.Forsythwriteimage(fd: ref Iobuf, image: ref Image): string 50*37da2899SCharles.Forsyth{ 51*37da2899SCharles.Forsyth case image.chans.desc { 52*37da2899SCharles.Forsyth (Draw->GREY1).desc or (Draw->GREY2).desc or 53*37da2899SCharles.Forsyth (Draw->GREY4).desc or (Draw->GREY8).desc or 54*37da2899SCharles.Forsyth (Draw->CMAP8).desc => 55*37da2899SCharles.Forsyth if(image.depth > 8 || (image.depth&(image.depth-1)) != 0) 56*37da2899SCharles.Forsyth return "inconsistent depth"; 57*37da2899SCharles.Forsyth * => 58*37da2899SCharles.Forsyth return "unsupported channel type"; 59*37da2899SCharles.Forsyth } 60*37da2899SCharles.Forsyth 61*37da2899SCharles.Forsyth inittbl(); 62*37da2899SCharles.Forsyth 63*37da2899SCharles.Forsyth writeheader(fd, image); 64*37da2899SCharles.Forsyth writedescriptor(fd, image); 65*37da2899SCharles.Forsyth 66*37da2899SCharles.Forsyth err := writedata(fd, image); 67*37da2899SCharles.Forsyth if(err != nil) 68*37da2899SCharles.Forsyth return err; 69*37da2899SCharles.Forsyth 70*37da2899SCharles.Forsyth writetrailer(fd); 71*37da2899SCharles.Forsyth fd.flush(); 72*37da2899SCharles.Forsyth return err; 73*37da2899SCharles.Forsyth} 74*37da2899SCharles.Forsyth 75*37da2899SCharles.Forsythinittbl() 76*37da2899SCharles.Forsyth{ 77*37da2899SCharles.Forsyth tbl = array[4096] of ref Entry; 78*37da2899SCharles.Forsyth for(i:=0; i<len tbl; i++) 79*37da2899SCharles.Forsyth tbl[i] = ref Entry(i, -1, i, nil); 80*37da2899SCharles.Forsyth} 81*37da2899SCharles.Forsyth 82*37da2899SCharles.Forsyth# Write header, logical screen descriptor, and color map 83*37da2899SCharles.Forsythwriteheader(fd: ref Iobuf, image: ref Image): string 84*37da2899SCharles.Forsyth{ 85*37da2899SCharles.Forsyth # Header 86*37da2899SCharles.Forsyth fd.puts("GIF89a"); 87*37da2899SCharles.Forsyth 88*37da2899SCharles.Forsyth # Logical Screen Descriptor 89*37da2899SCharles.Forsyth put2(fd, image.r.dx()); 90*37da2899SCharles.Forsyth put2(fd, image.r.dy()); 91*37da2899SCharles.Forsyth # color table present, 4 bits per color (for RGBV best case), size of color map 92*37da2899SCharles.Forsyth fd.putb(byte ((1<<7)|(3<<4)|(image.depth-1))); 93*37da2899SCharles.Forsyth fd.putb(byte 0); # white background (doesn't matter anyway) 94*37da2899SCharles.Forsyth fd.putb(byte 0); # pixel aspect ratio - unused 95*37da2899SCharles.Forsyth 96*37da2899SCharles.Forsyth # Global Color Table 97*37da2899SCharles.Forsyth getcolormap(image); 98*37da2899SCharles.Forsyth ldepth := log2[image.depth]; 99*37da2899SCharles.Forsyth if(image.chans.eq(Draw->GREY8)) 100*37da2899SCharles.Forsyth ldepth = 4; 101*37da2899SCharles.Forsyth fd.write(colormap[ldepth], len colormap[ldepth]); 102*37da2899SCharles.Forsyth return nil; 103*37da2899SCharles.Forsyth} 104*37da2899SCharles.Forsyth 105*37da2899SCharles.Forsyth# Write image descriptor 106*37da2899SCharles.Forsythwritedescriptor(fd: ref Iobuf, image: ref Image) 107*37da2899SCharles.Forsyth{ 108*37da2899SCharles.Forsyth # Image Separator 109*37da2899SCharles.Forsyth fd.putb(byte 16r2C); 110*37da2899SCharles.Forsyth 111*37da2899SCharles.Forsyth # Left, top, width, height 112*37da2899SCharles.Forsyth put2(fd, 0); 113*37da2899SCharles.Forsyth put2(fd, 0); 114*37da2899SCharles.Forsyth put2(fd, image.r.dx()); 115*37da2899SCharles.Forsyth put2(fd, image.r.dy()); 116*37da2899SCharles.Forsyth # no special processing 117*37da2899SCharles.Forsyth fd.putb(byte 0); 118*37da2899SCharles.Forsyth} 119*37da2899SCharles.Forsyth 120*37da2899SCharles.Forsyth# Write data 121*37da2899SCharles.Forsythwritedata(fd: ref Iobuf, image: ref Image): string 122*37da2899SCharles.Forsyth{ 123*37da2899SCharles.Forsyth # LZW Minimum code size 124*37da2899SCharles.Forsyth if(image.depth == 1) 125*37da2899SCharles.Forsyth fd.putb(byte 2); 126*37da2899SCharles.Forsyth 127*37da2899SCharles.Forsyth else 128*37da2899SCharles.Forsyth fd.putb(byte image.depth); 129*37da2899SCharles.Forsyth 130*37da2899SCharles.Forsyth # Encode and emit the data 131*37da2899SCharles.Forsyth err := encode(fd, image); 132*37da2899SCharles.Forsyth if(err != nil) 133*37da2899SCharles.Forsyth return err; 134*37da2899SCharles.Forsyth 135*37da2899SCharles.Forsyth # Block Terminator 136*37da2899SCharles.Forsyth fd.putb(byte 0); 137*37da2899SCharles.Forsyth return nil; 138*37da2899SCharles.Forsyth} 139*37da2899SCharles.Forsyth 140*37da2899SCharles.Forsyth# Write data 141*37da2899SCharles.Forsythwritetrailer(fd: ref Iobuf) 142*37da2899SCharles.Forsyth{ 143*37da2899SCharles.Forsyth fd.putb(byte 16r3B); 144*37da2899SCharles.Forsyth} 145*37da2899SCharles.Forsyth 146*37da2899SCharles.Forsyth# Write little-endian 16-bit integer 147*37da2899SCharles.Forsythput2(fd: ref Iobuf, i: int) 148*37da2899SCharles.Forsyth{ 149*37da2899SCharles.Forsyth fd.putb(byte i); 150*37da2899SCharles.Forsyth fd.putb(byte (i>>8)); 151*37da2899SCharles.Forsyth} 152*37da2899SCharles.Forsyth 153*37da2899SCharles.Forsyth# Get color map for all ldepths, in format suitable for writing out 154*37da2899SCharles.Forsythgetcolormap(image: ref Draw->Image) 155*37da2899SCharles.Forsyth{ 156*37da2899SCharles.Forsyth if(colormap != nil) 157*37da2899SCharles.Forsyth return; 158*37da2899SCharles.Forsyth colormap = array[5] of array of byte; 159*37da2899SCharles.Forsyth display := image.display; 160*37da2899SCharles.Forsyth colormap[4] = array[3*256] of byte; 161*37da2899SCharles.Forsyth colormap[3] = array[3*256] of byte; 162*37da2899SCharles.Forsyth colormap[2] = array[3*16] of byte; 163*37da2899SCharles.Forsyth colormap[1] = array[3*4] of byte; 164*37da2899SCharles.Forsyth colormap[0] = array[3*2] of byte; 165*37da2899SCharles.Forsyth c := colormap[4]; 166*37da2899SCharles.Forsyth for(i:=0; i<256; i++){ 167*37da2899SCharles.Forsyth c[3*i+0] = byte i; 168*37da2899SCharles.Forsyth c[3*i+1] = byte i; 169*37da2899SCharles.Forsyth c[3*i+2] = byte i; 170*37da2899SCharles.Forsyth } 171*37da2899SCharles.Forsyth c = colormap[3]; 172*37da2899SCharles.Forsyth for(i=0; i<256; i++){ 173*37da2899SCharles.Forsyth (r, g, b) := display.cmap2rgb(i); 174*37da2899SCharles.Forsyth c[3*i+0] = byte r; 175*37da2899SCharles.Forsyth c[3*i+1] = byte g; 176*37da2899SCharles.Forsyth c[3*i+2] = byte b; 177*37da2899SCharles.Forsyth } 178*37da2899SCharles.Forsyth c = colormap[2]; 179*37da2899SCharles.Forsyth for(i=0; i<16; i++){ 180*37da2899SCharles.Forsyth col := (i<<4)|i; 181*37da2899SCharles.Forsyth (r, g, b) := display.cmap2rgb(col); 182*37da2899SCharles.Forsyth c[3*i+0] = byte r; 183*37da2899SCharles.Forsyth c[3*i+1] = byte g; 184*37da2899SCharles.Forsyth c[3*i+2] = byte b; 185*37da2899SCharles.Forsyth } 186*37da2899SCharles.Forsyth c = colormap[1]; 187*37da2899SCharles.Forsyth for(i=0; i<4; i++){ 188*37da2899SCharles.Forsyth col := (i<<6)|(i<<4)|(i<<2)|i; 189*37da2899SCharles.Forsyth (r, g, b) := display.cmap2rgb(col); 190*37da2899SCharles.Forsyth c[3*i+0] = byte r; 191*37da2899SCharles.Forsyth c[3*i+1] = byte g; 192*37da2899SCharles.Forsyth c[3*i+2] = byte b; 193*37da2899SCharles.Forsyth } 194*37da2899SCharles.Forsyth c = colormap[0]; 195*37da2899SCharles.Forsyth for(i=0; i<2; i++){ 196*37da2899SCharles.Forsyth if(i == 0) 197*37da2899SCharles.Forsyth col := 0; 198*37da2899SCharles.Forsyth else 199*37da2899SCharles.Forsyth col = 16rFF; 200*37da2899SCharles.Forsyth (r, g, b) := display.cmap2rgb(col); 201*37da2899SCharles.Forsyth c[3*i+0] = byte r; 202*37da2899SCharles.Forsyth c[3*i+1] = byte g; 203*37da2899SCharles.Forsyth c[3*i+2] = byte b; 204*37da2899SCharles.Forsyth } 205*37da2899SCharles.Forsyth} 206*37da2899SCharles.Forsyth 207*37da2899SCharles.Forsyth# Put n bits of c into output at io.buf[i]; 208*37da2899SCharles.Forsythoutput(io: ref IO, c, n: int) 209*37da2899SCharles.Forsyth{ 210*37da2899SCharles.Forsyth if(c < 0){ 211*37da2899SCharles.Forsyth if(io.nbits != 0) 212*37da2899SCharles.Forsyth io.buf[io.i++] = byte io.sreg; 213*37da2899SCharles.Forsyth io.fd.putb(byte io.i); 214*37da2899SCharles.Forsyth io.fd.write(io.buf, io.i); 215*37da2899SCharles.Forsyth io.nbits = 0; 216*37da2899SCharles.Forsyth return; 217*37da2899SCharles.Forsyth } 218*37da2899SCharles.Forsyth 219*37da2899SCharles.Forsyth if(io.nbits+n >= 31){ 220*37da2899SCharles.Forsyth sys->print("panic: WriteGIF sr overflow\n"); 221*37da2899SCharles.Forsyth exit; 222*37da2899SCharles.Forsyth } 223*37da2899SCharles.Forsyth io.sreg |= c<<io.nbits; 224*37da2899SCharles.Forsyth io.nbits += n; 225*37da2899SCharles.Forsyth 226*37da2899SCharles.Forsyth while(io.nbits >= 8){ 227*37da2899SCharles.Forsyth io.buf[io.i++] = byte io.sreg; 228*37da2899SCharles.Forsyth io.sreg >>= 8; 229*37da2899SCharles.Forsyth io.nbits -= 8; 230*37da2899SCharles.Forsyth } 231*37da2899SCharles.Forsyth 232*37da2899SCharles.Forsyth if(io.i >= 255){ 233*37da2899SCharles.Forsyth io.fd.putb(byte 255); 234*37da2899SCharles.Forsyth io.fd.write(io.buf, 255); 235*37da2899SCharles.Forsyth io.buf[0:] = io.buf[255:io.i]; 236*37da2899SCharles.Forsyth io.i -= 255; 237*37da2899SCharles.Forsyth } 238*37da2899SCharles.Forsyth} 239*37da2899SCharles.Forsyth 240*37da2899SCharles.Forsyth# LZW encoder 241*37da2899SCharles.Forsythencode(fd: ref Iobuf, image: ref Image): string 242*37da2899SCharles.Forsyth{ 243*37da2899SCharles.Forsyth c, h, csize, prefix: int; 244*37da2899SCharles.Forsyth e, oe: ref Entry; 245*37da2899SCharles.Forsyth 246*37da2899SCharles.Forsyth first := 1; 247*37da2899SCharles.Forsyth ld := log2[image.depth]; 248*37da2899SCharles.Forsyth # ldepth 0 must generate codesize 2 with values 0 and 1 (see the spec.) 249*37da2899SCharles.Forsyth ld0 := ld; 250*37da2899SCharles.Forsyth if(ld0 == 0) 251*37da2899SCharles.Forsyth ld0 = 1; 252*37da2899SCharles.Forsyth codesize := (1<<ld0); 253*37da2899SCharles.Forsyth CTM := 1<<codesize; 254*37da2899SCharles.Forsyth EOD := CTM+1; 255*37da2899SCharles.Forsyth 256*37da2899SCharles.Forsyth io := ref IO (fd, array[300] of byte, 0, 0, 0); 257*37da2899SCharles.Forsyth sreg := 0; 258*37da2899SCharles.Forsyth nbits := 0; 259*37da2899SCharles.Forsyth bitsperpixel := 1<<ld; 260*37da2899SCharles.Forsyth pm := (1<<bitsperpixel)-1; 261*37da2899SCharles.Forsyth 262*37da2899SCharles.Forsyth # Read image data into memory 263*37da2899SCharles.Forsyth # potentially one extra byte on each end of each scan line 264*37da2899SCharles.Forsyth data := array[image.r.dy()*(2+(image.r.dx()>>(3-log2[image.depth])))] of byte; 265*37da2899SCharles.Forsyth ndata := image.readpixels(image.r, data); 266*37da2899SCharles.Forsyth if(ndata < 0) 267*37da2899SCharles.Forsyth return sys->sprint("WriteGIF: readpixels: %r"); 268*37da2899SCharles.Forsyth datai := 0; 269*37da2899SCharles.Forsyth x := image.r.min.x; 270*37da2899SCharles.Forsyth 271*37da2899SCharles.ForsythInit: 272*37da2899SCharles.Forsyth for(;;){ 273*37da2899SCharles.Forsyth csize = codesize+1; 274*37da2899SCharles.Forsyth nentry := EOD+1; 275*37da2899SCharles.Forsyth maxentry := (1<<csize); 276*37da2899SCharles.Forsyth hash := array[Nhash] of ref Entry; 277*37da2899SCharles.Forsyth for(i := 0; i<nentry; i++){ 278*37da2899SCharles.Forsyth e = tbl[i]; 279*37da2899SCharles.Forsyth h = (e.prefix<<24) | (e.exten<<8); 280*37da2899SCharles.Forsyth h %= Nhash; 281*37da2899SCharles.Forsyth if(h < 0) 282*37da2899SCharles.Forsyth h += Nhash; 283*37da2899SCharles.Forsyth e.next = hash[h]; 284*37da2899SCharles.Forsyth hash[h] = e; 285*37da2899SCharles.Forsyth } 286*37da2899SCharles.Forsyth prefix = -1; 287*37da2899SCharles.Forsyth if(first) 288*37da2899SCharles.Forsyth output(io, CTM, csize); 289*37da2899SCharles.Forsyth first = 0; 290*37da2899SCharles.Forsyth 291*37da2899SCharles.Forsyth # Scan over pixels. Because of partially filled bytes on ends of scan lines, 292*37da2899SCharles.Forsyth # which must be ignored in the data stream passed to GIF, this is more 293*37da2899SCharles.Forsyth # complex than we'd like 294*37da2899SCharles.Forsyth Next: 295*37da2899SCharles.Forsyth for(;;){ 296*37da2899SCharles.Forsyth if(ld != 3){ 297*37da2899SCharles.Forsyth # beginning of scan line is difficult; prime the shift register 298*37da2899SCharles.Forsyth if(x == image.r.min.x){ 299*37da2899SCharles.Forsyth if(datai == ndata) 300*37da2899SCharles.Forsyth break; 301*37da2899SCharles.Forsyth sreg = int data[datai++]; 302*37da2899SCharles.Forsyth nbits = 8-((x&(7>>ld))<<ld); 303*37da2899SCharles.Forsyth } 304*37da2899SCharles.Forsyth x++; 305*37da2899SCharles.Forsyth if(x == image.r.max.x) 306*37da2899SCharles.Forsyth x = image.r.min.x; 307*37da2899SCharles.Forsyth } 308*37da2899SCharles.Forsyth if(nbits == 0){ 309*37da2899SCharles.Forsyth if(datai == ndata) 310*37da2899SCharles.Forsyth break; 311*37da2899SCharles.Forsyth sreg = int data[datai++]; 312*37da2899SCharles.Forsyth nbits = 8; 313*37da2899SCharles.Forsyth } 314*37da2899SCharles.Forsyth nbits -= bitsperpixel; 315*37da2899SCharles.Forsyth c = sreg>>nbits & pm; 316*37da2899SCharles.Forsyth h = prefix<<24 | c<<8; 317*37da2899SCharles.Forsyth h %= Nhash; 318*37da2899SCharles.Forsyth if(h < 0) 319*37da2899SCharles.Forsyth h += Nhash; 320*37da2899SCharles.Forsyth oe = nil; 321*37da2899SCharles.Forsyth for(e = hash[h]; e!=nil; e=e.next){ 322*37da2899SCharles.Forsyth if(e.prefix == prefix && e.exten == c){ 323*37da2899SCharles.Forsyth if(oe != nil){ 324*37da2899SCharles.Forsyth oe.next = e.next; 325*37da2899SCharles.Forsyth e.next = hash[h]; 326*37da2899SCharles.Forsyth hash[h] = e; 327*37da2899SCharles.Forsyth } 328*37da2899SCharles.Forsyth prefix = e.index; 329*37da2899SCharles.Forsyth continue Next; 330*37da2899SCharles.Forsyth } 331*37da2899SCharles.Forsyth oe = e; 332*37da2899SCharles.Forsyth } 333*37da2899SCharles.Forsyth 334*37da2899SCharles.Forsyth output(io, prefix, csize); 335*37da2899SCharles.Forsyth early:=0; # peculiar tiff feature here for reference 336*37da2899SCharles.Forsyth if(nentry == maxentry-early){ 337*37da2899SCharles.Forsyth if(csize == 12){ 338*37da2899SCharles.Forsyth nbits += codesize; # unget pixel 339*37da2899SCharles.Forsyth x--; 340*37da2899SCharles.Forsyth output(io, CTM, csize); 341*37da2899SCharles.Forsyth continue Init; 342*37da2899SCharles.Forsyth } 343*37da2899SCharles.Forsyth csize++; 344*37da2899SCharles.Forsyth maxentry = (1<<csize); 345*37da2899SCharles.Forsyth } 346*37da2899SCharles.Forsyth 347*37da2899SCharles.Forsyth e = tbl[nentry]; 348*37da2899SCharles.Forsyth e.prefix = prefix; 349*37da2899SCharles.Forsyth e.exten = c; 350*37da2899SCharles.Forsyth e.next = hash[h]; 351*37da2899SCharles.Forsyth hash[h] = e; 352*37da2899SCharles.Forsyth 353*37da2899SCharles.Forsyth prefix = c; 354*37da2899SCharles.Forsyth nentry++; 355*37da2899SCharles.Forsyth } 356*37da2899SCharles.Forsyth break Init; 357*37da2899SCharles.Forsyth } 358*37da2899SCharles.Forsyth output(io, prefix, csize); 359*37da2899SCharles.Forsyth output(io, EOD, csize); 360*37da2899SCharles.Forsyth output(io, -1, csize); 361*37da2899SCharles.Forsyth return nil; 362*37da2899SCharles.Forsyth} 363