xref: /plan9/sys/src/cmd/jpg/writegif.c (revision bacfa46c74e1c310aff15aef9cb6bc4e6302513a)
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