xref: /plan9/sys/src/cmd/jpg/readgif.c (revision 9f2726c34299ea5a81cda1b22133dd5a4b421e04)
17dd7cddfSDavid du Colombier #include <u.h>
27dd7cddfSDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <bio.h>
47dd7cddfSDavid du Colombier #include <draw.h>
57dd7cddfSDavid du Colombier #include "imagefile.h"
67dd7cddfSDavid du Colombier 
77dd7cddfSDavid du Colombier typedef struct Entry Entry;
87dd7cddfSDavid du Colombier typedef struct Header Header;
97dd7cddfSDavid du Colombier 
107dd7cddfSDavid du Colombier struct Entry{
117dd7cddfSDavid du Colombier 	int		prefix;
127dd7cddfSDavid du Colombier 	int		exten;
137dd7cddfSDavid du Colombier };
147dd7cddfSDavid du Colombier 
157dd7cddfSDavid du Colombier 
167dd7cddfSDavid du Colombier struct Header{
177dd7cddfSDavid du Colombier 	Biobuf	*fd;
187dd7cddfSDavid du Colombier 	char		err[256];
197dd7cddfSDavid du Colombier 	jmp_buf	errlab;
207dd7cddfSDavid du Colombier 	uchar 	buf[3*256];
217dd7cddfSDavid du Colombier 	char 		vers[8];
227dd7cddfSDavid du Colombier 	uchar 	*globalcmap;
237dd7cddfSDavid du Colombier 	int		screenw;
247dd7cddfSDavid du Colombier 	int		screenh;
257dd7cddfSDavid du Colombier 	int		fields;
267dd7cddfSDavid du Colombier 	int		bgrnd;
277dd7cddfSDavid du Colombier 	int		aspect;
287dd7cddfSDavid du Colombier 	int		flags;
297dd7cddfSDavid du Colombier 	int		delay;
307dd7cddfSDavid du Colombier 	int		trindex;
317dd7cddfSDavid du Colombier 	int		loopcount;
327dd7cddfSDavid du Colombier 	Entry	tbl[4096];
337dd7cddfSDavid du Colombier 	Rawimage	**array;
347dd7cddfSDavid du Colombier 	Rawimage	*new;
357dd7cddfSDavid du Colombier 
367dd7cddfSDavid du Colombier 	uchar	*pic;
377dd7cddfSDavid du Colombier };
387dd7cddfSDavid du Colombier 
397dd7cddfSDavid du Colombier static char		readerr[] = "ReadGIF: read error: %r";
407dd7cddfSDavid du Colombier static char		extreaderr[] = "ReadGIF: can't read extension: %r";
417dd7cddfSDavid du Colombier static char		memerr[] = "ReadGIF: malloc failed: %r";
427dd7cddfSDavid du Colombier 
437dd7cddfSDavid du Colombier static Rawimage**	readarray(Header*);
447dd7cddfSDavid du Colombier static Rawimage*	readone(Header*);
457dd7cddfSDavid du Colombier static void			readheader(Header*);
467dd7cddfSDavid du Colombier static void			skipextension(Header*);
477dd7cddfSDavid du Colombier static uchar*		readcmap(Header*, int);
487dd7cddfSDavid du Colombier static uchar*		decode(Header*, Rawimage*, Entry*);
497dd7cddfSDavid du Colombier static void			interlace(Header*, Rawimage*);
507dd7cddfSDavid du Colombier 
517dd7cddfSDavid du Colombier static
527dd7cddfSDavid du Colombier void
clear(void ** p)537dd7cddfSDavid du Colombier clear(void **p)
547dd7cddfSDavid du Colombier {
557dd7cddfSDavid du Colombier 	if(*p){
567dd7cddfSDavid du Colombier 		free(*p);
577dd7cddfSDavid du Colombier 		*p = nil;
587dd7cddfSDavid du Colombier 	}
597dd7cddfSDavid du Colombier }
607dd7cddfSDavid du Colombier 
617dd7cddfSDavid du Colombier static
627dd7cddfSDavid du Colombier void
giffreeall(Header * h,int freeimage)637dd7cddfSDavid du Colombier giffreeall(Header *h, int freeimage)
647dd7cddfSDavid du Colombier {
657dd7cddfSDavid du Colombier 	int i;
667dd7cddfSDavid du Colombier 
677dd7cddfSDavid du Colombier 	if(h->fd){
687dd7cddfSDavid du Colombier 		Bterm(h->fd);
697dd7cddfSDavid du Colombier 		h->fd = nil;
707dd7cddfSDavid du Colombier 	}
717dd7cddfSDavid du Colombier 	clear(&h->pic);
727dd7cddfSDavid du Colombier 	if(h->new){
737dd7cddfSDavid du Colombier 		clear(&h->new->cmap);
747dd7cddfSDavid du Colombier 		clear(&h->new->chans[0]);
757dd7cddfSDavid du Colombier 		clear(&h->new);
767dd7cddfSDavid du Colombier 	}
777dd7cddfSDavid du Colombier 	clear(&h->globalcmap);
787dd7cddfSDavid du Colombier 	if(freeimage && h->array!=nil){
797dd7cddfSDavid du Colombier 		for(i=0; h->array[i]; i++){
807dd7cddfSDavid du Colombier 			clear(&h->array[i]->cmap);
817dd7cddfSDavid du Colombier 			clear(&h->array[i]->chans[0]);
827dd7cddfSDavid du Colombier 		}
837dd7cddfSDavid du Colombier 		clear(&h->array);
847dd7cddfSDavid du Colombier 	}
857dd7cddfSDavid du Colombier }
867dd7cddfSDavid du Colombier 
877dd7cddfSDavid du Colombier static
887dd7cddfSDavid du Colombier void
giferror(Header * h,char * fmt,...)897dd7cddfSDavid du Colombier giferror(Header *h, char *fmt, ...)
907dd7cddfSDavid du Colombier {
917dd7cddfSDavid du Colombier 	va_list arg;
927dd7cddfSDavid du Colombier 
937dd7cddfSDavid du Colombier 	va_start(arg, fmt);
949a747e4fSDavid du Colombier 	vseprint(h->err, h->err+sizeof h->err, fmt, arg);
957dd7cddfSDavid du Colombier 	va_end(arg);
967dd7cddfSDavid du Colombier 
977dd7cddfSDavid du Colombier 	werrstr(h->err);
987dd7cddfSDavid du Colombier 	giffreeall(h, 1);
997dd7cddfSDavid du Colombier 	longjmp(h->errlab, 1);
1007dd7cddfSDavid du Colombier }
1017dd7cddfSDavid du Colombier 
1027dd7cddfSDavid du Colombier 
1037dd7cddfSDavid du Colombier Rawimage**
readgif(int fd,int colorspace)1047dd7cddfSDavid du Colombier readgif(int fd, int colorspace)
1057dd7cddfSDavid du Colombier {
1067dd7cddfSDavid du Colombier 	Rawimage **a;
1077dd7cddfSDavid du Colombier 	Biobuf b;
1087dd7cddfSDavid du Colombier 	Header *h;
1099a747e4fSDavid du Colombier 	char buf[ERRMAX];
1107dd7cddfSDavid du Colombier 
1117dd7cddfSDavid du Colombier 	buf[0] = '\0';
1127dd7cddfSDavid du Colombier 	USED(colorspace);
1137dd7cddfSDavid du Colombier 	if(Binit(&b, fd, OREAD) < 0)
1147dd7cddfSDavid du Colombier 		return nil;
1157dd7cddfSDavid du Colombier 	h = malloc(sizeof(Header));
1167dd7cddfSDavid du Colombier 	if(h == nil){
1177dd7cddfSDavid du Colombier 		Bterm(&b);
1187dd7cddfSDavid du Colombier 		return nil;
1197dd7cddfSDavid du Colombier 	}
1207dd7cddfSDavid du Colombier 	memset(h, 0, sizeof(Header));
1217dd7cddfSDavid du Colombier 	h->fd = &b;
1229a747e4fSDavid du Colombier 	errstr(buf, sizeof buf);	/* throw it away */
1237dd7cddfSDavid du Colombier 	if(setjmp(h->errlab))
1247dd7cddfSDavid du Colombier 		a = nil;
1257dd7cddfSDavid du Colombier 	else
1267dd7cddfSDavid du Colombier 		a = readarray(h);
1277dd7cddfSDavid du Colombier 	giffreeall(h, 0);
1287dd7cddfSDavid du Colombier 	free(h);
1297dd7cddfSDavid du Colombier 	return a;
1307dd7cddfSDavid du Colombier }
1317dd7cddfSDavid du Colombier 
1327dd7cddfSDavid du Colombier static
1337dd7cddfSDavid du Colombier void
inittbl(Header * h)1347dd7cddfSDavid du Colombier inittbl(Header *h)
1357dd7cddfSDavid du Colombier {
1367dd7cddfSDavid du Colombier 	int i;
1377dd7cddfSDavid du Colombier 	Entry *tbl;
1387dd7cddfSDavid du Colombier 
1397dd7cddfSDavid du Colombier 	tbl = h->tbl;
1407dd7cddfSDavid du Colombier 	for(i=0; i<258; i++) {
1417dd7cddfSDavid du Colombier 		tbl[i].prefix = -1;
1427dd7cddfSDavid du Colombier 		tbl[i].exten = i;
1437dd7cddfSDavid du Colombier 	}
1447dd7cddfSDavid du Colombier }
1457dd7cddfSDavid du Colombier 
1467dd7cddfSDavid du Colombier static
1477dd7cddfSDavid du Colombier Rawimage**
readarray(Header * h)1487dd7cddfSDavid du Colombier readarray(Header *h)
1497dd7cddfSDavid du Colombier {
1507dd7cddfSDavid du Colombier 	Entry *tbl;
1517dd7cddfSDavid du Colombier 	Rawimage *new, **array;
1527dd7cddfSDavid du Colombier 	int c, nimages;
1537dd7cddfSDavid du Colombier 
1547dd7cddfSDavid du Colombier 	tbl = h->tbl;
1557dd7cddfSDavid du Colombier 
1567dd7cddfSDavid du Colombier 	readheader(h);
1577dd7cddfSDavid du Colombier 
1587dd7cddfSDavid du Colombier 	if(h->fields & 0x80)
1597dd7cddfSDavid du Colombier 		h->globalcmap = readcmap(h, (h->fields&7)+1);
1607dd7cddfSDavid du Colombier 
1617dd7cddfSDavid du Colombier 	array = malloc(sizeof(Rawimage**));
1627dd7cddfSDavid du Colombier 	if(array == nil)
1637dd7cddfSDavid du Colombier 		giferror(h, memerr);
1647dd7cddfSDavid du Colombier 	nimages = 0;
1657dd7cddfSDavid du Colombier 	array[0] = nil;
1667dd7cddfSDavid du Colombier 	h->array = array;
1677dd7cddfSDavid du Colombier 
1687dd7cddfSDavid du Colombier 	for(;;){
1697dd7cddfSDavid du Colombier 		switch(c = Bgetc(h->fd)){
1707dd7cddfSDavid du Colombier 		case Beof:
1717dd7cddfSDavid du Colombier 			goto Return;
1727dd7cddfSDavid du Colombier 
1737dd7cddfSDavid du Colombier 		case 0x21:	/* Extension (ignored) */
1747dd7cddfSDavid du Colombier 			skipextension(h);
1757dd7cddfSDavid du Colombier 			break;
1767dd7cddfSDavid du Colombier 
1777dd7cddfSDavid du Colombier 		case 0x2C:	/* Image Descriptor */
1787dd7cddfSDavid du Colombier 			inittbl(h);
1797dd7cddfSDavid du Colombier 			new = readone(h);
1807dd7cddfSDavid du Colombier 			if(new->fields & 0x80){
1817dd7cddfSDavid du Colombier 				new->cmaplen = 3*(1<<((new->fields&7)+1));
1827dd7cddfSDavid du Colombier 				new->cmap = readcmap(h, (new->fields&7)+1);
1837dd7cddfSDavid du Colombier 			}else{
1847dd7cddfSDavid du Colombier 				new->cmaplen = 3*(1<<((h->fields&7)+1));
1857dd7cddfSDavid du Colombier 				new->cmap = malloc(new->cmaplen);
1867dd7cddfSDavid du Colombier 				memmove(new->cmap, h->globalcmap, new->cmaplen);
1877dd7cddfSDavid du Colombier 			}
1887dd7cddfSDavid du Colombier 			h->new = new;
1897dd7cddfSDavid du Colombier 			new->chans[0] = decode(h, new, tbl);
1907dd7cddfSDavid du Colombier 			if(new->fields & 0x40)
1917dd7cddfSDavid du Colombier 				interlace(h, new);
1927dd7cddfSDavid du Colombier 			new->gifflags = h->flags;
1937dd7cddfSDavid du Colombier 			new->gifdelay = h->delay;
1947dd7cddfSDavid du Colombier 			new->giftrindex = h->trindex;
1957dd7cddfSDavid du Colombier 			new->gifloopcount = h->loopcount;
1967dd7cddfSDavid du Colombier 			array = realloc(h->array, (nimages+2)*sizeof(Rawimage*));
1977dd7cddfSDavid du Colombier 			if(array == nil)
1987dd7cddfSDavid du Colombier 				giferror(h, memerr);
1997dd7cddfSDavid du Colombier 			array[nimages++] = new;
2007dd7cddfSDavid du Colombier 			array[nimages] = nil;
2017dd7cddfSDavid du Colombier 			h->array = array;
2027dd7cddfSDavid du Colombier 			h->new = nil;
2037dd7cddfSDavid du Colombier 			break;
2047dd7cddfSDavid du Colombier 
2057dd7cddfSDavid du Colombier 		case 0x3B:	/* Trailer */
2067dd7cddfSDavid du Colombier 			goto Return;
2077dd7cddfSDavid du Colombier 
2087dd7cddfSDavid du Colombier 		default:
2097dd7cddfSDavid du Colombier 			fprint(2, "ReadGIF: unknown block type: 0x%.2x\n", c);
2107dd7cddfSDavid du Colombier 			goto Return;
2117dd7cddfSDavid du Colombier 		}
2127dd7cddfSDavid du Colombier 	}
2137dd7cddfSDavid du Colombier 
2147dd7cddfSDavid du Colombier    Return:
2157dd7cddfSDavid du Colombier 	if(array[0]==nil || array[0]->chans[0] == nil)
2167dd7cddfSDavid du Colombier 		giferror(h, "ReadGIF: no picture in file");
2177dd7cddfSDavid du Colombier 
2187dd7cddfSDavid du Colombier 	return array;
2197dd7cddfSDavid du Colombier }
2207dd7cddfSDavid du Colombier 
2217dd7cddfSDavid du Colombier static
2227dd7cddfSDavid du Colombier void
readheader(Header * h)2237dd7cddfSDavid du Colombier readheader(Header *h)
2247dd7cddfSDavid du Colombier {
2257dd7cddfSDavid du Colombier 	if(Bread(h->fd, h->buf, 13) != 13)
2267dd7cddfSDavid du Colombier 		giferror(h, "ReadGIF: can't read header: %r");
2277dd7cddfSDavid du Colombier 	memmove(h->vers, h->buf, 6);
2287dd7cddfSDavid du Colombier 	if(strcmp(h->vers, "GIF87a")!=0 &&  strcmp(h->vers, "GIF89a")!=0)
2297dd7cddfSDavid du Colombier 		giferror(h, "ReadGIF: can't recognize format %s", h->vers);
2307dd7cddfSDavid du Colombier 	h->screenw = h->buf[6]+(h->buf[7]<<8);
2317dd7cddfSDavid du Colombier 	h->screenh = h->buf[8]+(h->buf[9]<<8);
2327dd7cddfSDavid du Colombier 	h->fields = h->buf[10];
2337dd7cddfSDavid du Colombier 	h->bgrnd = h->buf[11];
2347dd7cddfSDavid du Colombier 	h->aspect = h->buf[12];
2357dd7cddfSDavid du Colombier 	h->flags = 0;
2367dd7cddfSDavid du Colombier 	h->delay = 0;
2377dd7cddfSDavid du Colombier 	h->trindex = 0;
2387dd7cddfSDavid du Colombier 	h->loopcount = -1;
2397dd7cddfSDavid du Colombier }
2407dd7cddfSDavid du Colombier 
2417dd7cddfSDavid du Colombier static
2427dd7cddfSDavid du Colombier uchar*
readcmap(Header * h,int size)2437dd7cddfSDavid du Colombier readcmap(Header *h, int size)
2447dd7cddfSDavid du Colombier {
2457dd7cddfSDavid du Colombier 	uchar *map;
2467dd7cddfSDavid du Colombier 
2477dd7cddfSDavid du Colombier 	if(size > 8)
2487dd7cddfSDavid du Colombier 		giferror(h, "ReadGIF: can't handles %d bits per pixel", size);
2497dd7cddfSDavid du Colombier 	size = 3*(1<<size);
2507dd7cddfSDavid du Colombier 	if(Bread(h->fd, h->buf, size) != size)
2517dd7cddfSDavid du Colombier 		giferror(h, "ReadGIF: short read on color map");
2527dd7cddfSDavid du Colombier 	map = malloc(size);
2537dd7cddfSDavid du Colombier 	if(map == nil)
2547dd7cddfSDavid du Colombier 		giferror(h, memerr);
2557dd7cddfSDavid du Colombier 	memmove(map, h->buf, size);
2567dd7cddfSDavid du Colombier 	return map;
2577dd7cddfSDavid du Colombier }
2587dd7cddfSDavid du Colombier 
2597dd7cddfSDavid du Colombier static
2607dd7cddfSDavid du Colombier Rawimage*
readone(Header * h)2617dd7cddfSDavid du Colombier readone(Header *h)
2627dd7cddfSDavid du Colombier {
2637dd7cddfSDavid du Colombier 	Rawimage *i;
2647dd7cddfSDavid du Colombier 	int left, top, width, height;
2657dd7cddfSDavid du Colombier 
2667dd7cddfSDavid du Colombier 	if(Bread(h->fd, h->buf, 9) != 9)
2677dd7cddfSDavid du Colombier 		giferror(h, "ReadGIF: can't read image descriptor: %r");
2687dd7cddfSDavid du Colombier 	i = malloc(sizeof(Rawimage));
2697dd7cddfSDavid du Colombier 	if(i == nil)
2707dd7cddfSDavid du Colombier 		giferror(h, memerr);
2717dd7cddfSDavid du Colombier 	left = h->buf[0]+(h->buf[1]<<8);
2727dd7cddfSDavid du Colombier 	top = h->buf[2]+(h->buf[3]<<8);
2737dd7cddfSDavid du Colombier 	width = h->buf[4]+(h->buf[5]<<8);
2747dd7cddfSDavid du Colombier 	height = h->buf[6]+(h->buf[7]<<8);
2757dd7cddfSDavid du Colombier 	i->fields = h->buf[8];
2767dd7cddfSDavid du Colombier 	i->r.min.x = left;
2777dd7cddfSDavid du Colombier 	i->r.min.y = top;
2787dd7cddfSDavid du Colombier 	i->r.max.x = left+width;
2797dd7cddfSDavid du Colombier 	i->r.max.y = top+height;
2807dd7cddfSDavid du Colombier 	i->nchans = 1;
2817dd7cddfSDavid du Colombier 	i->chandesc = CRGB1;
282e37f07c4SDavid du Colombier 	memset(i->chans, 0, sizeof(i->chans));
2837dd7cddfSDavid du Colombier 	return i;
2847dd7cddfSDavid du Colombier }
2857dd7cddfSDavid du Colombier 
2867dd7cddfSDavid du Colombier 
2877dd7cddfSDavid du Colombier static
2887dd7cddfSDavid du Colombier int
readdata(Header * h,uchar * data)2897dd7cddfSDavid du Colombier readdata(Header *h, uchar *data)
2907dd7cddfSDavid du Colombier {
2917dd7cddfSDavid du Colombier 	int nbytes, n;
2927dd7cddfSDavid du Colombier 
2937dd7cddfSDavid du Colombier 	nbytes = Bgetc(h->fd);
2947dd7cddfSDavid du Colombier 	if(nbytes < 0)
2957dd7cddfSDavid du Colombier 		giferror(h, "ReadGIF: can't read data: %r");
2967dd7cddfSDavid du Colombier 	if(nbytes == 0)
2977dd7cddfSDavid du Colombier 		return 0;
2987dd7cddfSDavid du Colombier 	n = Bread(h->fd, data, nbytes);
2997dd7cddfSDavid du Colombier 	if(n < 0)
3007dd7cddfSDavid du Colombier 		giferror(h, "ReadGIF: can't read data: %r");
3017dd7cddfSDavid du Colombier 	if(n != nbytes)
3027dd7cddfSDavid du Colombier 		fprint(2, "ReadGIF: short data subblock\n");
3037dd7cddfSDavid du Colombier 	return n;
3047dd7cddfSDavid du Colombier }
3057dd7cddfSDavid du Colombier 
3067dd7cddfSDavid du Colombier static
3077dd7cddfSDavid du Colombier void
graphiccontrol(Header * h)3087dd7cddfSDavid du Colombier graphiccontrol(Header *h)
3097dd7cddfSDavid du Colombier {
3107dd7cddfSDavid du Colombier 	if(Bread(h->fd, h->buf, 5+1) != 5+1)
3117dd7cddfSDavid du Colombier 		giferror(h, readerr);
3127dd7cddfSDavid du Colombier 	h->flags = h->buf[1];
3137dd7cddfSDavid du Colombier 	h->delay = h->buf[2]+(h->buf[3]<<8);
3147dd7cddfSDavid du Colombier 	h->trindex = h->buf[4];
3157dd7cddfSDavid du Colombier }
3167dd7cddfSDavid du Colombier 
3177dd7cddfSDavid du Colombier static
3187dd7cddfSDavid du Colombier void
skipextension(Header * h)3197dd7cddfSDavid du Colombier skipextension(Header *h)
3207dd7cddfSDavid du Colombier {
3217dd7cddfSDavid du Colombier 	int type, hsize, hasdata, n;
3227dd7cddfSDavid du Colombier 	uchar data[256];
3237dd7cddfSDavid du Colombier 
3247dd7cddfSDavid du Colombier 	hsize = 0;
3257dd7cddfSDavid du Colombier 	hasdata = 0;
3267dd7cddfSDavid du Colombier 
3277dd7cddfSDavid du Colombier 	type = Bgetc(h->fd);
3287dd7cddfSDavid du Colombier 	switch(type){
3297dd7cddfSDavid du Colombier 	case Beof:
3307dd7cddfSDavid du Colombier 		giferror(h, extreaderr);
3317dd7cddfSDavid du Colombier 		break;
3327dd7cddfSDavid du Colombier 	case 0x01:	/* Plain Text Extension */
3337dd7cddfSDavid du Colombier 		hsize = 13;
3347dd7cddfSDavid du Colombier 		hasdata = 1;
3357dd7cddfSDavid du Colombier 		break;
3367dd7cddfSDavid du Colombier 	case 0xF9:	/* Graphic Control Extension */
3377dd7cddfSDavid du Colombier 		graphiccontrol(h);
3387dd7cddfSDavid du Colombier 		return;
3397dd7cddfSDavid du Colombier 	case 0xFE:	/* Comment Extension */
3407dd7cddfSDavid du Colombier 		hasdata = 1;
3417dd7cddfSDavid du Colombier 		break;
3427dd7cddfSDavid du Colombier 	case 0xFF:	/* Application Extension */
3437dd7cddfSDavid du Colombier 		hsize = Bgetc(h->fd);
3447dd7cddfSDavid du Colombier 		/* standard says this must be 11, but Adobe likes to put out 10-byte ones,
3457dd7cddfSDavid du Colombier 		 * so we pay attention to the field. */
3467dd7cddfSDavid du Colombier 		hasdata = 1;
3477dd7cddfSDavid du Colombier 		break;
3487dd7cddfSDavid du Colombier 	default:
3497dd7cddfSDavid du Colombier 		giferror(h, "ReadGIF: unknown extension");
3507dd7cddfSDavid du Colombier 	}
3517dd7cddfSDavid du Colombier 	if(hsize>0 && Bread(h->fd, h->buf, hsize) != hsize)
3527dd7cddfSDavid du Colombier 		giferror(h, extreaderr);
3537dd7cddfSDavid du Colombier 	if(!hasdata){
354*9f2726c3SDavid du Colombier 		/*
355*9f2726c3SDavid du Colombier 		 * This code used to check h->buf[hsize-1] != 0
356*9f2726c3SDavid du Colombier 		 * and giferror if so, but if !hasdata, hsize == 0.
357*9f2726c3SDavid du Colombier 		 */
3587dd7cddfSDavid du Colombier 		return;
3597dd7cddfSDavid du Colombier 	}
3607dd7cddfSDavid du Colombier 
3617dd7cddfSDavid du Colombier 	/* loop counter: Application Extension with NETSCAPE2.0 as string and 1 <loop.count> in data */
3627dd7cddfSDavid du Colombier 	if(type == 0xFF && hsize==11 && memcmp(h->buf, "NETSCAPE2.0", 11)==0){
3637dd7cddfSDavid du Colombier 		n = readdata(h, data);
3647dd7cddfSDavid du Colombier 		if(n == 0)
3657dd7cddfSDavid du Colombier 			return;
3667dd7cddfSDavid du Colombier 		if(n==3 && data[0]==1)
3677dd7cddfSDavid du Colombier 			h->loopcount = data[1] | (data[2]<<8);
3687dd7cddfSDavid du Colombier 	}
3697dd7cddfSDavid du Colombier 	while(readdata(h, data) != 0)
3707dd7cddfSDavid du Colombier 		;
3717dd7cddfSDavid du Colombier }
3727dd7cddfSDavid du Colombier 
3737dd7cddfSDavid du Colombier static
3747dd7cddfSDavid du Colombier uchar*
decode(Header * h,Rawimage * i,Entry * tbl)3757dd7cddfSDavid du Colombier decode(Header *h, Rawimage *i, Entry *tbl)
3767dd7cddfSDavid du Colombier {
377e37f07c4SDavid du Colombier 	int c, doclip, incode, codesize, CTM, EOD, pici, datai, stacki, nbits, sreg, fc, code, piclen;
3787dd7cddfSDavid du Colombier 	int csize, nentry, maxentry, first, ocode, ndata, nb;
379e37f07c4SDavid du Colombier 	uchar clip, *p, *pic;
3807dd7cddfSDavid du Colombier 	uchar stack[4096], data[256];
3817dd7cddfSDavid du Colombier 
3827dd7cddfSDavid du Colombier 	if(Bread(h->fd, h->buf, 1) != 1)
3837dd7cddfSDavid du Colombier 		giferror(h, "ReadGIF: can't read data: %r");
3847dd7cddfSDavid du Colombier 	codesize = h->buf[0];
3857dd7cddfSDavid du Colombier 	if(codesize>8 || 0>codesize)
3867dd7cddfSDavid du Colombier 		giferror(h, "ReadGIF: can't handle codesize %d", codesize);
387e37f07c4SDavid du Colombier 	doclip = 0;
3887dd7cddfSDavid du Colombier 	if(i->cmap!=nil && i->cmaplen!=3*(1<<codesize)
3897dd7cddfSDavid du Colombier 	  && (codesize!=2 || i->cmaplen!=3*2))			/* peculiar GIF bitmap files... */
390e37f07c4SDavid du Colombier 		doclip = 1;
3917dd7cddfSDavid du Colombier 
3927dd7cddfSDavid du Colombier 	CTM =1<<codesize;
3937dd7cddfSDavid du Colombier 	EOD = CTM+1;
3947dd7cddfSDavid du Colombier 
3957dd7cddfSDavid du Colombier 	piclen = (i->r.max.x-i->r.min.x)*(i->r.max.y-i->r.min.y);
3967dd7cddfSDavid du Colombier 	i->chanlen = piclen;
3977dd7cddfSDavid du Colombier 	pic = malloc(piclen);
3987dd7cddfSDavid du Colombier 	if(pic == nil)
3997dd7cddfSDavid du Colombier 		giferror(h, memerr);
4007dd7cddfSDavid du Colombier 	h->pic = pic;
4017dd7cddfSDavid du Colombier 	pici = 0;
4027dd7cddfSDavid du Colombier 	ndata = 0;
4037dd7cddfSDavid du Colombier 	datai = 0;
4047dd7cddfSDavid du Colombier 	nbits = 0;
4057dd7cddfSDavid du Colombier 	sreg = 0;
4067dd7cddfSDavid du Colombier 	fc = 0;
4077dd7cddfSDavid du Colombier 
4087dd7cddfSDavid du Colombier     Loop:
4097dd7cddfSDavid du Colombier 	for(;;){
4107dd7cddfSDavid du Colombier 		csize = codesize+1;
4117dd7cddfSDavid du Colombier 		nentry = EOD+1;
4127dd7cddfSDavid du Colombier 		maxentry = (1<<csize)-1;
4137dd7cddfSDavid du Colombier 		first = 1;
4147dd7cddfSDavid du Colombier 		ocode = -1;
4157dd7cddfSDavid du Colombier 
4167dd7cddfSDavid du Colombier 		for(;; ocode = incode) {
4177dd7cddfSDavid du Colombier 			while(nbits < csize) {
4187dd7cddfSDavid du Colombier 				if(datai == ndata){
4197dd7cddfSDavid du Colombier 					ndata = readdata(h, data);
4207dd7cddfSDavid du Colombier 					if(ndata == 0)
4217dd7cddfSDavid du Colombier 						goto Return;
4227dd7cddfSDavid du Colombier 					datai = 0;
4237dd7cddfSDavid du Colombier 				}
4247dd7cddfSDavid du Colombier 				c = data[datai++];
4257dd7cddfSDavid du Colombier 				sreg |= c<<nbits;
4267dd7cddfSDavid du Colombier 				nbits += 8;
4277dd7cddfSDavid du Colombier 			}
4287dd7cddfSDavid du Colombier 			code = sreg & ((1<<csize) - 1);
4297dd7cddfSDavid du Colombier 			sreg >>= csize;
4307dd7cddfSDavid du Colombier 			nbits -= csize;
4317dd7cddfSDavid du Colombier 
4327dd7cddfSDavid du Colombier 			if(code == EOD){
4337dd7cddfSDavid du Colombier 				ndata = readdata(h, data);
4347dd7cddfSDavid du Colombier 				if(ndata != 0)
435e37f07c4SDavid du Colombier 					fprint(2, "ReadGIF: unexpected data past EOD\n");
4367dd7cddfSDavid du Colombier 				goto Return;
4377dd7cddfSDavid du Colombier 			}
4387dd7cddfSDavid du Colombier 
4397dd7cddfSDavid du Colombier 			if(code == CTM)
4407dd7cddfSDavid du Colombier 				goto Loop;
4417dd7cddfSDavid du Colombier 
4427dd7cddfSDavid du Colombier 			stacki = (sizeof stack)-1;
4437dd7cddfSDavid du Colombier 
4447dd7cddfSDavid du Colombier 			incode = code;
4457dd7cddfSDavid du Colombier 
4467dd7cddfSDavid du Colombier 			/* special case for KwKwK */
4477dd7cddfSDavid du Colombier 			if(code == nentry) {
4487dd7cddfSDavid du Colombier 				stack[stacki--] = fc;
4497dd7cddfSDavid du Colombier 				code = ocode;
4507dd7cddfSDavid du Colombier 			}
4517dd7cddfSDavid du Colombier 
452e37f07c4SDavid du Colombier 			if(code > nentry){
453e37f07c4SDavid du Colombier 				fprint(2, "ReadGIF: GIF invalid, code out of range, %x > %x\n", code, nentry);
454e37f07c4SDavid du Colombier 				code = nentry;
455e37f07c4SDavid du Colombier 			}
4564ac975e2SDavid du Colombier 			for(c=code; stacki>0 && c>=0; c=tbl[c].prefix)
4577dd7cddfSDavid du Colombier 				stack[stacki--] = tbl[c].exten;
4587dd7cddfSDavid du Colombier 
4597dd7cddfSDavid du Colombier 			nb = (sizeof stack)-(stacki+1);
4607dd7cddfSDavid du Colombier 			if(pici+nb > piclen){
4617dd7cddfSDavid du Colombier 				/* this common error is harmless
4627dd7cddfSDavid du Colombier 				 * we have to keep reading to keep the blocks in sync */
4637dd7cddfSDavid du Colombier 				;
4647dd7cddfSDavid du Colombier 			}else{
4657dd7cddfSDavid du Colombier 				memmove(pic+pici, stack+stacki+1, sizeof stack - (stacki+1));
4667dd7cddfSDavid du Colombier 				pici += nb;
4677dd7cddfSDavid du Colombier 			}
4687dd7cddfSDavid du Colombier 
4697dd7cddfSDavid du Colombier 			fc = stack[stacki+1];
4707dd7cddfSDavid du Colombier 
4717dd7cddfSDavid du Colombier 			if(first){
4727dd7cddfSDavid du Colombier 				first = 0;
4737dd7cddfSDavid du Colombier 				continue;
4747dd7cddfSDavid du Colombier 			}
4757dd7cddfSDavid du Colombier 			#define early 0 /* peculiar tiff feature here for reference */
4767dd7cddfSDavid du Colombier 			if(nentry == maxentry-early) {
4777dd7cddfSDavid du Colombier 				if(csize >= 12)
4787dd7cddfSDavid du Colombier 					continue;
4797dd7cddfSDavid du Colombier 				csize++;
4807dd7cddfSDavid du Colombier 				maxentry = (1<<csize);
4817dd7cddfSDavid du Colombier 				if(csize < 12)
4827dd7cddfSDavid du Colombier 					maxentry--;
4837dd7cddfSDavid du Colombier 			}
4847dd7cddfSDavid du Colombier 			tbl[nentry].prefix = ocode;
4857dd7cddfSDavid du Colombier 			tbl[nentry].exten = fc;
4867dd7cddfSDavid du Colombier 			nentry++;
4877dd7cddfSDavid du Colombier 		}
4887dd7cddfSDavid du Colombier 	}
4897dd7cddfSDavid du Colombier 
4907dd7cddfSDavid du Colombier Return:
491e37f07c4SDavid du Colombier 	if(doclip){
492e37f07c4SDavid du Colombier 		clip = i->cmaplen/3;
493e37f07c4SDavid du Colombier 		for(p = pic; p < pic+piclen; p++)
494e37f07c4SDavid du Colombier 			if(*p >= clip)
495e37f07c4SDavid du Colombier 				*p = clip;
496e37f07c4SDavid du Colombier 	}
4977dd7cddfSDavid du Colombier 	h->pic = nil;
4987dd7cddfSDavid du Colombier 	return pic;
4997dd7cddfSDavid du Colombier }
5007dd7cddfSDavid du Colombier 
5017dd7cddfSDavid du Colombier static
5027dd7cddfSDavid du Colombier void
interlace(Header * h,Rawimage * image)5037dd7cddfSDavid du Colombier interlace(Header *h, Rawimage *image)
5047dd7cddfSDavid du Colombier {
5057dd7cddfSDavid du Colombier 	uchar *pic;
5067dd7cddfSDavid du Colombier 	Rectangle r;
5077dd7cddfSDavid du Colombier 	int dx, yy, y;
5087dd7cddfSDavid du Colombier 	uchar *ipic;
5097dd7cddfSDavid du Colombier 
5107dd7cddfSDavid du Colombier 	pic = image->chans[0];
5117dd7cddfSDavid du Colombier 	r = image->r;
5127dd7cddfSDavid du Colombier 	dx = r.max.x-r.min.x;
5137dd7cddfSDavid du Colombier 	ipic = malloc(dx*(r.max.y-r.min.y));
5147dd7cddfSDavid du Colombier 	if(ipic == nil)
5157dd7cddfSDavid du Colombier 		giferror(h, nil);
5167dd7cddfSDavid du Colombier 
5177dd7cddfSDavid du Colombier 	/* Group 1: every 8th row, starting with row 0 */
5187dd7cddfSDavid du Colombier 	yy = 0;
5197dd7cddfSDavid du Colombier 	for(y=r.min.y; y<r.max.y; y+=8){
5207dd7cddfSDavid du Colombier 		memmove(&ipic[(y-r.min.y)*dx], &pic[yy*dx], dx);
5217dd7cddfSDavid du Colombier 		yy++;
5227dd7cddfSDavid du Colombier 	}
5237dd7cddfSDavid du Colombier 
5247dd7cddfSDavid du Colombier 	/* Group 2: every 8th row, starting with row 4 */
5257dd7cddfSDavid du Colombier 	for(y=r.min.y+4; y<r.max.y; y+=8){
5267dd7cddfSDavid du Colombier 		memmove(&ipic[(y-r.min.y)*dx], &pic[yy*dx], dx);
5277dd7cddfSDavid du Colombier 		yy++;
5287dd7cddfSDavid du Colombier 	}
5297dd7cddfSDavid du Colombier 
5307dd7cddfSDavid du Colombier 	/* Group 3: every 4th row, starting with row 2 */
5317dd7cddfSDavid du Colombier 	for(y=r.min.y+2; y<r.max.y; y+=4){
5327dd7cddfSDavid du Colombier 		memmove(&ipic[(y-r.min.y)*dx], &pic[yy*dx], dx);
5337dd7cddfSDavid du Colombier 		yy++;
5347dd7cddfSDavid du Colombier 	}
5357dd7cddfSDavid du Colombier 
5367dd7cddfSDavid du Colombier 	/* Group 4: every 2nd row, starting with row 1 */
5377dd7cddfSDavid du Colombier 	for(y=r.min.y+1; y<r.max.y; y+=2){
5387dd7cddfSDavid du Colombier 		memmove(&ipic[(y-r.min.y)*dx], &pic[yy*dx], dx);
5397dd7cddfSDavid du Colombier 		yy++;
5407dd7cddfSDavid du Colombier 	}
5417dd7cddfSDavid du Colombier 
5427dd7cddfSDavid du Colombier 	free(image->chans[0]);
5437dd7cddfSDavid du Colombier 	image->chans[0] = ipic;
5447dd7cddfSDavid du Colombier }
545