ref: 01338076d1a3610858f41fcbcdc7e46fb1109b77
dir: /appl/lib/readgif.b/
implement RImagefile; include "sys.m"; sys: Sys; include "draw.m"; include "bufio.m"; bufio: Bufio; Iobuf: import bufio; include "imagefile.m"; Header: adt { fd: ref Iobuf; buf: array of byte; vers: string; screenw: int; screenh: int; fields: int; bgrnd: int; aspect: int; transp: int; trindex: byte; }; Entry: adt { prefix: int; exten: int; }; tbl: array of Entry; init(iomod: Bufio) { if(sys == nil) sys = load Sys Sys->PATH; bufio = iomod; } read(fd: ref Iobuf): (ref Rawimage, string) { (a, err) := readarray(fd, 0); if(a != nil) return (a[0], err); return (nil, err); } readmulti(fd: ref Iobuf): (array of ref Rawimage, string) { return readarray(fd, 1); } readarray(fd: ref Iobuf, multi: int): (array of ref Rawimage, string) { inittbl(); buf := array[3*256] of byte; (header, err) := readheader(fd, buf); if(header == nil) return (nil, err); globalcmap: array of byte; if(header.fields & 16r80){ (globalcmap, err) = readcmap(header, (header.fields&7)+1); if(globalcmap == nil) return (nil, err); } images: array of ref Rawimage; new: ref Rawimage; Loop: for(;;){ case c := fd.getb(){ Bufio->EOF => if(err == "") err = "ReadGIF: premature EOF"; break Loop; Bufio->ERROR => err = sys->sprint("ReadGIF: read error: %r"); return (nil, err); 16r21 => # Extension (ignored) err = skipextension(header); if(err != nil) return (nil, err); 16r2C => # Image Descriptor if(!multi && images!=nil) # why read the rest? break Loop; (new, err) = readimage(header); if(new == nil) return (nil ,err); if(new.fields & 16r80){ (new.cmap, err) = readcmap(header, (new.fields&7)+1); if(new.cmap == nil) return (nil, err); }else new.cmap = globalcmap; (new.chans[0], err) = decode(header, new); if(new.chans[0] == nil) return (nil, err); if(new.fields & 16r40) interlace(new); new.transp = header.transp; new.trindex = header.trindex; nimages := array[len images+1] of ref Rawimage; nimages[0:] = images[0:]; nimages[len images] = new; images = nimages; 16r3B => # Trailer break Loop; * => err = sys->sprint("ReadGIF: unknown block type: %x", c); break Loop; } } if(images==nil || images[0].chans[0] == nil){ if(err == nil) err = "ReadGIF: no picture in file"; return (nil, err); } return (images, err); } readheader(fd: ref Iobuf, buf: array of byte): (ref Header, string) { if(fd.read(buf, 13) != 13){ err := sys->sprint("ReadGIF: can't read header: %r"); return (nil, err); } h := ref Header; h.vers = string buf[0:6]; if(h.vers!="GIF87a" && h.vers!="GIF89a"){ err := sys->sprint("ReadGIF: can't recognize format %s", h.vers); return (nil, err); } h.screenw = int buf[6]+(int buf[7]<<8); h.screenh = int buf[8]+(int buf[9]<<8); h.fields = int buf[10]; h.bgrnd = int buf[11]; h.aspect = int buf[12]; h.fd = fd; h.buf = buf; h.transp = 0; return (h, ""); } readcmap(h: ref Header, size: int): (array of byte,string) { size = 3*(1<<size); map := array[size] of byte; if(h.fd.read(map, size) != size) return (nil, "ReadGIF: short read on color map"); return (map, ""); } readimage(h: ref Header): (ref Rawimage, string) { if(h.fd.read(h.buf, 9) != 9){ err := sys->sprint("ReadGIF: can't read image descriptor: %r"); return (nil, err); } i := ref Rawimage; left := int h.buf[0]+(int h.buf[1]<<8); top := int h.buf[2]+(int h.buf[3]<<8); width := int h.buf[4]+(int h.buf[5]<<8); height := int h.buf[6]+(int h.buf[7]<<8); i.fields = int h.buf[8]; i.r.min.x = left; i.r.min.y = top; i.r.max.x = left+width; i.r.max.y = top+height; i.nchans = 1; i.chans = array[1] of array of byte; i.chandesc = CRGB1; return (i, ""); } readdata(h: ref Header, ch: chan of (array of byte, string)) { err: string; # send nil for error, buffer of length 0 for EOF for(;;){ nbytes := h.fd.getb(); if(nbytes < 0){ err = sys->sprint("ReadGIF: can't read data: %r"); ch <-= (nil, err); return; } d := array[nbytes] of byte; if(nbytes == 0){ ch <-= (d, ""); return; } n := h.fd.read(d, nbytes); if(n != nbytes){ if(n > 0){ ch <-= (d[0:n], nil); ch <-= (d[0:0], "ReadGIF: short data subblock"); }else ch <-= (nil, sys->sprint("ReadGIF: can't read data: %r")); return; } ch <-= (d, ""); } } readerr: con "ReadGIF: can't read extension: %r"; skipextension(h: ref Header): string { fmterr: con "ReadGIF: bad extension format"; hsize := 0; hasdata := 0; case h.fd.getb(){ Bufio->ERROR or Bufio->EOF => return sys->sprint(readerr); 16r01 => # Plain Text Extension hsize = 13; hasdata = 1; 16rF9 => # Graphic Control Extension return graphiccontrol(h); 16rFE => # Comment Extension hasdata = 1; 16rFF => # Application Extension hsize = h.fd.getb(); # standard says this must be 11, but Adobe likes to put out 10-byte ones, # so we pay attention to the field. hasdata = 1; * => return "ReadGIF: unknown extension"; } if(hsize>0 && h.fd.read(h.buf, hsize) != hsize) return sys->sprint(readerr); if(!hasdata){ if(int h.buf[hsize-1] != 0) return fmterr; }else{ ch := chan of (array of byte, string); spawn readdata(h, ch); for(;;){ (data, err) := <-ch; if(data == nil) return err; if(len data == 0) break; } } return ""; } graphiccontrol(h: ref Header): string { if(h.fd.read(h.buf, 5+1) != 5+1) return sys->sprint(readerr); if(int h.buf[1] & 1){ h.transp = 1; h.trindex = h.buf[4]; } return ""; } inittbl() { tbl = array[4096] of Entry; for(i:=0; i<258; i++) { tbl[i].prefix = -1; tbl[i].exten = i; } } decode(h: ref Header, i: ref Rawimage): (array of byte, string) { c, incode: int; err := ""; if(h.fd.read(h.buf, 1) != 1){ err = sys->sprint("ReadGIF: can't read data: %r"); return (nil, err); } codesize := int h.buf[0]; if(codesize>8 || 0>codesize){ err = sys->sprint("ReadGIF: can't handle codesize %d", codesize); return (nil, err); } err1 := ""; if(i.cmap!=nil && len i.cmap!=3*(1<<codesize) && (codesize!=2 || len i.cmap!=3*2)) # peculiar GIF bitmap files... err1 = sys->sprint("ReadGIF: codesize %d doesn't match color map 3*%d", codesize, len i.cmap/3); ch := chan of (array of byte, string); spawn readdata(h, ch); CTM :=1<<codesize; EOD := CTM+1; pic := array[(i.r.max.x-i.r.min.x)*(i.r.max.y-i.r.min.y)] of byte; pici := 0; data := array[0] of byte; datai := 0; nbits := 0; sreg := 0; stack := array[4096] of byte; stacki: int; fc := 0; Init: for(;;){ csize := codesize+1; nentry := EOD+1; maxentry := (1<<csize)-1; first := 1; ocode := -1; for(;; ocode = incode) { while(nbits < csize) { if(datai == len data){ (data, err) = <-ch; if(data == nil) return (nil, err); if(err!="" && err1=="") err1 = err; if(len data == 0) break Init; datai = 0; } c = int data[datai++]; sreg |= c<<nbits; nbits += 8; } code := sreg & ((1<<csize) - 1); sreg >>= csize; nbits -= csize; if(code == EOD){ (data, err) = <-ch; if(len data != 0) err = "ReadGIF: unexpected data past EOD"; if(err!="" && err1=="") err1 = err; break Init; } if(code == CTM) continue Init; stacki = len stack-1; incode = code; # special case for KwKwK if(code == nentry) { stack[stacki--] = byte fc; code = ocode; } if(code > nentry) { err = sys->sprint("ReadGIF: bad code %x %x", code, nentry); return (nil, err); } for(c=code; c>=0; c=tbl[c].prefix) stack[stacki--] = byte tbl[c].exten; nb := len stack-(stacki+1); if(pici+nb > len pic){ if(err1 == "") err1 = "ReadGIF: data overflows picture"; }else{ pic[pici:] = stack[stacki+1:]; pici += nb; } fc = int stack[stacki+1]; if(first){ first = 0; continue; } early:=0; # peculiar tiff feature here for reference if(nentry == maxentry-early) { if(csize >= 12) continue; csize++; maxentry = (1<<csize); if(csize < 12) maxentry--; } tbl[nentry].prefix = ocode; tbl[nentry].exten = fc; nentry++; } } return (pic, err1); } interlace(image: ref Rawimage) { pic := image.chans[0]; r := image.r; dx := r.max.x-r.min.x; ipic := array[dx*(r.max.y-r.min.y)] of byte; # Group 1: every 8th row, starting with row 0 yy := 0; for(y:=r.min.y; y<r.max.y; y+=8){ ipic[y*dx:] = pic[yy*dx:(yy+1)*dx]; yy++; } # Group 2: every 8th row, starting with row 4 for(y=r.min.y+4; y<r.max.y; y+=8){ ipic[y*dx:] = pic[yy*dx:(yy+1)*dx]; yy++; } # Group 3: every 4th row, starting with row 2 for(y=r.min.y+2; y<r.max.y; y+=4){ ipic[y*dx:] = pic[yy*dx:(yy+1)*dx]; yy++; } # Group 4: every 2nd row, starting with row 1 for(y=r.min.y+1; y<r.max.y; y+=2){ ipic[y*dx:] = pic[yy*dx:(yy+1)*dx]; yy++; } image.chans[0] = ipic; }