xref: /inferno-os/appl/lib/writegif.b (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1*37da2899SCharles.Forsythimplement WImagefile;
2*37da2899SCharles.Forsyth
3*37da2899SCharles.Forsythinclude "sys.m";
4*37da2899SCharles.Forsyth	sys: Sys;
5*37da2899SCharles.Forsyth
6*37da2899SCharles.Forsythinclude "draw.m";
7*37da2899SCharles.Forsyth	draw: Draw;
8*37da2899SCharles.Forsyth	Chans, Display, Image, Rect: import draw;
9*37da2899SCharles.Forsyth
10*37da2899SCharles.Forsythinclude "bufio.m";
11*37da2899SCharles.Forsyth	bufio: Bufio;
12*37da2899SCharles.Forsyth	Iobuf: import bufio;
13*37da2899SCharles.Forsyth
14*37da2899SCharles.Forsythinclude "imagefile.m";
15*37da2899SCharles.Forsyth
16*37da2899SCharles.ForsythNhash: con 4001;
17*37da2899SCharles.Forsyth
18*37da2899SCharles.ForsythEntry: adt
19*37da2899SCharles.Forsyth{
20*37da2899SCharles.Forsyth	index: int;
21*37da2899SCharles.Forsyth	prefix: int;
22*37da2899SCharles.Forsyth	exten: int;
23*37da2899SCharles.Forsyth	next:	cyclic ref Entry;
24*37da2899SCharles.Forsyth};
25*37da2899SCharles.Forsyth
26*37da2899SCharles.ForsythIO: adt
27*37da2899SCharles.Forsyth{
28*37da2899SCharles.Forsyth	fd:	ref Iobuf;
29*37da2899SCharles.Forsyth	buf:	array of byte;
30*37da2899SCharles.Forsyth	i:	int;
31*37da2899SCharles.Forsyth	nbits:	int;	 # bits in right side of shift register
32*37da2899SCharles.Forsyth	sreg:	int;	# shift register
33*37da2899SCharles.Forsyth};
34*37da2899SCharles.Forsyth
35*37da2899SCharles.Forsythtbl: array of ref Entry;
36*37da2899SCharles.Forsyth
37*37da2899SCharles.Forsythcolormap: array of array of byte;
38*37da2899SCharles.Forsythlog2 := array[] of {1 => 0, 2 => 1, 4 => 2, 8 => 3, * => -1};
39*37da2899SCharles.Forsyth
40*37da2899SCharles.Forsythinit(iomod: Bufio)
41*37da2899SCharles.Forsyth{
42*37da2899SCharles.Forsyth	if(sys == nil){
43*37da2899SCharles.Forsyth		sys = load Sys Sys->PATH;
44*37da2899SCharles.Forsyth		draw = load Draw Draw->PATH;
45*37da2899SCharles.Forsyth	}
46*37da2899SCharles.Forsyth	bufio = iomod;
47*37da2899SCharles.Forsyth}
48*37da2899SCharles.Forsyth
49*37da2899SCharles.Forsythwriteimage(fd: ref Iobuf, image: ref Image): string
50*37da2899SCharles.Forsyth{
51*37da2899SCharles.Forsyth	case image.chans.desc {
52*37da2899SCharles.Forsyth	(Draw->GREY1).desc or (Draw->GREY2).desc or
53*37da2899SCharles.Forsyth	(Draw->GREY4).desc or (Draw->GREY8).desc or
54*37da2899SCharles.Forsyth	(Draw->CMAP8).desc =>
55*37da2899SCharles.Forsyth		if(image.depth > 8 || (image.depth&(image.depth-1)) != 0)
56*37da2899SCharles.Forsyth			return "inconsistent depth";
57*37da2899SCharles.Forsyth	* =>
58*37da2899SCharles.Forsyth		return "unsupported channel type";
59*37da2899SCharles.Forsyth	}
60*37da2899SCharles.Forsyth
61*37da2899SCharles.Forsyth	inittbl();
62*37da2899SCharles.Forsyth
63*37da2899SCharles.Forsyth	writeheader(fd, image);
64*37da2899SCharles.Forsyth	writedescriptor(fd, image);
65*37da2899SCharles.Forsyth
66*37da2899SCharles.Forsyth	err := writedata(fd, image);
67*37da2899SCharles.Forsyth	if(err != nil)
68*37da2899SCharles.Forsyth		return err;
69*37da2899SCharles.Forsyth
70*37da2899SCharles.Forsyth	writetrailer(fd);
71*37da2899SCharles.Forsyth	fd.flush();
72*37da2899SCharles.Forsyth	return err;
73*37da2899SCharles.Forsyth}
74*37da2899SCharles.Forsyth
75*37da2899SCharles.Forsythinittbl()
76*37da2899SCharles.Forsyth{
77*37da2899SCharles.Forsyth	tbl = array[4096] of ref Entry;
78*37da2899SCharles.Forsyth	for(i:=0; i<len tbl; i++)
79*37da2899SCharles.Forsyth		tbl[i] = ref Entry(i, -1, i, nil);
80*37da2899SCharles.Forsyth}
81*37da2899SCharles.Forsyth
82*37da2899SCharles.Forsyth# Write header, logical screen descriptor, and color map
83*37da2899SCharles.Forsythwriteheader(fd: ref Iobuf, image: ref Image): string
84*37da2899SCharles.Forsyth{
85*37da2899SCharles.Forsyth	# Header
86*37da2899SCharles.Forsyth	fd.puts("GIF89a");
87*37da2899SCharles.Forsyth
88*37da2899SCharles.Forsyth	# Logical Screen Descriptor
89*37da2899SCharles.Forsyth	put2(fd, image.r.dx());
90*37da2899SCharles.Forsyth	put2(fd, image.r.dy());
91*37da2899SCharles.Forsyth	# color table present, 4 bits per color (for RGBV best case), size of color map
92*37da2899SCharles.Forsyth	fd.putb(byte ((1<<7)|(3<<4)|(image.depth-1)));
93*37da2899SCharles.Forsyth	fd.putb(byte 0);	# white background (doesn't matter anyway)
94*37da2899SCharles.Forsyth	fd.putb(byte 0);	# pixel aspect ratio - unused
95*37da2899SCharles.Forsyth
96*37da2899SCharles.Forsyth	# Global Color Table
97*37da2899SCharles.Forsyth	getcolormap(image);
98*37da2899SCharles.Forsyth	ldepth := log2[image.depth];
99*37da2899SCharles.Forsyth	if(image.chans.eq(Draw->GREY8))
100*37da2899SCharles.Forsyth		ldepth = 4;
101*37da2899SCharles.Forsyth	fd.write(colormap[ldepth], len colormap[ldepth]);
102*37da2899SCharles.Forsyth	return nil;
103*37da2899SCharles.Forsyth}
104*37da2899SCharles.Forsyth
105*37da2899SCharles.Forsyth# Write image descriptor
106*37da2899SCharles.Forsythwritedescriptor(fd: ref Iobuf, image: ref Image)
107*37da2899SCharles.Forsyth{
108*37da2899SCharles.Forsyth	# Image Separator
109*37da2899SCharles.Forsyth	fd.putb(byte 16r2C);
110*37da2899SCharles.Forsyth
111*37da2899SCharles.Forsyth	# Left, top, width, height
112*37da2899SCharles.Forsyth	put2(fd, 0);
113*37da2899SCharles.Forsyth	put2(fd, 0);
114*37da2899SCharles.Forsyth	put2(fd, image.r.dx());
115*37da2899SCharles.Forsyth	put2(fd, image.r.dy());
116*37da2899SCharles.Forsyth	# no special processing
117*37da2899SCharles.Forsyth	fd.putb(byte 0);
118*37da2899SCharles.Forsyth}
119*37da2899SCharles.Forsyth
120*37da2899SCharles.Forsyth# Write data
121*37da2899SCharles.Forsythwritedata(fd: ref Iobuf, image: ref Image): string
122*37da2899SCharles.Forsyth{
123*37da2899SCharles.Forsyth	# LZW Minimum code size
124*37da2899SCharles.Forsyth	if(image.depth == 1)
125*37da2899SCharles.Forsyth		fd.putb(byte 2);
126*37da2899SCharles.Forsyth
127*37da2899SCharles.Forsyth	else
128*37da2899SCharles.Forsyth		fd.putb(byte image.depth);
129*37da2899SCharles.Forsyth
130*37da2899SCharles.Forsyth	# Encode and emit the data
131*37da2899SCharles.Forsyth	err := encode(fd, image);
132*37da2899SCharles.Forsyth	if(err != nil)
133*37da2899SCharles.Forsyth		return err;
134*37da2899SCharles.Forsyth
135*37da2899SCharles.Forsyth	# Block Terminator
136*37da2899SCharles.Forsyth	fd.putb(byte 0);
137*37da2899SCharles.Forsyth	return nil;
138*37da2899SCharles.Forsyth}
139*37da2899SCharles.Forsyth
140*37da2899SCharles.Forsyth# Write data
141*37da2899SCharles.Forsythwritetrailer(fd: ref Iobuf)
142*37da2899SCharles.Forsyth{
143*37da2899SCharles.Forsyth	fd.putb(byte 16r3B);
144*37da2899SCharles.Forsyth}
145*37da2899SCharles.Forsyth
146*37da2899SCharles.Forsyth# Write little-endian 16-bit integer
147*37da2899SCharles.Forsythput2(fd: ref Iobuf, i: int)
148*37da2899SCharles.Forsyth{
149*37da2899SCharles.Forsyth	fd.putb(byte i);
150*37da2899SCharles.Forsyth	fd.putb(byte (i>>8));
151*37da2899SCharles.Forsyth}
152*37da2899SCharles.Forsyth
153*37da2899SCharles.Forsyth# Get color map for all ldepths, in format suitable for writing out
154*37da2899SCharles.Forsythgetcolormap(image: ref Draw->Image)
155*37da2899SCharles.Forsyth{
156*37da2899SCharles.Forsyth	if(colormap != nil)
157*37da2899SCharles.Forsyth		return;
158*37da2899SCharles.Forsyth	colormap = array[5] of array of byte;
159*37da2899SCharles.Forsyth	display := image.display;
160*37da2899SCharles.Forsyth	colormap[4] = array[3*256] of byte;
161*37da2899SCharles.Forsyth	colormap[3] = array[3*256] of byte;
162*37da2899SCharles.Forsyth	colormap[2] = array[3*16] of byte;
163*37da2899SCharles.Forsyth	colormap[1] = array[3*4] of byte;
164*37da2899SCharles.Forsyth	colormap[0] = array[3*2] of byte;
165*37da2899SCharles.Forsyth	c := colormap[4];
166*37da2899SCharles.Forsyth	for(i:=0; i<256; i++){
167*37da2899SCharles.Forsyth		c[3*i+0] = byte i;
168*37da2899SCharles.Forsyth		c[3*i+1] = byte i;
169*37da2899SCharles.Forsyth		c[3*i+2] = byte i;
170*37da2899SCharles.Forsyth	}
171*37da2899SCharles.Forsyth	c = colormap[3];
172*37da2899SCharles.Forsyth	for(i=0; i<256; i++){
173*37da2899SCharles.Forsyth		(r, g, b) := display.cmap2rgb(i);
174*37da2899SCharles.Forsyth		c[3*i+0] = byte r;
175*37da2899SCharles.Forsyth		c[3*i+1] = byte g;
176*37da2899SCharles.Forsyth		c[3*i+2] = byte b;
177*37da2899SCharles.Forsyth	}
178*37da2899SCharles.Forsyth	c = colormap[2];
179*37da2899SCharles.Forsyth	for(i=0; i<16; i++){
180*37da2899SCharles.Forsyth		col := (i<<4)|i;
181*37da2899SCharles.Forsyth		(r, g, b) := display.cmap2rgb(col);
182*37da2899SCharles.Forsyth		c[3*i+0] = byte r;
183*37da2899SCharles.Forsyth		c[3*i+1] = byte g;
184*37da2899SCharles.Forsyth		c[3*i+2] = byte b;
185*37da2899SCharles.Forsyth	}
186*37da2899SCharles.Forsyth	c = colormap[1];
187*37da2899SCharles.Forsyth	for(i=0; i<4; i++){
188*37da2899SCharles.Forsyth		col := (i<<6)|(i<<4)|(i<<2)|i;
189*37da2899SCharles.Forsyth		(r, g, b) := display.cmap2rgb(col);
190*37da2899SCharles.Forsyth		c[3*i+0] = byte r;
191*37da2899SCharles.Forsyth		c[3*i+1] = byte g;
192*37da2899SCharles.Forsyth		c[3*i+2] = byte b;
193*37da2899SCharles.Forsyth	}
194*37da2899SCharles.Forsyth	c = colormap[0];
195*37da2899SCharles.Forsyth	for(i=0; i<2; i++){
196*37da2899SCharles.Forsyth		if(i == 0)
197*37da2899SCharles.Forsyth			col := 0;
198*37da2899SCharles.Forsyth		else
199*37da2899SCharles.Forsyth			col = 16rFF;
200*37da2899SCharles.Forsyth		(r, g, b) := display.cmap2rgb(col);
201*37da2899SCharles.Forsyth		c[3*i+0] = byte r;
202*37da2899SCharles.Forsyth		c[3*i+1] = byte g;
203*37da2899SCharles.Forsyth		c[3*i+2] = byte b;
204*37da2899SCharles.Forsyth	}
205*37da2899SCharles.Forsyth}
206*37da2899SCharles.Forsyth
207*37da2899SCharles.Forsyth# Put n bits of c into output at io.buf[i];
208*37da2899SCharles.Forsythoutput(io: ref IO, c, n: int)
209*37da2899SCharles.Forsyth{
210*37da2899SCharles.Forsyth	if(c < 0){
211*37da2899SCharles.Forsyth		if(io.nbits != 0)
212*37da2899SCharles.Forsyth			io.buf[io.i++] = byte io.sreg;
213*37da2899SCharles.Forsyth		io.fd.putb(byte io.i);
214*37da2899SCharles.Forsyth		io.fd.write(io.buf, io.i);
215*37da2899SCharles.Forsyth		io.nbits = 0;
216*37da2899SCharles.Forsyth		return;
217*37da2899SCharles.Forsyth	}
218*37da2899SCharles.Forsyth
219*37da2899SCharles.Forsyth	if(io.nbits+n >= 31){
220*37da2899SCharles.Forsyth		sys->print("panic: WriteGIF sr overflow\n");
221*37da2899SCharles.Forsyth		exit;
222*37da2899SCharles.Forsyth	}
223*37da2899SCharles.Forsyth	io.sreg |= c<<io.nbits;
224*37da2899SCharles.Forsyth	io.nbits += n;
225*37da2899SCharles.Forsyth
226*37da2899SCharles.Forsyth	while(io.nbits >= 8){
227*37da2899SCharles.Forsyth		io.buf[io.i++] = byte io.sreg;
228*37da2899SCharles.Forsyth		io.sreg >>= 8;
229*37da2899SCharles.Forsyth		io.nbits -= 8;
230*37da2899SCharles.Forsyth	}
231*37da2899SCharles.Forsyth
232*37da2899SCharles.Forsyth	if(io.i >= 255){
233*37da2899SCharles.Forsyth		io.fd.putb(byte 255);
234*37da2899SCharles.Forsyth		io.fd.write(io.buf, 255);
235*37da2899SCharles.Forsyth		io.buf[0:] = io.buf[255:io.i];
236*37da2899SCharles.Forsyth		io.i -= 255;
237*37da2899SCharles.Forsyth	}
238*37da2899SCharles.Forsyth}
239*37da2899SCharles.Forsyth
240*37da2899SCharles.Forsyth# LZW encoder
241*37da2899SCharles.Forsythencode(fd: ref Iobuf, image: ref Image): string
242*37da2899SCharles.Forsyth{
243*37da2899SCharles.Forsyth	c, h, csize, prefix: int;
244*37da2899SCharles.Forsyth	e, oe: ref Entry;
245*37da2899SCharles.Forsyth
246*37da2899SCharles.Forsyth	first := 1;
247*37da2899SCharles.Forsyth	ld := log2[image.depth];
248*37da2899SCharles.Forsyth	# ldepth 0 must generate codesize 2 with values 0 and 1 (see the spec.)
249*37da2899SCharles.Forsyth	ld0 := ld;
250*37da2899SCharles.Forsyth	if(ld0 == 0)
251*37da2899SCharles.Forsyth		ld0 = 1;
252*37da2899SCharles.Forsyth	codesize := (1<<ld0);
253*37da2899SCharles.Forsyth	CTM := 1<<codesize;
254*37da2899SCharles.Forsyth	EOD := CTM+1;
255*37da2899SCharles.Forsyth
256*37da2899SCharles.Forsyth	io := ref IO (fd, array[300] of byte, 0, 0, 0);
257*37da2899SCharles.Forsyth	sreg := 0;
258*37da2899SCharles.Forsyth	nbits := 0;
259*37da2899SCharles.Forsyth	bitsperpixel := 1<<ld;
260*37da2899SCharles.Forsyth	pm := (1<<bitsperpixel)-1;
261*37da2899SCharles.Forsyth
262*37da2899SCharles.Forsyth	# Read image data into memory
263*37da2899SCharles.Forsyth	# potentially one extra byte on each end of each scan line
264*37da2899SCharles.Forsyth	data := array[image.r.dy()*(2+(image.r.dx()>>(3-log2[image.depth])))] of byte;
265*37da2899SCharles.Forsyth	ndata := image.readpixels(image.r, data);
266*37da2899SCharles.Forsyth	if(ndata < 0)
267*37da2899SCharles.Forsyth		return sys->sprint("WriteGIF: readpixels: %r");
268*37da2899SCharles.Forsyth	datai := 0;
269*37da2899SCharles.Forsyth	x := image.r.min.x;
270*37da2899SCharles.Forsyth
271*37da2899SCharles.ForsythInit:
272*37da2899SCharles.Forsyth	for(;;){
273*37da2899SCharles.Forsyth		csize = codesize+1;
274*37da2899SCharles.Forsyth		nentry := EOD+1;
275*37da2899SCharles.Forsyth		maxentry := (1<<csize);
276*37da2899SCharles.Forsyth		hash := array[Nhash] of ref Entry;
277*37da2899SCharles.Forsyth		for(i := 0; i<nentry; i++){
278*37da2899SCharles.Forsyth			e = tbl[i];
279*37da2899SCharles.Forsyth			h = (e.prefix<<24) | (e.exten<<8);
280*37da2899SCharles.Forsyth			h %= Nhash;
281*37da2899SCharles.Forsyth			if(h < 0)
282*37da2899SCharles.Forsyth				h += Nhash;
283*37da2899SCharles.Forsyth			e.next = hash[h];
284*37da2899SCharles.Forsyth			hash[h] = e;
285*37da2899SCharles.Forsyth		}
286*37da2899SCharles.Forsyth		prefix = -1;
287*37da2899SCharles.Forsyth		if(first)
288*37da2899SCharles.Forsyth			output(io, CTM, csize);
289*37da2899SCharles.Forsyth		first = 0;
290*37da2899SCharles.Forsyth
291*37da2899SCharles.Forsyth		# Scan over pixels.  Because of partially filled bytes on ends of scan lines,
292*37da2899SCharles.Forsyth		# which must be ignored in the data stream passed to GIF, this is more
293*37da2899SCharles.Forsyth		# complex than we'd like
294*37da2899SCharles.Forsyth	Next:
295*37da2899SCharles.Forsyth		for(;;){
296*37da2899SCharles.Forsyth			if(ld != 3){
297*37da2899SCharles.Forsyth				# beginning of scan line is difficult; prime the shift register
298*37da2899SCharles.Forsyth				if(x == image.r.min.x){
299*37da2899SCharles.Forsyth					if(datai == ndata)
300*37da2899SCharles.Forsyth						break;
301*37da2899SCharles.Forsyth					sreg = int data[datai++];
302*37da2899SCharles.Forsyth					nbits = 8-((x&(7>>ld))<<ld);
303*37da2899SCharles.Forsyth				}
304*37da2899SCharles.Forsyth				x++;
305*37da2899SCharles.Forsyth				if(x == image.r.max.x)
306*37da2899SCharles.Forsyth					x = image.r.min.x;
307*37da2899SCharles.Forsyth			}
308*37da2899SCharles.Forsyth			if(nbits == 0){
309*37da2899SCharles.Forsyth				if(datai == ndata)
310*37da2899SCharles.Forsyth					break;
311*37da2899SCharles.Forsyth				sreg = int data[datai++];
312*37da2899SCharles.Forsyth				nbits = 8;
313*37da2899SCharles.Forsyth			}
314*37da2899SCharles.Forsyth			nbits -= bitsperpixel;
315*37da2899SCharles.Forsyth			c = sreg>>nbits & pm;
316*37da2899SCharles.Forsyth			h = prefix<<24 | c<<8;
317*37da2899SCharles.Forsyth			h %= Nhash;
318*37da2899SCharles.Forsyth			if(h < 0)
319*37da2899SCharles.Forsyth				h += Nhash;
320*37da2899SCharles.Forsyth			oe = nil;
321*37da2899SCharles.Forsyth			for(e = hash[h]; e!=nil; e=e.next){
322*37da2899SCharles.Forsyth				if(e.prefix == prefix && e.exten == c){
323*37da2899SCharles.Forsyth					if(oe != nil){
324*37da2899SCharles.Forsyth						oe.next = e.next;
325*37da2899SCharles.Forsyth						e.next = hash[h];
326*37da2899SCharles.Forsyth						hash[h] = e;
327*37da2899SCharles.Forsyth					}
328*37da2899SCharles.Forsyth					prefix = e.index;
329*37da2899SCharles.Forsyth					continue Next;
330*37da2899SCharles.Forsyth				}
331*37da2899SCharles.Forsyth				oe = e;
332*37da2899SCharles.Forsyth			}
333*37da2899SCharles.Forsyth
334*37da2899SCharles.Forsyth			output(io, prefix, csize);
335*37da2899SCharles.Forsyth			early:=0; # peculiar tiff feature here for reference
336*37da2899SCharles.Forsyth			if(nentry == maxentry-early){
337*37da2899SCharles.Forsyth				if(csize == 12){
338*37da2899SCharles.Forsyth					nbits += codesize;	# unget pixel
339*37da2899SCharles.Forsyth					x--;
340*37da2899SCharles.Forsyth					output(io, CTM, csize);
341*37da2899SCharles.Forsyth					continue Init;
342*37da2899SCharles.Forsyth				}
343*37da2899SCharles.Forsyth				csize++;
344*37da2899SCharles.Forsyth				maxentry = (1<<csize);
345*37da2899SCharles.Forsyth			}
346*37da2899SCharles.Forsyth
347*37da2899SCharles.Forsyth			e = tbl[nentry];
348*37da2899SCharles.Forsyth			e.prefix = prefix;
349*37da2899SCharles.Forsyth			e.exten = c;
350*37da2899SCharles.Forsyth			e.next = hash[h];
351*37da2899SCharles.Forsyth			hash[h] = e;
352*37da2899SCharles.Forsyth
353*37da2899SCharles.Forsyth			prefix = c;
354*37da2899SCharles.Forsyth			nentry++;
355*37da2899SCharles.Forsyth		}
356*37da2899SCharles.Forsyth		break Init;
357*37da2899SCharles.Forsyth	}
358*37da2899SCharles.Forsyth	output(io, prefix, csize);
359*37da2899SCharles.Forsyth	output(io, EOD, csize);
360*37da2899SCharles.Forsyth	output(io, -1, csize);
361*37da2899SCharles.Forsyth	return nil;
362*37da2899SCharles.Forsyth}
363