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