Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
#include <u.h>
2
#include <libc.h>
3
#include <bio.h>
4
#include <draw.h>
5
#include <event.h>
6
#include "imagefile.h"
7
 
8
int		cflag = 0;
9
int		dflag = 0;
10
int		eflag = 0;
11
int		nineflag = 0;
12
int		threeflag = 0;
13
int		output = 0;
14
ulong	outchan = CMAP8;
15
Image	**allims;
16
Image	**allmasks;
17
Rawimage	**allimages;
18
int		which;
19
int		defaultcolor = 1;
20
 
21
enum{
22
	Border	= 2,
23
	Edge		= 5
24
};
25
 
26
char	*show(int, char*);
27
 
28
Rectangle
29
imager(void)
30
{
31
	Rectangle r;
32
 
33
	if(allims==nil || allims[0]==nil)
34
		return screen->r;
35
	r = insetrect(screen->clipr, Edge+Border);
36
	r.max.x = r.min.x+Dx(allims[0]->r);
37
	r.max.y = r.min.y+Dy(allims[0]->r);
38
	return r;
39
}
40
 
41
void
42
eresized(int new)
43
{
44
	Rectangle r;
45
 
46
	if(new && getwindow(display, Refnone) < 0){
47
		fprint(2, "gif: can't reattach to window\n");
48
		exits("resize");
49
	}
50
	if(allims==nil || allims[which]==nil)
51
		return;
52
	r = imager();
53
	border(screen, r, -Border, nil, ZP);
54
	r.min.x += allims[which]->r.min.x - allims[0]->r.min.x;
55
	r.min.y += allims[which]->r.min.y - allims[0]->r.min.y;
56
	if(which > 0 && allimages[which]->gifflags & TRANSP)
57
		drawop(screen, r, allims[which], allmasks[which],
58
			allims[which]->r.min, SoverD);
59
	else
60
		drawop(screen, r, allims[which], allmasks[which],
61
			allims[which]->r.min, S);
62
	flushimage(display, 1);
63
}
64
 
65
void
66
main(int argc, char *argv[])
67
{
68
	int fd, i;
69
	char *err;
70
 
71
	ARGBEGIN{
72
	case '3':		/* produce encoded, compressed, three-color bitmap file; no display by default */
73
		threeflag++;
74
		/* fall through */
75
	case 't':		/* produce encoded, compressed, true-color bitmap file; no display by default */
76
		cflag++;
77
		dflag++;
78
		output++;
79
		defaultcolor = 0;
80
		outchan = RGB24;
81
		break;
82
	case 'c':		/* produce encoded, compressed, bitmap file; no display by default */
83
		cflag++;
84
		dflag++;
85
		output++;
86
		if(defaultcolor)
87
			outchan = CMAP8;
88
		break;
89
	case 'd':		/* suppress display of image */
90
		dflag++;
91
		break;
92
	case 'e':		/* disable floyd-steinberg error diffusion */
93
		eflag++;
94
		break;
95
	case 'k':		/* force black and white */
96
		defaultcolor = 0;
97
		outchan = GREY8;
98
		break;
99
	case 'v':		/* force RGBV */
100
		defaultcolor = 0;
101
		outchan = CMAP8;
102
		break;
103
	case '9':		/* produce plan 9, uncompressed, bitmap file; no display by default */
104
		nineflag++;
105
		dflag++;
106
		output++;
107
		if(defaultcolor)
108
			outchan = CMAP8;
109
		break;
110
	default:
111
		fprint(2, "usage: gif -39cdektv  [file.gif ...]\n");
112
		exits("usage");
113
	}ARGEND;
114
 
115
	err = nil;
116
	if(argc == 0)
117
		err = show(0, "<stdin>");
118
	else{
119
		for(i=0; i<argc; i++){
120
			fd = open(argv[i], OREAD);
121
			if(fd < 0){
122
				fprint(2, "gif: can't open %s: %r\n", argv[i]);
123
				err = "open";
124
			}else{
125
				err = show(fd, argv[i]);
126
				close(fd);
127
			}
128
			if(output && argc>1 && err==nil){
129
				fprint(2, "gif: exiting after one file\n");
130
				break;
131
			}
132
		}
133
	}
134
	exits(err);
135
}
136
 
137
Image*
138
transparency(Rawimage *r, char *name)
139
{
140
	Image *i;
141
	int j, index;
142
	uchar *pic, *mpic, *mask;
143
 
144
	if((r->gifflags&TRANSP) == 0)
145
		return nil;
146
	i = allocimage(display, r->r, GREY8, 0, 0);
147
	if(i == nil){
148
		fprint(2, "gif: allocimage for mask of %s failed: %r\n", name);
149
		return nil;
150
	}
151
	pic = r->chans[0];
152
	mask = malloc(r->chanlen);
153
	if(mask == nil){
154
		fprint(2, "gif: malloc for mask of %s failed: %r\n", name);
155
		freeimage(i);
156
		return nil;
157
	}
158
	index = r->giftrindex;
159
	mpic = mask;
160
	for(j=0; j<r->chanlen; j++)
161
		if(*pic++ == index)
162
			*mpic++ = 0;
163
		else
164
			*mpic++ = 0xFF;
165
	if(loadimage(i, i->r, mask, r->chanlen) < 0){
166
		fprint(2, "gif: loadimage for mask of %s failed: %r\n", name);
167
		free(mask);
168
		freeimage(i);
169
		return nil;
170
	}
171
	free(mask);
172
	return i;
173
}
174
 
175
/* interleave alpha values of 0xFF in data stream. alpha value comes first, then b g r */
176
uchar*
177
expand(uchar *u, int chanlen, int nchan)
178
{
179
	int j, k;
180
	uchar *v, *up, *vp;
181
 
182
	v = malloc(chanlen*(nchan+1));
183
	if(v == nil){
184
		fprint(2, "gif: malloc fails: %r\n");
185
		exits("malloc");
186
	}
187
	up = u;
188
	vp = v;
189
	for(j=0; j<chanlen; j++){
190
		*vp++ = 0xFF;
191
		for(k=0; k<nchan; k++)
192
			*vp++ = *up++;
193
	}
194
	return v;
195
}
196
 
197
void
198
addalpha(Rawimage *i)
199
{
200
	char buf[32];
201
 
202
	switch(outchan){
203
	case CMAP8:
204
		i->chans[0] = expand(i->chans[0], i->chanlen/1, 1);
205
		i->chanlen = 2*(i->chanlen/1);
206
		i->chandesc = CRGBVA16;
207
		outchan = CHAN2(CMap, 8, CAlpha, 8);
208
		break;
209
 
210
	case GREY8:
211
		i->chans[0] = expand(i->chans[0], i->chanlen/1, 1);
212
		i->chanlen = 2*(i->chanlen/1);
213
		i->chandesc = CYA16;
214
		outchan = CHAN2(CGrey, 8, CAlpha, 8);
215
		break;
216
 
217
	case RGB24:
218
		i->chans[0] = expand(i->chans[0], i->chanlen/3, 3);
219
		i->chanlen = 4*(i->chanlen/3);
220
		i->chandesc = CRGBA32;
221
		outchan = RGBA32;
222
		break;
223
 
224
	default:
225
		chantostr(buf, outchan);
226
		fprint(2, "gif: can't add alpha to type %s\n", buf);
227
		exits("err");
228
	}
229
}
230
 
231
/*
232
 * Called only when writing output.  If the output is RGBA32,
233
 * we must write four bytes per pixel instead of two.
234
 * There's always at least two: data plus alpha.
235
 * r is used only for reference; the image is already in c.
236
 */
237
void
238
blackout(Rawimage *r, Rawimage *c)
239
{
240
	int i, trindex;
241
	uchar *rp, *cp;
242
 
243
	rp = r->chans[0];
244
	cp = c->chans[0];
245
	trindex = r->giftrindex;
246
	if(outchan == RGBA32)
247
		for(i=0; i<r->chanlen; i++){
248
			if(*rp == trindex){
249
				*cp++ = 0x00;
250
				*cp++ = 0x00;
251
				*cp++ = 0x00;
252
				*cp++ = 0x00;
253
			}else{
254
				*cp++ = 0xFF;
255
				cp += 3;
256
			}
257
			rp++;
258
		}
259
	else
260
		for(i=0; i<r->chanlen; i++){
261
			if(*rp == trindex){
262
				*cp++ = 0x00;
263
				*cp++ = 0x00;
264
			}else{
265
				*cp++ = 0xFF;
266
				cp++;
267
			}
268
			rp++;
269
		}
270
}
271
 
272
int
273
init(void)
274
{
275
	static int inited;
276
 
277
	if(inited == 0){
278
		if(initdraw(0, 0, 0) < 0){
279
			fprint(2, "gif: initdraw failed: %r\n");
280
			return -1;
281
		}
282
		einit(Ekeyboard|Emouse);
283
		inited++;
284
	}
285
	return 1;
286
}
287
 
288
char*
289
show(int fd, char *name)
290
{
291
	Rawimage **images, **rgbv;
292
	Image **ims, **masks;
293
	int j, k, n, ch, nloop, loopcount, dt;
294
	char *err;
295
	char buf[32];
296
 
297
	err = nil;
298
	images = readgif(fd, CRGB);
299
	if(images == nil){
300
		fprint(2, "gif: decode %s failed: %r\n", name);
301
		return "decode";
302
	}
303
	for(n=0; images[n]; n++)
304
		;
305
	ims = malloc((n+1)*sizeof(Image*));
306
	masks = malloc((n+1)*sizeof(Image*));
307
	rgbv = malloc((n+1)*sizeof(Rawimage*));
308
	if(masks==nil || rgbv==nil || ims==nil){
309
		fprint(2, "gif: malloc of masks for %s failed: %r\n", name);
310
		err = "malloc";
311
		goto Return;
312
	}
313
	memset(masks, 0, (n+1)*sizeof(Image*));
314
	memset(ims, 0, (n+1)*sizeof(Image*));
315
	memset(rgbv, 0, (n+1)*sizeof(Rawimage*));
316
	if(!dflag){
317
		if(init() < 0){
318
			err = "initdraw";
319
			goto Return;
320
		}
321
		if(defaultcolor && screen->depth>8)
322
			outchan = RGB24;
323
	}
324
 
325
	for(k=0; k<n; k++){
326
		if(outchan == CMAP8)
327
			rgbv[k] = torgbv(images[k], !eflag);
328
		else{
329
			if(outchan==GREY8 || (images[k]->chandesc==CY && threeflag==0))
330
				rgbv[k] = totruecolor(images[k], CY);
331
			else
332
				rgbv[k] = totruecolor(images[k], CRGB24);
333
		}
334
		if(rgbv[k] == nil){
335
			fprint(2, "gif: converting %s to local format failed: %r\n", name);
336
			err = "torgbv";
337
			goto Return;
338
		}
339
		if(!dflag){
340
			masks[k] = transparency(images[k], name);
341
			if(rgbv[k]->chandesc == CY)
342
				ims[k] = allocimage(display, rgbv[k]->r, GREY8, 0, 0);
343
			else
344
				ims[k] = allocimage(display, rgbv[k]->r, outchan, 0, 0);
345
			if(ims[k] == nil){
346
				fprint(2, "gif: allocimage %s failed: %r\n", name);
347
				err = "allocimage";
348
				goto Return;
349
			}
350
			if(loadimage(ims[k], ims[k]->r, rgbv[k]->chans[0], rgbv[k]->chanlen) < 0){
351
				fprint(2, "gif: loadimage %s failed: %r\n", name);
352
				err = "loadimage";
353
				goto Return;
354
			}
355
		}
356
	}
357
 
358
	allimages = images;
359
	allims = ims;
360
	allmasks = masks;
361
	loopcount = images[0]->gifloopcount;
362
	if(!dflag){
363
		nloop = 0;
364
		do{
365
			for(k=0; k<n; k++){
366
				which = k;
367
				eresized(0);
368
				dt = images[k]->gifdelay*10;
369
				if(dt < 50)
370
					dt = 50;
371
				while(n==1 || ecankbd()){
372
					if((ch=ekbd())=='q' || ch==0x7F || ch==0x04)	/* an odd, democratic list */
373
						exits(nil);
374
					if(ch == '\n')
375
						goto Out;
376
				}sleep(dt);
377
			}
378
			/* loopcount -1 means no loop (this code's rule), loopcount 0 means loop forever (netscape's rule)*/
379
		}while(loopcount==0 || ++nloop<loopcount);
380
		/* loop count has run out */
381
		ekbd();
382
    Out:
383
		drawop(screen, screen->clipr, display->white, nil, ZP, S);
384
	}
385
	if(n>1 && output)
386
		fprint(2, "gif: warning: only writing first image in %d-image GIF %s\n", n, name);
387
	if(nineflag){
388
		if(images[0]->gifflags&TRANSP){
389
			addalpha(rgbv[0]);
390
			blackout(images[0], rgbv[0]);
391
		}
392
		chantostr(buf, outchan);
393
		print("%11s %11d %11d %11d %11d ", buf,
394
			rgbv[0]->r.min.x, rgbv[0]->r.min.y, rgbv[0]->r.max.x, rgbv[0]->r.max.y);
395
		if(write(1, rgbv[0]->chans[0], rgbv[0]->chanlen) != rgbv[0]->chanlen){
396
			fprint(2, "gif: %s: write error %r\n", name);
397
			return "write";
398
		}
399
	}else if(cflag){
400
		if(images[0]->gifflags&TRANSP){
401
			addalpha(rgbv[0]);
402
			blackout(images[0], rgbv[0]);
403
		}
404
		if(writerawimage(1, rgbv[0]) < 0){
405
			fprint(2, "gif: %s: write error: %r\n", name);
406
			return "write";
407
		}
408
	}
409
 
410
    Return:
411
	allims = nil;
412
	allmasks = nil;
413
	allimages = nil;
414
	for(k=0; images[k]; k++){
415
		for(j=0; j<images[k]->nchans; j++)
416
			free(images[k]->chans[j]);
417
		free(images[k]->cmap);
418
		if(rgbv[k])
419
			free(rgbv[k]->chans[0]);
420
		freeimage(ims[k]);
421
		freeimage(masks[k]);
422
		free(images[k]);
423
		free(rgbv[k]);
424
	}
425
	free(images);
426
	free(masks);
427
	free(ims);
428
	return err;
429
}