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