ref: fe1eb39db7ae6904924f3ab1f6f9b34416f2eb1b
dir: /sys/src/cmd/jpg/gif.c/
#include <u.h> #include <libc.h> #include <bio.h> #include <draw.h> #include <event.h> #include <keyboard.h> #include "imagefile.h" int cflag = 0; int dflag = 0; int eflag = 0; int nineflag = 0; int threeflag = 0; int output = 0; ulong outchan = CMAP8; Image **allims; int which; int defaultcolor = 1; enum{ Border = 2, Edge = 5 }; char *show(int, char*); Rectangle imager(void) { Point p1, p2; if(allims==nil || allims[0]==nil) return screen->r; p1 = addpt(divpt(subpt(allims[0]->r.max, allims[0]->r.min), 2), allims[0]->r.min); p2 = addpt(divpt(subpt(screen->clipr.max, screen->clipr.min), 2), screen->clipr.min); return rectaddpt(allims[0]->r, subpt(p2, p1)); } void eresized(int new) { Rectangle r; if(new && getwindow(display, Refnone) < 0){ fprint(2, "gif: can't reattach to window\n"); exits("resize"); } if(allims==nil || allims[which]==nil) return; r = imager(); border(screen, r, -Border, nil, ZP); draw(screen, r, allims[which], nil, allims[which]->r.min); flushimage(display, 1); } void main(int argc, char *argv[]) { int fd, i; char *err; ARGBEGIN{ case '3': /* produce encoded, compressed, three-color bitmap file; no display by default */ threeflag++; /* fall through */ case 't': /* produce encoded, compressed, true-color bitmap file; no display by default */ cflag++; dflag++; output++; defaultcolor = 0; outchan = RGB24; break; case 'c': /* produce encoded, compressed, bitmap file; no display by default */ cflag++; dflag++; output++; if(defaultcolor) outchan = CMAP8; break; case 'd': /* suppress display of image */ dflag++; break; case 'e': /* disable floyd-steinberg error diffusion */ eflag++; break; case 'k': /* force black and white */ defaultcolor = 0; outchan = GREY8; break; case 'v': /* force RGBV */ defaultcolor = 0; outchan = CMAP8; break; case '9': /* produce plan 9, uncompressed, bitmap file; no display by default */ nineflag++; dflag++; output++; if(defaultcolor) outchan = CMAP8; break; default: fprint(2, "usage: gif -39cdektv [file.gif ...]\n"); exits("usage"); }ARGEND; err = nil; if(argc == 0) err = show(0, "<stdin>"); else{ for(i=0; i<argc; i++){ fd = open(argv[i], OREAD); if(fd < 0){ fprint(2, "gif: can't open %s: %r\n", argv[i]); err = "open"; }else{ err = show(fd, argv[i]); close(fd); } if(output && argc>1 && err==nil){ fprint(2, "gif: exiting after one file\n"); break; } } } exits(err); } Image* transparency(Rawimage *r, char *name) { Image *i; int j, index; uchar *pic, *mpic, *mask; if((r->gifflags&TRANSP) == 0) return nil; i = allocimage(display, r->r, GREY8, 0, 0); if(i == nil){ fprint(2, "gif: allocimage for mask of %s failed: %r\n", name); return nil; } pic = r->chans[0]; mask = malloc(r->chanlen); if(mask == nil){ fprint(2, "gif: malloc for mask of %s failed: %r\n", name); freeimage(i); return nil; } index = r->giftrindex; mpic = mask; for(j=0; j<r->chanlen; j++) if(*pic++ == index) *mpic++ = 0; else *mpic++ = 0xFF; if(loadimage(i, i->r, mask, r->chanlen) < 0){ fprint(2, "gif: loadimage for mask of %s failed: %r\n", name); free(mask); freeimage(i); return nil; } free(mask); return i; } /* interleave alpha values of 0xFF in data stream. alpha value comes first, then b g r */ uchar* expand(uchar *u, int chanlen, int nchan) { int j, k; uchar *v, *up, *vp; v = malloc(chanlen*(nchan+1)); if(v == nil){ fprint(2, "gif: malloc fails: %r\n"); exits("malloc"); } up = u; vp = v; for(j=0; j<chanlen; j++){ *vp++ = 0xFF; for(k=0; k<nchan; k++) *vp++ = *up++; } return v; } void addalpha(Rawimage *i) { char buf[32]; switch(outchan){ case CMAP8: i->chans[0] = expand(i->chans[0], i->chanlen/1, 1); i->chanlen = 2*(i->chanlen/1); i->chandesc = CRGBVA16; outchan = CHAN2(CMap, 8, CAlpha, 8); break; case GREY8: i->chans[0] = expand(i->chans[0], i->chanlen/1, 1); i->chanlen = 2*(i->chanlen/1); i->chandesc = CYA16; outchan = CHAN2(CGrey, 8, CAlpha, 8); break; case RGB24: i->chans[0] = expand(i->chans[0], i->chanlen/3, 3); i->chanlen = 4*(i->chanlen/3); i->chandesc = CRGBA32; outchan = RGBA32; break; default: chantostr(buf, outchan); fprint(2, "gif: can't add alpha to type %s\n", buf); exits("err"); } } /* * Called only when writing output. If the output is RGBA32, * we must write four bytes per pixel instead of two. * There's always at least two: data plus alpha. * r is used only for reference; the image is already in c. */ void blackout(Rawimage *r, Rawimage *c) { int i, trindex; uchar *rp, *cp; rp = r->chans[0]; cp = c->chans[0]; trindex = r->giftrindex; if(outchan == RGBA32) for(i=0; i<r->chanlen; i++){ if(*rp == trindex){ *cp++ = 0x00; *cp++ = 0x00; *cp++ = 0x00; *cp++ = 0x00; }else{ *cp++ = 0xFF; cp += 3; } rp++; } else for(i=0; i<r->chanlen; i++){ if(*rp == trindex){ *cp++ = 0x00; *cp++ = 0x00; }else{ *cp++ = 0xFF; cp++; } rp++; } } int init(void) { static int inited; if(inited == 0){ if(initdraw(0, 0, 0) < 0){ fprint(2, "gif: initdraw failed: %r\n"); return -1; } einit(Ekeyboard|Emouse); inited++; } return 1; } char* show(int fd, char *name) { Rawimage **images, **rgbv; Image *tmp, *msk, *img, *dst, **ims; Rectangle r; int j, k, n, ch, nloop, loopcount, dt; char *err; char buf[32]; err = nil; images = readgif(fd, CRGB, dflag); if(images == nil){ fprint(2, "gif: decode %s failed: %r\n", name); return "decode"; } for(n=0; images[n]; n++) if(n == 0) r = images[n]->r; else combinerect(&r, images[n]->r); tmp = nil; ims = malloc((n+1)*sizeof(Image*)); rgbv = malloc((n+1)*sizeof(Rawimage*)); if(rgbv==nil || ims==nil){ fprint(2, "gif: malloc of masks for %s failed: %r\n", name); err = "malloc"; goto Return; } memset(ims, 0, (n+1)*sizeof(Image*)); memset(rgbv, 0, (n+1)*sizeof(Rawimage*)); if(!dflag){ if(init() < 0){ err = "initdraw"; goto Return; } if(defaultcolor && screen->depth>8) outchan = RGB24; } for(k=0; k<n; k++){ if(outchan == CMAP8) rgbv[k] = torgbv(images[k], !eflag); else{ if(outchan==GREY8 || (images[k]->chandesc==CY && threeflag==0)) rgbv[k] = totruecolor(images[k], CY); else rgbv[k] = totruecolor(images[k], CRGB24); } if(rgbv[k] == nil){ fprint(2, "gif: converting %s to local format failed: %r\n", name); err = "torgbv"; goto Return; } if(!dflag){ msk = transparency(images[k], name); if(rgbv[k]->chandesc == CY) img = allocimage(display, rgbv[k]->r, GREY8, 0, 0); else img = allocimage(display, rgbv[k]->r, outchan, 0, 0); if(tmp == nil) tmp = allocimage(display, r, img->chan, 0, DWhite); ims[k]= dst = allocimage(display, r, tmp->chan, 0, DWhite); if(tmp == nil || img == nil || dst == nil){ fprint(2, "gif: allocimage %s failed: %r\n", name); err = "allocimage"; goto Return; } if(loadimage(img, img->r, rgbv[k]->chans[0], rgbv[k]->chanlen) < 0){ fprint(2, "gif: loadimage %s failed: %r\n", name); err = "loadimage"; goto Return; } switch((images[k]->gifflags>>2)&7){ default: draw(tmp, img->r, img, msk, img->r.min); draw(dst, tmp->r, tmp, nil, tmp->r.min); break; case 2: draw(tmp, img->r, display->white, msk, img->r.min); /* no break */ case 3: draw(dst, tmp->r, tmp, nil, tmp->r.min); draw(dst, img->r, img, msk, img->r.min); break; } freeimage(msk); freeimage(img); } } if(tmp) freeimage(tmp); allims = ims; loopcount = images[0]->gifloopcount; if(!dflag){ nloop = 0; do{ for(k=0; k<n; k++){ which = k; eresized(0); dt = images[k]->gifdelay*10; if(dt < 50) dt = 50; while(n==1 || ecankbd()){ /* an odd, democratic list */ if((ch=ekbd())=='q' || ch==Kdel || ch==Keof) exits(nil); if(ch == '\n') goto Out; }sleep(dt); } /* loopcount -1 means no loop (this code's rule), loopcount 0 means loop forever (netscape's rule)*/ }while(loopcount==0 || ++nloop<loopcount); /* loop count has run out */ ekbd(); Out: draw(screen, screen->clipr, display->white, nil, ZP); } if(nineflag){ if(images[0]->gifflags&TRANSP){ addalpha(rgbv[0]); blackout(images[0], rgbv[0]); } chantostr(buf, outchan); print("%11s %11d %11d %11d %11d ", buf, rgbv[0]->r.min.x, rgbv[0]->r.min.y, rgbv[0]->r.max.x, rgbv[0]->r.max.y); if(write(1, rgbv[0]->chans[0], rgbv[0]->chanlen) != rgbv[0]->chanlen){ fprint(2, "gif: %s: write error %r\n", name); return "write"; } }else if(cflag){ if(images[0]->gifflags&TRANSP){ addalpha(rgbv[0]); blackout(images[0], rgbv[0]); } if(writerawimage(1, rgbv[0]) < 0){ fprint(2, "gif: %s: write error: %r\n", name); return "write"; } } Return: allims = nil; for(k=0; images[k]; k++){ for(j=0; j<images[k]->nchans; j++) free(images[k]->chans[j]); free(images[k]->cmap); if(rgbv[k]) free(rgbv[k]->chans[0]); freeimage(ims[k]); free(images[k]); free(rgbv[k]); } free(images); free(ims); return err; }