diff options
Diffstat (limited to 'appl/lib/readgif.b')
| -rw-r--r-- | appl/lib/readgif.b | 442 |
1 files changed, 442 insertions, 0 deletions
diff --git a/appl/lib/readgif.b b/appl/lib/readgif.b new file mode 100644 index 00000000..3c3bb486 --- /dev/null +++ b/appl/lib/readgif.b @@ -0,0 +1,442 @@ +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 XX := 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; +} |
