implement WImagefile; include "sys.m"; sys: Sys; include "draw.m"; draw: Draw; Chans, Display, Image, Rect: import draw; include "bufio.m"; bufio: Bufio; Iobuf: import bufio; include "imagefile.m"; Nhash: con 4001; Entry: adt { index: int; prefix: int; exten: int; next: cyclic ref Entry; }; IO: adt { fd: ref Iobuf; buf: array of byte; i: int; nbits: int; # bits in right side of shift register sreg: int; # shift register }; tbl: array of ref Entry; colormap: array of array of byte; log2 := array[] of {1 => 0, 2 => 1, 4 => 2, 8 => 3, * => -1}; init(iomod: Bufio) { if(sys == nil){ sys = load Sys Sys->PATH; draw = load Draw Draw->PATH; } bufio = iomod; } writeimage(fd: ref Iobuf, image: ref Image): string { case image.chans.desc { (Draw->GREY1).desc or (Draw->GREY2).desc or (Draw->GREY4).desc or (Draw->GREY8).desc or (Draw->CMAP8).desc => if(image.depth > 8 || (image.depth&(image.depth-1)) != 0) return "inconsistent depth"; * => return "unsupported channel type"; } inittbl(); writeheader(fd, image); writedescriptor(fd, image); err := writedata(fd, image); if(err != nil) return err; writetrailer(fd); fd.flush(); return err; } inittbl() { tbl = array[4096] of ref Entry; for(i:=0; iGREY8)) ldepth = 4; fd.write(colormap[ldepth], len colormap[ldepth]); return nil; } # Write image descriptor writedescriptor(fd: ref Iobuf, image: ref Image) { # Image Separator fd.putb(byte 16r2C); # Left, top, width, height put2(fd, 0); put2(fd, 0); put2(fd, image.r.dx()); put2(fd, image.r.dy()); # no special processing fd.putb(byte 0); } # Write data writedata(fd: ref Iobuf, image: ref Image): string { # LZW Minimum code size if(image.depth == 1) fd.putb(byte 2); else fd.putb(byte image.depth); # Encode and emit the data err := encode(fd, image); if(err != nil) return err; # Block Terminator fd.putb(byte 0); return nil; } # Write data writetrailer(fd: ref Iobuf) { fd.putb(byte 16r3B); } # Write little-endian 16-bit integer put2(fd: ref Iobuf, i: int) { fd.putb(byte i); fd.putb(byte (i>>8)); } # Get color map for all ldepths, in format suitable for writing out getcolormap(image: ref Draw->Image) { if(colormap != nil) return; colormap = array[5] of array of byte; display := image.display; colormap[4] = array[3*256] of byte; colormap[3] = array[3*256] of byte; colormap[2] = array[3*16] of byte; colormap[1] = array[3*4] of byte; colormap[0] = array[3*2] of byte; c := colormap[4]; for(i:=0; i<256; i++){ c[3*i+0] = byte i; c[3*i+1] = byte i; c[3*i+2] = byte i; } c = colormap[3]; for(i=0; i<256; i++){ (r, g, b) := display.cmap2rgb(i); c[3*i+0] = byte r; c[3*i+1] = byte g; c[3*i+2] = byte b; } c = colormap[2]; for(i=0; i<16; i++){ col := (i<<4)|i; (r, g, b) := display.cmap2rgb(col); c[3*i+0] = byte r; c[3*i+1] = byte g; c[3*i+2] = byte b; } c = colormap[1]; for(i=0; i<4; i++){ col := (i<<6)|(i<<4)|(i<<2)|i; (r, g, b) := display.cmap2rgb(col); c[3*i+0] = byte r; c[3*i+1] = byte g; c[3*i+2] = byte b; } c = colormap[0]; for(i=0; i<2; i++){ if(i == 0) col := 0; else col = 16rFF; (r, g, b) := display.cmap2rgb(col); c[3*i+0] = byte r; c[3*i+1] = byte g; c[3*i+2] = byte b; } } # Put n bits of c into output at io.buf[i]; output(io: ref IO, c, n: int) { if(c < 0){ if(io.nbits != 0) io.buf[io.i++] = byte io.sreg; io.fd.putb(byte io.i); io.fd.write(io.buf, io.i); io.nbits = 0; return; } if(io.nbits+n >= 31){ sys->print("panic: WriteGIF sr overflow\n"); exit; } io.sreg |= c<= 8){ io.buf[io.i++] = byte io.sreg; io.sreg >>= 8; io.nbits -= 8; } if(io.i >= 255){ io.fd.putb(byte 255); io.fd.write(io.buf, 255); io.buf[0:] = io.buf[255:io.i]; io.i -= 255; } } # LZW encoder encode(fd: ref Iobuf, image: ref Image): string { c, h, csize, prefix: int; e, oe: ref Entry; first := 1; ld := log2[image.depth]; # ldepth 0 must generate codesize 2 with values 0 and 1 (see the spec.) ld0 := ld; if(ld0 == 0) ld0 = 1; codesize := (1<>(3-log2[image.depth])))] of byte; ndata := image.readpixels(image.r, data); if(ndata < 0) return sys->sprint("WriteGIF: readpixels: %r"); datai := 0; x := image.r.min.x; Init: for(;;){ csize = codesize+1; nentry := EOD+1; maxentry := (1<>ld))<>nbits & pm; h = prefix<<24 | c<<8; h %= Nhash; if(h < 0) h += Nhash; oe = nil; for(e = hash[h]; e!=nil; e=e.next){ if(e.prefix == prefix && e.exten == c){ if(oe != nil){ oe.next = e.next; e.next = hash[h]; hash[h] = e; } prefix = e.index; continue Next; } oe = e; } output(io, prefix, csize); early:=0; # peculiar tiff feature here for reference if(nentry == maxentry-early){ if(csize == 12){ nbits += codesize; # unget pixel x--; output(io, CTM, csize); continue Init; } csize++; maxentry = (1<