Blob


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