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