From 37da2899f40661e3e9631e497da8dc59b971cbd0 Mon Sep 17 00:00:00 2001 From: "Charles.Forsyth" Date: Fri, 22 Dec 2006 17:07:39 +0000 Subject: 20060303a --- appl/lib/writegif.b | 362 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 362 insertions(+) create mode 100644 appl/lib/writegif.b (limited to 'appl/lib/writegif.b') diff --git a/appl/lib/writegif.b b/appl/lib/writegif.b new file mode 100644 index 00000000..3990ed63 --- /dev/null +++ b/appl/lib/writegif.b @@ -0,0 +1,362 @@ +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<