17dd7cddfSDavid du Colombier #include <u.h>
27dd7cddfSDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <draw.h>
47dd7cddfSDavid du Colombier #include <memdraw.h>
57dd7cddfSDavid du Colombier #include <bio.h>
67dd7cddfSDavid du Colombier #include "imagefile.h"
77dd7cddfSDavid du Colombier
87dd7cddfSDavid du Colombier enum
97dd7cddfSDavid du Colombier {
107dd7cddfSDavid du Colombier Nhash = 4001,
117dd7cddfSDavid du Colombier Nbuf = 300,
127dd7cddfSDavid du Colombier };
137dd7cddfSDavid du Colombier
147dd7cddfSDavid du Colombier typedef struct Entry Entry;
157dd7cddfSDavid du Colombier typedef struct IO IO;
167dd7cddfSDavid du Colombier
177dd7cddfSDavid du Colombier
187dd7cddfSDavid du Colombier struct Entry
197dd7cddfSDavid du Colombier {
207dd7cddfSDavid du Colombier int index;
217dd7cddfSDavid du Colombier int prefix;
227dd7cddfSDavid du Colombier int exten;
237dd7cddfSDavid du Colombier Entry *next;
247dd7cddfSDavid du Colombier };
257dd7cddfSDavid du Colombier
267dd7cddfSDavid du Colombier struct IO
277dd7cddfSDavid du Colombier {
287dd7cddfSDavid du Colombier Biobuf *fd;
297dd7cddfSDavid du Colombier uchar buf[Nbuf];
307dd7cddfSDavid du Colombier int i;
317dd7cddfSDavid du Colombier int nbits; /* bits in right side of shift register */
327dd7cddfSDavid du Colombier int sreg; /* shift register */
337dd7cddfSDavid du Colombier };
347dd7cddfSDavid du Colombier
3559cc4ca5SDavid du Colombier static Rectangle mainrect;
367dd7cddfSDavid du Colombier static Entry tbl[4096];
377dd7cddfSDavid du Colombier static uchar *colormap[5]; /* one for each ldepth: GREY1 GREY2 GREY4 CMAP8=rgbv plus GREY8 */
387dd7cddfSDavid du Colombier #define GREYMAP 4
397dd7cddfSDavid du Colombier static int colormapsize[] = { 2, 4, 16, 256, 256 }; /* 2 for zero is an odd property of GIF */
407dd7cddfSDavid du Colombier
417dd7cddfSDavid du Colombier static void writeheader(Biobuf*, Rectangle, int, ulong, int);
427dd7cddfSDavid du Colombier static void writedescriptor(Biobuf*, Rectangle);
437dd7cddfSDavid du Colombier static char* writedata(Biobuf*, Image*, Memimage*);
447dd7cddfSDavid du Colombier static void writetrailer(Biobuf *fd);
457dd7cddfSDavid du Colombier static void writecomment(Biobuf *fd, char*);
467dd7cddfSDavid du Colombier static void writegraphiccontrol(Biobuf *fd, int, int);
477dd7cddfSDavid du Colombier static void* gifmalloc(ulong);
487dd7cddfSDavid du Colombier static void encode(Biobuf*, Rectangle, int, uchar*, uint);
497dd7cddfSDavid du Colombier
507dd7cddfSDavid du Colombier static
517dd7cddfSDavid du Colombier char*
startgif0(Biobuf * fd,ulong chan,Rectangle r,int depth,int loopcount)527dd7cddfSDavid du Colombier startgif0(Biobuf *fd, ulong chan, Rectangle r, int depth, int loopcount)
537dd7cddfSDavid du Colombier {
547dd7cddfSDavid du Colombier int i;
557dd7cddfSDavid du Colombier
567dd7cddfSDavid du Colombier for(i=0; i<nelem(tbl); i++)
577dd7cddfSDavid du Colombier tbl[i] = (Entry){i, -1, i, nil};
587dd7cddfSDavid du Colombier
597dd7cddfSDavid du Colombier switch(chan){
607dd7cddfSDavid du Colombier case GREY1:
617dd7cddfSDavid du Colombier case GREY2:
627dd7cddfSDavid du Colombier case GREY4:
637dd7cddfSDavid du Colombier case CMAP8:
647dd7cddfSDavid du Colombier case GREY8:
657dd7cddfSDavid du Colombier break;
667dd7cddfSDavid du Colombier default:
677dd7cddfSDavid du Colombier return "WriteGIF: can't handle channel type";
687dd7cddfSDavid du Colombier }
697dd7cddfSDavid du Colombier
7059cc4ca5SDavid du Colombier mainrect = r;
717dd7cddfSDavid du Colombier writeheader(fd, r, depth, chan, loopcount);
727dd7cddfSDavid du Colombier return nil;
737dd7cddfSDavid du Colombier }
747dd7cddfSDavid du Colombier
757dd7cddfSDavid du Colombier char*
startgif(Biobuf * fd,Image * image,int loopcount)767dd7cddfSDavid du Colombier startgif(Biobuf *fd, Image *image, int loopcount)
777dd7cddfSDavid du Colombier {
787dd7cddfSDavid du Colombier return startgif0(fd, image->chan, image->r, image->depth, loopcount);
797dd7cddfSDavid du Colombier }
807dd7cddfSDavid du Colombier
817dd7cddfSDavid du Colombier char*
memstartgif(Biobuf * fd,Memimage * memimage,int loopcount)827dd7cddfSDavid du Colombier memstartgif(Biobuf *fd, Memimage *memimage, int loopcount)
837dd7cddfSDavid du Colombier {
847dd7cddfSDavid du Colombier return startgif0(fd, memimage->chan, memimage->r, memimage->depth, loopcount);
857dd7cddfSDavid du Colombier }
867dd7cddfSDavid du Colombier
877dd7cddfSDavid du Colombier static
887dd7cddfSDavid du Colombier char*
writegif0(Biobuf * fd,Image * image,Memimage * memimage,ulong chan,Rectangle r,char * comment,int dt,int trans)897dd7cddfSDavid du Colombier writegif0(Biobuf *fd, Image *image, Memimage *memimage, ulong chan, Rectangle r, char *comment, int dt, int trans)
907dd7cddfSDavid du Colombier {
917dd7cddfSDavid du Colombier char *err;
927dd7cddfSDavid du Colombier
937dd7cddfSDavid du Colombier switch(chan){
947dd7cddfSDavid du Colombier case GREY1:
957dd7cddfSDavid du Colombier case GREY2:
967dd7cddfSDavid du Colombier case GREY4:
977dd7cddfSDavid du Colombier case CMAP8:
987dd7cddfSDavid du Colombier case GREY8:
997dd7cddfSDavid du Colombier break;
1007dd7cddfSDavid du Colombier default:
1017dd7cddfSDavid du Colombier return "WriteGIF: can't handle channel type";
1027dd7cddfSDavid du Colombier }
1037dd7cddfSDavid du Colombier
1047dd7cddfSDavid du Colombier writecomment(fd, comment);
1057dd7cddfSDavid du Colombier writegraphiccontrol(fd, dt, trans);
1067dd7cddfSDavid du Colombier writedescriptor(fd, r);
1077dd7cddfSDavid du Colombier
1087dd7cddfSDavid du Colombier err = writedata(fd, image, memimage);
1097dd7cddfSDavid du Colombier if(err != nil)
1107dd7cddfSDavid du Colombier return err;
1117dd7cddfSDavid du Colombier
1127dd7cddfSDavid du Colombier return nil;
1137dd7cddfSDavid du Colombier }
1147dd7cddfSDavid du Colombier
1157dd7cddfSDavid du Colombier char*
writegif(Biobuf * fd,Image * image,char * comment,int dt,int trans)1167dd7cddfSDavid du Colombier writegif(Biobuf *fd, Image *image, char *comment, int dt, int trans)
1177dd7cddfSDavid du Colombier {
1187dd7cddfSDavid du Colombier return writegif0(fd, image, nil, image->chan, image->r, comment, dt, trans);
1197dd7cddfSDavid du Colombier }
1207dd7cddfSDavid du Colombier
1217dd7cddfSDavid du Colombier char*
memwritegif(Biobuf * fd,Memimage * memimage,char * comment,int dt,int trans)1227dd7cddfSDavid du Colombier memwritegif(Biobuf *fd, Memimage *memimage, char *comment, int dt, int trans)
1237dd7cddfSDavid du Colombier {
1247dd7cddfSDavid du Colombier return writegif0(fd, nil, memimage, memimage->chan, memimage->r, comment, dt, trans);
1257dd7cddfSDavid du Colombier }
1267dd7cddfSDavid du Colombier
1277dd7cddfSDavid du Colombier /*
1287dd7cddfSDavid du Colombier * Write little-endian 16-bit integer
1297dd7cddfSDavid du Colombier */
1307dd7cddfSDavid du Colombier static
1317dd7cddfSDavid du Colombier void
put2(Biobuf * fd,int i)1327dd7cddfSDavid du Colombier put2(Biobuf *fd, int i)
1337dd7cddfSDavid du Colombier {
1347dd7cddfSDavid du Colombier Bputc(fd, i);
1357dd7cddfSDavid du Colombier Bputc(fd, i>>8);
1367dd7cddfSDavid du Colombier }
1377dd7cddfSDavid du Colombier
1387dd7cddfSDavid du Colombier /*
1397dd7cddfSDavid du Colombier * Get color map for all ldepths, in format suitable for writing out
1407dd7cddfSDavid du Colombier */
1417dd7cddfSDavid du Colombier static
1427dd7cddfSDavid du Colombier void
getcolormap(void)1437dd7cddfSDavid du Colombier getcolormap(void)
1447dd7cddfSDavid du Colombier {
1457dd7cddfSDavid du Colombier int i, col;
1467dd7cddfSDavid du Colombier ulong rgb;
1477dd7cddfSDavid du Colombier uchar *c;
1487dd7cddfSDavid du Colombier
1497dd7cddfSDavid du Colombier if(colormap[0] != nil)
1507dd7cddfSDavid du Colombier return;
1517dd7cddfSDavid du Colombier for(i=0; i<nelem(colormap); i++)
1527dd7cddfSDavid du Colombier colormap[i] = gifmalloc(3* colormapsize[i]);
1537dd7cddfSDavid du Colombier c = colormap[GREYMAP]; /* GREY8 */
1547dd7cddfSDavid du Colombier for(i=0; i<256; i++){
1557dd7cddfSDavid du Colombier c[3*i+0] = i; /* red */
1567dd7cddfSDavid du Colombier c[3*i+1] = i; /* green */
1577dd7cddfSDavid du Colombier c[3*i+2] = i; /* blue */
1587dd7cddfSDavid du Colombier }
1597dd7cddfSDavid du Colombier c = colormap[3]; /* RGBV */
1607dd7cddfSDavid du Colombier for(i=0; i<256; i++){
1617dd7cddfSDavid du Colombier rgb = cmap2rgb(i);
1627dd7cddfSDavid du Colombier c[3*i+0] = (rgb>>16) & 0xFF; /* red */
1637dd7cddfSDavid du Colombier c[3*i+1] = (rgb>> 8) & 0xFF; /* green */
1647dd7cddfSDavid du Colombier c[3*i+2] = (rgb>> 0) & 0xFF; /* blue */
1657dd7cddfSDavid du Colombier }
1667dd7cddfSDavid du Colombier c = colormap[2]; /* GREY4 */
1677dd7cddfSDavid du Colombier for(i=0; i<16; i++){
1687dd7cddfSDavid du Colombier col = (i<<4)|i;
1697dd7cddfSDavid du Colombier rgb = cmap2rgb(col);
1707dd7cddfSDavid du Colombier c[3*i+0] = (rgb>>16) & 0xFF; /* red */
1717dd7cddfSDavid du Colombier c[3*i+1] = (rgb>> 8) & 0xFF; /* green */
1727dd7cddfSDavid du Colombier c[3*i+2] = (rgb>> 0) & 0xFF; /* blue */
1737dd7cddfSDavid du Colombier }
1747dd7cddfSDavid du Colombier c = colormap[1]; /* GREY2 */
1757dd7cddfSDavid du Colombier for(i=0; i<4; i++){
1767dd7cddfSDavid du Colombier col = (i<<6)|(i<<4)|(i<<2)|i;
1777dd7cddfSDavid du Colombier rgb = cmap2rgb(col);
1787dd7cddfSDavid du Colombier c[3*i+0] = (rgb>>16) & 0xFF; /* red */
1797dd7cddfSDavid du Colombier c[3*i+1] = (rgb>> 8) & 0xFF; /* green */
1807dd7cddfSDavid du Colombier c[3*i+2] = (rgb>> 0) & 0xFF; /* blue */
1817dd7cddfSDavid du Colombier }
1827dd7cddfSDavid du Colombier c = colormap[0]; /* GREY1 */
1837dd7cddfSDavid du Colombier for(i=0; i<2; i++){
1847dd7cddfSDavid du Colombier if(i == 0)
1857dd7cddfSDavid du Colombier col = 0;
1867dd7cddfSDavid du Colombier else
1877dd7cddfSDavid du Colombier col = 0xFF;
1887dd7cddfSDavid du Colombier rgb = cmap2rgb(col);
1897dd7cddfSDavid du Colombier c[3*i+0] = (rgb>>16) & 0xFF; /* red */
1907dd7cddfSDavid du Colombier c[3*i+1] = (rgb>> 8) & 0xFF; /* green */
1917dd7cddfSDavid du Colombier c[3*i+2] = (rgb>> 0) & 0xFF; /* blue */
1927dd7cddfSDavid du Colombier }
1937dd7cddfSDavid du Colombier }
1947dd7cddfSDavid du Colombier
195*bacfa46cSDavid du Colombier /* imported from libdraw/arith.c to permit an extern log2 function */
196*bacfa46cSDavid du Colombier static int log2[] = {
197*bacfa46cSDavid du Colombier -1, 0, 1, -1, 2, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1, 4,
198*bacfa46cSDavid du Colombier -1, -1, -1, -1, -1, -1, -1, 4 /* BUG */, -1, -1, -1, -1, -1, -1, -1, 5
199*bacfa46cSDavid du Colombier };
200*bacfa46cSDavid du Colombier
2017dd7cddfSDavid du Colombier /*
2027dd7cddfSDavid du Colombier * Write header, logical screen descriptor, and color map
2037dd7cddfSDavid du Colombier */
2047dd7cddfSDavid du Colombier static
2057dd7cddfSDavid du Colombier void
writeheader(Biobuf * fd,Rectangle r,int depth,ulong chan,int loopcount)2067dd7cddfSDavid du Colombier writeheader(Biobuf *fd, Rectangle r, int depth, ulong chan, int loopcount)
2077dd7cddfSDavid du Colombier {
2087dd7cddfSDavid du Colombier /* Header */
2097dd7cddfSDavid du Colombier Bprint(fd, "%s", "GIF89a");
2107dd7cddfSDavid du Colombier
2117dd7cddfSDavid du Colombier /* Logical Screen Descriptor */
2127dd7cddfSDavid du Colombier put2(fd, Dx(r));
2137dd7cddfSDavid du Colombier put2(fd, Dy(r));
2147dd7cddfSDavid du Colombier
2157dd7cddfSDavid du Colombier /* Color table present, 4 bits per color (for RGBV best case), size of color map */
2167dd7cddfSDavid du Colombier Bputc(fd, (1<<7)|(3<<4)|(depth-1)); /* not right for GREY8, but GIF doesn't let us specify enough bits */
2177dd7cddfSDavid du Colombier Bputc(fd, 0xFF); /* white background (doesn't matter anyway) */
2187dd7cddfSDavid du Colombier Bputc(fd, 0); /* pixel aspect ratio - unused */
2197dd7cddfSDavid du Colombier
2207dd7cddfSDavid du Colombier /* Global Color Table */
2217dd7cddfSDavid du Colombier getcolormap();
2227dd7cddfSDavid du Colombier if(chan == GREY8)
2237dd7cddfSDavid du Colombier depth = GREYMAP;
2247dd7cddfSDavid du Colombier else
2257dd7cddfSDavid du Colombier depth = log2[depth];
2267dd7cddfSDavid du Colombier Bwrite(fd, colormap[depth], 3*colormapsize[depth]);
2277dd7cddfSDavid du Colombier
2287dd7cddfSDavid du Colombier if(loopcount >= 0){ /* hard-to-discover way to force cycled animation */
2297dd7cddfSDavid du Colombier /* Application Extension with (1 loopcountlo loopcounthi) as data */
2307dd7cddfSDavid du Colombier Bputc(fd, 0x21);
2317dd7cddfSDavid du Colombier Bputc(fd, 0xFF);
2327dd7cddfSDavid du Colombier Bputc(fd, 11);
2337dd7cddfSDavid du Colombier Bwrite(fd, "NETSCAPE2.0", 11);
2347dd7cddfSDavid du Colombier Bputc(fd, 3);
2357dd7cddfSDavid du Colombier Bputc(fd, 1);
2367dd7cddfSDavid du Colombier put2(fd, loopcount);
2377dd7cddfSDavid du Colombier Bputc(fd, 0);
2387dd7cddfSDavid du Colombier }
2397dd7cddfSDavid du Colombier }
2407dd7cddfSDavid du Colombier
2417dd7cddfSDavid du Colombier /*
2427dd7cddfSDavid du Colombier * Write optional comment block
2437dd7cddfSDavid du Colombier */
2447dd7cddfSDavid du Colombier static
2457dd7cddfSDavid du Colombier void
writecomment(Biobuf * fd,char * comment)2467dd7cddfSDavid du Colombier writecomment(Biobuf *fd, char *comment)
2477dd7cddfSDavid du Colombier {
2487dd7cddfSDavid du Colombier int n;
2497dd7cddfSDavid du Colombier
2507dd7cddfSDavid du Colombier if(comment==nil || comment[0]=='\0')
2517dd7cddfSDavid du Colombier return;
2527dd7cddfSDavid du Colombier
2537dd7cddfSDavid du Colombier /* Comment extension and label */
2547dd7cddfSDavid du Colombier Bputc(fd, 0x21);
2557dd7cddfSDavid du Colombier Bputc(fd, 0xFE);
2567dd7cddfSDavid du Colombier
2577dd7cddfSDavid du Colombier /* Comment data */
2587dd7cddfSDavid du Colombier n = strlen(comment);
2597dd7cddfSDavid du Colombier if(n > 255)
2607dd7cddfSDavid du Colombier n = 255;
2617dd7cddfSDavid du Colombier Bputc(fd, n);
2627dd7cddfSDavid du Colombier Bwrite(fd, comment, n);
2637dd7cddfSDavid du Colombier
2647dd7cddfSDavid du Colombier /* Block terminator */
2657dd7cddfSDavid du Colombier Bputc(fd, 0x00);
2667dd7cddfSDavid du Colombier }
2677dd7cddfSDavid du Colombier
2687dd7cddfSDavid du Colombier /*
2697dd7cddfSDavid du Colombier * Write optional control block (sets Delay Time)
2707dd7cddfSDavid du Colombier */
2717dd7cddfSDavid du Colombier static
2727dd7cddfSDavid du Colombier void
writegraphiccontrol(Biobuf * fd,int dt,int trans)2737dd7cddfSDavid du Colombier writegraphiccontrol(Biobuf *fd, int dt, int trans)
2747dd7cddfSDavid du Colombier {
2757dd7cddfSDavid du Colombier if(dt < 0 && trans < 0)
2767dd7cddfSDavid du Colombier return;
2777dd7cddfSDavid du Colombier
2787dd7cddfSDavid du Colombier /* Comment extension and label and block size*/
2797dd7cddfSDavid du Colombier Bputc(fd, 0x21);
2807dd7cddfSDavid du Colombier Bputc(fd, 0xF9);
2817dd7cddfSDavid du Colombier Bputc(fd, 0x04);
2827dd7cddfSDavid du Colombier
2837dd7cddfSDavid du Colombier /* Disposal method and other flags (none) */
2847dd7cddfSDavid du Colombier if(trans >= 0)
2857dd7cddfSDavid du Colombier Bputc(fd, 0x01);
2867dd7cddfSDavid du Colombier else
2877dd7cddfSDavid du Colombier Bputc(fd, 0x00);
2887dd7cddfSDavid du Colombier
2897dd7cddfSDavid du Colombier /* Delay time, in centisec (argument is millisec for sanity) */
2907dd7cddfSDavid du Colombier if(dt < 0)
2917dd7cddfSDavid du Colombier dt = 0;
2927dd7cddfSDavid du Colombier else if(dt < 10)
2937dd7cddfSDavid du Colombier dt = 1;
2947dd7cddfSDavid du Colombier else
2957dd7cddfSDavid du Colombier dt = (dt+5)/10;
2967dd7cddfSDavid du Colombier put2(fd, dt);
2977dd7cddfSDavid du Colombier
2987dd7cddfSDavid du Colombier /* Transparency index */
2997dd7cddfSDavid du Colombier if(trans < 0)
3007dd7cddfSDavid du Colombier trans = 0;
3017dd7cddfSDavid du Colombier Bputc(fd, trans);
3027dd7cddfSDavid du Colombier
3037dd7cddfSDavid du Colombier /* Block terminator */
3047dd7cddfSDavid du Colombier Bputc(fd, 0x00);
3057dd7cddfSDavid du Colombier }
3067dd7cddfSDavid du Colombier
3077dd7cddfSDavid du Colombier /*
3087dd7cddfSDavid du Colombier * Write image descriptor
3097dd7cddfSDavid du Colombier */
3107dd7cddfSDavid du Colombier static
3117dd7cddfSDavid du Colombier void
writedescriptor(Biobuf * fd,Rectangle r)3127dd7cddfSDavid du Colombier writedescriptor(Biobuf *fd, Rectangle r)
3137dd7cddfSDavid du Colombier {
3147dd7cddfSDavid du Colombier /* Image Separator */
3157dd7cddfSDavid du Colombier Bputc(fd, 0x2C);
3167dd7cddfSDavid du Colombier
3177dd7cddfSDavid du Colombier /* Left, top, width, height */
31859cc4ca5SDavid du Colombier put2(fd, r.min.x-mainrect.min.x);
31959cc4ca5SDavid du Colombier put2(fd, r.min.y-mainrect.min.y);
3207dd7cddfSDavid du Colombier put2(fd, Dx(r));
3217dd7cddfSDavid du Colombier put2(fd, Dy(r));
3227dd7cddfSDavid du Colombier /* no special processing */
3237dd7cddfSDavid du Colombier Bputc(fd, 0);
3247dd7cddfSDavid du Colombier }
3257dd7cddfSDavid du Colombier
3267dd7cddfSDavid du Colombier /*
3277dd7cddfSDavid du Colombier * Write data
3287dd7cddfSDavid du Colombier */
3297dd7cddfSDavid du Colombier static
3307dd7cddfSDavid du Colombier char*
writedata(Biobuf * fd,Image * image,Memimage * memimage)3317dd7cddfSDavid du Colombier writedata(Biobuf *fd, Image *image, Memimage *memimage)
3327dd7cddfSDavid du Colombier {
3337dd7cddfSDavid du Colombier char *err;
3347dd7cddfSDavid du Colombier uchar *data;
3357dd7cddfSDavid du Colombier int ndata, depth;
3367dd7cddfSDavid du Colombier Rectangle r;
3377dd7cddfSDavid du Colombier
3387dd7cddfSDavid du Colombier if(memimage != nil){
3397dd7cddfSDavid du Colombier r = memimage->r;
3407dd7cddfSDavid du Colombier depth = memimage->depth;
3417dd7cddfSDavid du Colombier }else{
3427dd7cddfSDavid du Colombier r = image->r;
3437dd7cddfSDavid du Colombier depth = image->depth;
3447dd7cddfSDavid du Colombier }
3457dd7cddfSDavid du Colombier
3467dd7cddfSDavid du Colombier /* LZW Minimum code size */
3477dd7cddfSDavid du Colombier if(depth == 1)
3487dd7cddfSDavid du Colombier Bputc(fd, 2);
3497dd7cddfSDavid du Colombier else
3507dd7cddfSDavid du Colombier Bputc(fd, depth);
3517dd7cddfSDavid du Colombier
3527dd7cddfSDavid du Colombier /*
3537dd7cddfSDavid du Colombier * Read image data into memory
3547dd7cddfSDavid du Colombier * potentially one extra byte on each end of each scan line
3557dd7cddfSDavid du Colombier */
3567dd7cddfSDavid du Colombier ndata = Dy(r)*(2+(Dx(r)>>(3-log2[depth])));
3577dd7cddfSDavid du Colombier data = gifmalloc(ndata);
3587dd7cddfSDavid du Colombier if(memimage != nil)
3597dd7cddfSDavid du Colombier ndata = unloadmemimage(memimage, r, data, ndata);
3607dd7cddfSDavid du Colombier else
3617dd7cddfSDavid du Colombier ndata = unloadimage(image, r, data, ndata);
3627dd7cddfSDavid du Colombier if(ndata < 0){
3639a747e4fSDavid du Colombier err = gifmalloc(ERRMAX);
3649a747e4fSDavid du Colombier snprint(err, ERRMAX, "WriteGIF: %r");
3657dd7cddfSDavid du Colombier free(data);
3667dd7cddfSDavid du Colombier return err;
3677dd7cddfSDavid du Colombier }
3687dd7cddfSDavid du Colombier
3697dd7cddfSDavid du Colombier /* Encode and emit the data */
3707dd7cddfSDavid du Colombier encode(fd, r, depth, data, ndata);
3717dd7cddfSDavid du Colombier free(data);
3727dd7cddfSDavid du Colombier
3737dd7cddfSDavid du Colombier /* Block Terminator */
3747dd7cddfSDavid du Colombier Bputc(fd, 0);
3757dd7cddfSDavid du Colombier return nil;
3767dd7cddfSDavid du Colombier }
3777dd7cddfSDavid du Colombier
3787dd7cddfSDavid du Colombier /*
3797dd7cddfSDavid du Colombier * Write trailer
3807dd7cddfSDavid du Colombier */
3817dd7cddfSDavid du Colombier void
endgif(Biobuf * fd)3827dd7cddfSDavid du Colombier endgif(Biobuf *fd)
3837dd7cddfSDavid du Colombier {
3847dd7cddfSDavid du Colombier Bputc(fd, 0x3B);
3857dd7cddfSDavid du Colombier Bflush(fd);
3867dd7cddfSDavid du Colombier }
3877dd7cddfSDavid du Colombier
3887dd7cddfSDavid du Colombier void
memendgif(Biobuf * fd)3897dd7cddfSDavid du Colombier memendgif(Biobuf *fd)
3907dd7cddfSDavid du Colombier {
3917dd7cddfSDavid du Colombier endgif(fd);
3927dd7cddfSDavid du Colombier }
3937dd7cddfSDavid du Colombier
3947dd7cddfSDavid du Colombier /*
3957dd7cddfSDavid du Colombier * Put n bits of c into output at io.buf[i];
3967dd7cddfSDavid du Colombier */
3977dd7cddfSDavid du Colombier static
3987dd7cddfSDavid du Colombier void
output(IO * io,int c,int n)3997dd7cddfSDavid du Colombier output(IO *io, int c, int n)
4007dd7cddfSDavid du Colombier {
4017dd7cddfSDavid du Colombier if(c < 0){
4027dd7cddfSDavid du Colombier if(io->nbits != 0)
4037dd7cddfSDavid du Colombier io->buf[io->i++] = io->sreg;
4047dd7cddfSDavid du Colombier Bputc(io->fd, io->i);
4057dd7cddfSDavid du Colombier Bwrite(io->fd, io->buf, io->i);
4067dd7cddfSDavid du Colombier io->nbits = 0;
4077dd7cddfSDavid du Colombier return;
4087dd7cddfSDavid du Colombier }
4097dd7cddfSDavid du Colombier
4107dd7cddfSDavid du Colombier if(io->nbits+n >= 31){
4117dd7cddfSDavid du Colombier fprint(2, "panic: WriteGIF sr overflow\n");
4127dd7cddfSDavid du Colombier exits("WriteGIF panic");
4137dd7cddfSDavid du Colombier }
4147dd7cddfSDavid du Colombier io->sreg |= c<<io->nbits;
4157dd7cddfSDavid du Colombier io->nbits += n;
4167dd7cddfSDavid du Colombier
4177dd7cddfSDavid du Colombier while(io->nbits >= 8){
4187dd7cddfSDavid du Colombier io->buf[io->i++] = io->sreg;
4197dd7cddfSDavid du Colombier io->sreg >>= 8;
4207dd7cddfSDavid du Colombier io->nbits -= 8;
4217dd7cddfSDavid du Colombier }
4227dd7cddfSDavid du Colombier
4237dd7cddfSDavid du Colombier if(io->i >= 255){
4247dd7cddfSDavid du Colombier Bputc(io->fd, 255);
4257dd7cddfSDavid du Colombier Bwrite(io->fd, io->buf, 255);
4267dd7cddfSDavid du Colombier memmove(io->buf, io->buf+255, io->i-255);
4277dd7cddfSDavid du Colombier io->i -= 255;
4287dd7cddfSDavid du Colombier }
4297dd7cddfSDavid du Colombier }
4307dd7cddfSDavid du Colombier
4317dd7cddfSDavid du Colombier /*
4327dd7cddfSDavid du Colombier * LZW encoder
4337dd7cddfSDavid du Colombier */
4347dd7cddfSDavid du Colombier static
4357dd7cddfSDavid du Colombier void
encode(Biobuf * fd,Rectangle r,int depth,uchar * data,uint ndata)4367dd7cddfSDavid du Colombier encode(Biobuf *fd, Rectangle r, int depth, uchar *data, uint ndata)
4377dd7cddfSDavid du Colombier {
4387dd7cddfSDavid du Colombier int i, c, h, csize, prefix, first, sreg, nbits, bitsperpixel;
4397dd7cddfSDavid du Colombier int CTM, EOD, codesize, ld0, datai, x, ld, pm;
4407dd7cddfSDavid du Colombier int nentry, maxentry, early;
4417dd7cddfSDavid du Colombier Entry *e, *oe;
4427dd7cddfSDavid du Colombier IO *io;
4437dd7cddfSDavid du Colombier Entry **hash;
4447dd7cddfSDavid du Colombier
4457dd7cddfSDavid du Colombier first = 1;
4467dd7cddfSDavid du Colombier ld = log2[depth];
4477dd7cddfSDavid du Colombier /* ldepth 0 must generate codesize 2 with values 0 and 1 (see the spec.) */
4487dd7cddfSDavid du Colombier ld0 = ld;
4497dd7cddfSDavid du Colombier if(ld0 == 0)
4507dd7cddfSDavid du Colombier ld0 = 1;
4517dd7cddfSDavid du Colombier codesize = (1<<ld0);
4527dd7cddfSDavid du Colombier CTM = 1<<codesize;
4537dd7cddfSDavid du Colombier EOD = CTM+1;
4547dd7cddfSDavid du Colombier
4557dd7cddfSDavid du Colombier io = gifmalloc(sizeof(IO));
4567dd7cddfSDavid du Colombier io->fd = fd;
4577dd7cddfSDavid du Colombier sreg = 0;
4587dd7cddfSDavid du Colombier nbits = 0;
4597dd7cddfSDavid du Colombier bitsperpixel = 1<<ld;
4607dd7cddfSDavid du Colombier pm = (1<<bitsperpixel)-1;
4617dd7cddfSDavid du Colombier
4627dd7cddfSDavid du Colombier datai = 0;
4637dd7cddfSDavid du Colombier x = r.min.x;
4647dd7cddfSDavid du Colombier hash = gifmalloc(Nhash*sizeof(Entry*));
4657dd7cddfSDavid du Colombier
4667dd7cddfSDavid du Colombier Init:
4677dd7cddfSDavid du Colombier memset(hash, 0, Nhash*sizeof(Entry*));
4687dd7cddfSDavid du Colombier csize = codesize+1;
4697dd7cddfSDavid du Colombier nentry = EOD+1;
4707dd7cddfSDavid du Colombier maxentry = (1<<csize);
4717dd7cddfSDavid du Colombier for(i = 0; i<nentry; i++){
4727dd7cddfSDavid du Colombier e = &tbl[i];
4737dd7cddfSDavid du Colombier h = (e->prefix<<24) | (e->exten<<8);
4747dd7cddfSDavid du Colombier h %= Nhash;
4757dd7cddfSDavid du Colombier if(h < 0)
4767dd7cddfSDavid du Colombier h += Nhash;
4777dd7cddfSDavid du Colombier e->next = hash[h];
4787dd7cddfSDavid du Colombier hash[h] = e;
4797dd7cddfSDavid du Colombier }
4807dd7cddfSDavid du Colombier prefix = -1;
4817dd7cddfSDavid du Colombier if(first)
4827dd7cddfSDavid du Colombier output(io, CTM, csize);
4837dd7cddfSDavid du Colombier first = 0;
4847dd7cddfSDavid du Colombier
4857dd7cddfSDavid du Colombier /*
4867dd7cddfSDavid du Colombier * Scan over pixels. Because of partially filled bytes on ends of scan lines,
4877dd7cddfSDavid du Colombier * which must be ignored in the data stream passed to GIF, this is more
4887dd7cddfSDavid du Colombier * complex than we'd like.
4897dd7cddfSDavid du Colombier */
4907dd7cddfSDavid du Colombier Next:
4917dd7cddfSDavid du Colombier for(;;){
4927dd7cddfSDavid du Colombier if(ld != 3){
4937dd7cddfSDavid du Colombier /* beginning of scan line is difficult; prime the shift register */
4947dd7cddfSDavid du Colombier if(x == r.min.x){
4957dd7cddfSDavid du Colombier if(datai == ndata)
4967dd7cddfSDavid du Colombier break;
4977dd7cddfSDavid du Colombier sreg = data[datai++];
4987dd7cddfSDavid du Colombier nbits = 8-((x&(7>>ld))<<ld);
4997dd7cddfSDavid du Colombier }
5007dd7cddfSDavid du Colombier x++;
5017dd7cddfSDavid du Colombier if(x == r.max.x)
5027dd7cddfSDavid du Colombier x = r.min.x;
5037dd7cddfSDavid du Colombier }
5047dd7cddfSDavid du Colombier if(nbits == 0){
5057dd7cddfSDavid du Colombier if(datai == ndata)
5067dd7cddfSDavid du Colombier break;
5077dd7cddfSDavid du Colombier sreg = data[datai++];
5087dd7cddfSDavid du Colombier nbits = 8;
5097dd7cddfSDavid du Colombier }
5107dd7cddfSDavid du Colombier nbits -= bitsperpixel;
5117dd7cddfSDavid du Colombier c = sreg>>nbits & pm;
5127dd7cddfSDavid du Colombier h = prefix<<24 | c<<8;
5137dd7cddfSDavid du Colombier h %= Nhash;
5147dd7cddfSDavid du Colombier if(h < 0)
5157dd7cddfSDavid du Colombier h += Nhash;
5167dd7cddfSDavid du Colombier oe = nil;
5177dd7cddfSDavid du Colombier for(e = hash[h]; e!=nil; e=e->next){
5187dd7cddfSDavid du Colombier if(e->prefix == prefix && e->exten == c){
5197dd7cddfSDavid du Colombier if(oe != nil){
5207dd7cddfSDavid du Colombier oe->next = e->next;
5217dd7cddfSDavid du Colombier e->next = hash[h];
5227dd7cddfSDavid du Colombier hash[h] = e;
5237dd7cddfSDavid du Colombier }
5247dd7cddfSDavid du Colombier prefix = e->index;
5257dd7cddfSDavid du Colombier goto Next;
5267dd7cddfSDavid du Colombier }
5277dd7cddfSDavid du Colombier oe = e;
5287dd7cddfSDavid du Colombier }
5297dd7cddfSDavid du Colombier
5307dd7cddfSDavid du Colombier output(io, prefix, csize);
5317dd7cddfSDavid du Colombier early = 0; /* peculiar tiff feature here for reference */
5327dd7cddfSDavid du Colombier if(nentry == maxentry-early){
5337dd7cddfSDavid du Colombier if(csize == 12){
53459cc4ca5SDavid du Colombier nbits += bitsperpixel; /* unget pixel */
5357dd7cddfSDavid du Colombier x--;
53659cc4ca5SDavid du Colombier if(ld != 3 && x == r.min.x)
53759cc4ca5SDavid du Colombier datai--;
5387dd7cddfSDavid du Colombier output(io, CTM, csize);
5397dd7cddfSDavid du Colombier goto Init;
5407dd7cddfSDavid du Colombier }
5417dd7cddfSDavid du Colombier csize++;
5427dd7cddfSDavid du Colombier maxentry = (1<<csize);
5437dd7cddfSDavid du Colombier }
5447dd7cddfSDavid du Colombier
5457dd7cddfSDavid du Colombier e = &tbl[nentry];
5467dd7cddfSDavid du Colombier e->prefix = prefix;
5477dd7cddfSDavid du Colombier e->exten = c;
5487dd7cddfSDavid du Colombier e->next = hash[h];
5497dd7cddfSDavid du Colombier hash[h] = e;
5507dd7cddfSDavid du Colombier
5517dd7cddfSDavid du Colombier prefix = c;
5527dd7cddfSDavid du Colombier nentry++;
5537dd7cddfSDavid du Colombier }
5547dd7cddfSDavid du Colombier
5557dd7cddfSDavid du Colombier output(io, prefix, csize);
5567dd7cddfSDavid du Colombier output(io, EOD, csize);
5577dd7cddfSDavid du Colombier output(io, -1, csize);
5587dd7cddfSDavid du Colombier free(io);
5597dd7cddfSDavid du Colombier free(hash);
5607dd7cddfSDavid du Colombier }
5617dd7cddfSDavid du Colombier
5627dd7cddfSDavid du Colombier static
5637dd7cddfSDavid du Colombier void*
gifmalloc(ulong sz)5647dd7cddfSDavid du Colombier gifmalloc(ulong sz)
5657dd7cddfSDavid du Colombier {
5667dd7cddfSDavid du Colombier void *v;
5677dd7cddfSDavid du Colombier v = malloc(sz);
5687dd7cddfSDavid du Colombier if(v == nil) {
5697dd7cddfSDavid du Colombier fprint(2, "WriteGIF: out of memory allocating %ld\n", sz);
5707dd7cddfSDavid du Colombier abort();
5717dd7cddfSDavid du Colombier exits("mem");
5727dd7cddfSDavid du Colombier }
5737dd7cddfSDavid du Colombier memset(v, 0, sz);
5747dd7cddfSDavid du Colombier return v;
5757dd7cddfSDavid du Colombier }
576