]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/jpg/readgif.c
merge
[plan9front.git] / sys / src / cmd / jpg / readgif.c
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <draw.h>
5 #include "imagefile.h"
6
7 typedef struct Entry Entry;
8 typedef struct Header Header;
9
10 struct Entry{
11         int             prefix;
12         int             exten;
13 };
14
15 struct Header{
16         Biobuf  *fd;
17         char            err[256];
18         jmp_buf errlab;
19         uchar   buf[3*256];
20         char            vers[8];
21         uchar   *globalcmap;
22         int             screenw;
23         int             screenh;
24         int             fields;
25         int             bgrnd;
26         int             aspect;
27         int             flags;
28         int             delay;
29         int             trindex;
30         int             loopcount;
31         Entry   tbl[4096];
32         Rawimage        **array;
33         Rawimage        *new;
34
35         uchar   *pic;
36 };
37
38 static char             readerr[] = "ReadGIF: read error: %r";
39 static char             extreaderr[] = "ReadGIF: can't read extension: %r";
40 static char             memerr[] = "ReadGIF: malloc failed: %r";
41
42 static Rawimage**       readarray(Header*);
43 static Rawimage*        readone(Header*);
44 static void                     readheader(Header*);
45 static void                     skipextension(Header*);
46 static uchar*           readcmap(Header*, int);
47 static uchar*           decode(Header*, Rawimage*, Entry*);
48 static void             interlace(Header*, Rawimage*);
49
50 static
51 void
52 clear(void **p)
53 {
54         if(*p){
55                 free(*p);
56                 *p = nil;
57         }
58 }
59
60 static
61 void
62 giffreeall(Header *h, int freeimage)
63 {
64         int i;
65
66         if(h->fd){
67                 Bterm(h->fd);
68                 h->fd = nil;
69         }
70         clear(&h->pic);
71         if(h->new){
72                 clear(&h->new->cmap);
73                 clear(&h->new->chans[0]);
74                 clear(&h->new);
75         }
76         clear(&h->globalcmap);
77         if(freeimage && h->array!=nil){
78                 for(i=0; h->array[i]; i++){
79                         clear(&h->array[i]->cmap);
80                         clear(&h->array[i]->chans[0]);
81                 }
82                 clear(&h->array);
83         }
84 }
85
86 static
87 void
88 giferror(Header *h, char *fmt, ...)
89 {
90         va_list arg;
91
92         va_start(arg, fmt);
93         vseprint(h->err, h->err+sizeof h->err, fmt, arg);
94         va_end(arg);
95
96         werrstr("%s", h->err);
97         giffreeall(h, 1);
98         longjmp(h->errlab, 1);
99 }
100
101
102 Rawimage**
103 readgif(int fd, int colorspace)
104 {
105         Rawimage **a;
106         Biobuf b;
107         Header *h;
108         char buf[ERRMAX];
109
110         buf[0] = '\0';
111         USED(colorspace);
112         if(Binit(&b, fd, OREAD) < 0)
113                 return nil;
114         h = malloc(sizeof(Header));
115         if(h == nil){
116                 Bterm(&b);
117                 return nil;
118         }
119         memset(h, 0, sizeof(Header));
120         h->fd = &b;
121         errstr(buf, sizeof buf);        /* throw it away */
122         if(setjmp(h->errlab))
123                 a = nil;
124         else
125                 a = readarray(h);
126         giffreeall(h, 0);
127         free(h);
128         return a;
129 }
130
131 static
132 void
133 inittbl(Header *h)
134 {
135         int i;
136         Entry *tbl;
137
138         tbl = h->tbl;
139         for(i=0; i<258; i++) {
140                 tbl[i].prefix = -1;
141                 tbl[i].exten = i;
142         }
143 }
144
145 static
146 Rawimage**
147 readarray(Header *h)
148 {
149         Entry *tbl;
150         Rawimage *new, **array;
151         int c, nimages;
152
153         tbl = h->tbl;
154
155         readheader(h);
156
157         if(h->fields & 0x80)
158                 h->globalcmap = readcmap(h, (h->fields&7)+1);
159         array = malloc(sizeof(Rawimage*));
160         if(array == nil)
161                 giferror(h, memerr);
162         nimages = 0;
163         array[0] = nil;
164         h->array = array;
165                 
166         for(;;){
167                 switch(c = Bgetc(h->fd)){
168                 case Beof:
169                         goto Return;
170
171                 case 0x21:      /* Extension (ignored) */
172                         skipextension(h);
173                         break;
174
175                 case 0x2C:      /* Image Descriptor */
176                         inittbl(h);
177                         new = readone(h);
178                         if(new->fields & 0x80){
179                                 new->cmaplen = 3*(1<<((new->fields&7)+1));
180                                 new->cmap = readcmap(h, (new->fields&7)+1);
181                         }else{
182                                 if(h->globalcmap == nil)
183                                         giferror(h, "ReadGIF: globalcmap missing");
184                                 new->cmaplen = 3*(1<<((h->fields&7)+1));
185                                 new->cmap = malloc(new->cmaplen);
186                                 if(new->cmap == nil)
187                                         giferror(h, memerr);
188                                 memmove(new->cmap, h->globalcmap, new->cmaplen);
189                         }
190                         h->new = new;
191                         new->chans[0] = decode(h, new, tbl);
192                         if(new->fields & 0x40)
193                                 interlace(h, new);
194                         new->gifflags = h->flags;
195                         new->gifdelay = h->delay;
196                         new->giftrindex = h->trindex;
197                         new->gifloopcount = h->loopcount;
198                         array = realloc(h->array, (nimages+2)*sizeof(Rawimage*));
199                         if(array == nil)
200                                 giferror(h, memerr);
201                         array[nimages++] = new;
202                         array[nimages] = nil;
203                         h->array = array;
204                         h->new = nil;
205                         break;
206
207                 case 0x3B:      /* Trailer */
208                         goto Return;
209
210                 default:
211                         fprint(2, "ReadGIF: unknown block type: 0x%.2x\n", c);
212                         goto Return;
213                 }
214         }
215
216    Return:
217         if(array[0]==nil || array[0]->chans[0] == nil)
218                 giferror(h, "ReadGIF: no picture in file");
219
220         return array;
221 }
222
223 static
224 void
225 readheader(Header *h)
226 {
227         if(Bread(h->fd, h->buf, 13) != 13)
228                 giferror(h, "ReadGIF: can't read header: %r");
229         memmove(h->vers, h->buf, 6);
230         if(strcmp(h->vers, "GIF87a")!=0 &&  strcmp(h->vers, "GIF89a")!=0)
231                 giferror(h, "ReadGIF: can't recognize format %s", h->vers);
232         h->screenw = h->buf[6]+(h->buf[7]<<8);
233         h->screenh = h->buf[8]+(h->buf[9]<<8);
234         h->fields = h->buf[10];
235         h->bgrnd = h->buf[11];
236         h->aspect = h->buf[12];
237         h->flags = 0;
238         h->delay = 0;
239         h->trindex = 0;
240         h->loopcount = -1;
241 }
242
243 static
244 uchar*
245 readcmap(Header *h, int size)
246 {
247         uchar *map;
248
249         if(size > 8)
250                 giferror(h, "ReadGIF: can't handles %d bits per pixel", size);
251         size = 3*(1<<size);
252         if(Bread(h->fd, h->buf, size) != size)
253                 giferror(h, "ReadGIF: short read on color map");
254         map = malloc(size);
255         if(map == nil)
256                 giferror(h, memerr);
257         memmove(map, h->buf, size);
258         return map;
259 }
260
261 static
262 Rawimage*
263 readone(Header *h)
264 {
265         Rawimage *i;
266         int left, top, width, height;
267
268         if(Bread(h->fd, h->buf, 9) != 9)
269                 giferror(h, "ReadGIF: can't read image descriptor: %r");
270         i = malloc(sizeof(Rawimage));
271         if(i == nil)
272                 giferror(h, memerr);
273         left = h->buf[0]+(h->buf[1]<<8);
274         top = h->buf[2]+(h->buf[3]<<8);
275         width = h->buf[4]+(h->buf[5]<<8);
276         height = h->buf[6]+(h->buf[7]<<8);
277         i->fields = h->buf[8];
278         i->r.min.x = left;
279         i->r.min.y = top;
280         i->r.max.x = left+width;
281         i->r.max.y = top+height;
282         i->nchans = 1;
283         i->chandesc = CRGB1;
284         memset(i->chans, 0, sizeof(i->chans));
285         return i;
286 }
287
288
289 static
290 int
291 readdata(Header *h, uchar *data)
292 {
293         int nbytes, n;
294
295         nbytes = Bgetc(h->fd);
296         if(nbytes < 0)
297                 giferror(h, "ReadGIF: can't read data: %r");
298         if(nbytes == 0)
299                 return 0;
300         n = Bread(h->fd, data, nbytes);
301         if(n < 0)
302                 giferror(h, "ReadGIF: can't read data: %r");
303         if(n != nbytes)
304                 fprint(2, "ReadGIF: short data subblock\n");
305         return n;
306 }
307
308 static
309 void
310 graphiccontrol(Header *h)
311 {
312         if(Bread(h->fd, h->buf, 5+1) != 5+1)
313                 giferror(h, readerr);
314         h->flags = h->buf[1];
315         h->delay = h->buf[2]+(h->buf[3]<<8);
316         h->trindex = h->buf[4];
317 }
318
319 static
320 void
321 skipextension(Header *h)
322 {
323         int type, hsize, hasdata, n;
324         uchar data[256];
325
326         hsize = 0;
327         hasdata = 0;
328
329         type = Bgetc(h->fd);
330         switch(type){
331         case Beof:
332                 giferror(h, extreaderr);
333                 break;
334         case 0x01:      /* Plain Text Extension */
335                 hsize = 13;
336                 hasdata = 1;
337                 break;
338         case 0xF9:      /* Graphic Control Extension */
339                 graphiccontrol(h);
340                 return;
341         case 0xFE:      /* Comment Extension */
342                 hasdata = 1;
343                 break;
344         case 0xFF:      /* Application Extension */
345                 hsize = Bgetc(h->fd);
346                 /* standard says this must be 11, but Adobe likes to put out 10-byte ones,
347                  * so we pay attention to the field. */
348                 hasdata = 1;
349                 break;
350         default:
351                 giferror(h, "ReadGIF: unknown extension");
352         }
353         if(hsize>0 && Bread(h->fd, h->buf, hsize) != hsize)
354                 giferror(h, extreaderr);
355         if(!hasdata){
356                 /*
357                  * This code used to check h->buf[hsize-1] != 0
358                  * and giferror if so, but if !hasdata, hsize == 0.
359                  */
360                 return;
361         }
362
363         /* loop counter: Application Extension with NETSCAPE2.0 as string and 1 <loop.count> in data */
364         if(type == 0xFF && hsize==11 && memcmp(h->buf, "NETSCAPE2.0", 11)==0){
365                 n = readdata(h, data);
366                 if(n == 0)
367                         return;
368                 if(n==3 && data[0]==1)
369                         h->loopcount = data[1] | (data[2]<<8);
370         }
371         while(readdata(h, data) != 0)
372                 ;
373 }
374
375 static
376 uchar*
377 decode(Header *h, Rawimage *i, Entry *tbl)
378 {
379         int c, doclip, incode, codesize, CTM, EOD, pici, datai, stacki, nbits, sreg, fc, code, piclen;
380         int csize, nentry, maxentry, first, ocode, ndata, nb;
381         uchar clip, *p, *pic;
382         uchar stack[4096], data[256];
383
384         if(Bread(h->fd, h->buf, 1) != 1)
385                 giferror(h, "ReadGIF: can't read data: %r");
386         codesize = h->buf[0];
387         if(codesize>8 || 0>codesize)
388                 giferror(h, "ReadGIF: can't handle codesize %d", codesize);
389         doclip = 0;
390         if(i->cmap!=nil && i->cmaplen!=3*(1<<codesize)
391           && (codesize!=2 || i->cmaplen!=3*2))                  /* peculiar GIF bitmap files... */
392                 doclip = 1;
393
394         CTM =1<<codesize;
395         EOD = CTM+1;
396
397         piclen = (i->r.max.x-i->r.min.x)*(i->r.max.y-i->r.min.y);
398         i->chanlen = piclen;
399         pic = malloc(piclen);
400         if(pic == nil)
401                 giferror(h, memerr);
402         h->pic = pic;
403         pici = 0;
404         ndata = 0;
405         datai = 0;
406         nbits = 0;
407         sreg = 0;
408         fc = 0;
409
410     Loop:
411         for(;;){
412                 csize = codesize+1;
413                 nentry = EOD+1;
414                 maxentry = (1<<csize)-1;
415                 first = 1;
416                 ocode = -1;
417
418                 for(;; ocode = incode) {
419                         while(nbits < csize) {
420                                 if(datai == ndata){
421                                         ndata = readdata(h, data);
422                                         if(ndata == 0)
423                                                 goto Return;
424                                         datai = 0;
425                                 }
426                                 c = data[datai++];
427                                 sreg |= c<<nbits;
428                                 nbits += 8;
429                         }
430                         code = sreg & ((1<<csize) - 1);
431                         sreg >>= csize;
432                         nbits -= csize;
433
434                         if(code == EOD){
435                                 ndata = readdata(h, data);
436                                 if(ndata != 0)
437                                         fprint(2, "ReadGIF: unexpected data past EOD\n");
438                                 goto Return;
439                         }
440
441                         if(code == CTM)
442                                 goto Loop;
443
444                         stacki = (sizeof stack)-1;
445
446                         incode = code;
447
448                         /* special case for KwKwK */
449                         if(code == nentry) {
450                                 stack[stacki--] = fc;
451                                 code = ocode;
452                         }
453
454                         if(code > nentry){
455                                 fprint(2, "ReadGIF: GIF invalid, code out of range, %x > %x\n", code, nentry);
456                                 code = nentry;
457                         }
458                         for(c=code; stacki>0 && c>=0; c=tbl[c].prefix)
459                                 stack[stacki--] = tbl[c].exten;
460
461                         nb = (sizeof stack)-(stacki+1);
462                         if(pici+nb > piclen){
463                                 /* this common error is harmless
464                                  * we have to keep reading to keep the blocks in sync */
465                                 ;
466                         }else{
467                                 memmove(pic+pici, stack+stacki+1, sizeof stack - (stacki+1));
468                                 pici += nb;
469                         }
470
471                         fc = stack[stacki+1];
472
473                         if(first){
474                                 first = 0;
475                                 continue;
476                         }
477                         #define early 0 /* peculiar tiff feature here for reference */
478                         if(nentry == maxentry-early) {
479                                 if(csize >= 12)
480                                         continue;
481                                 csize++;
482                                 maxentry = (1<<csize);
483                                 if(csize < 12)
484                                         maxentry--;
485                         }
486                         tbl[nentry].prefix = ocode;
487                         tbl[nentry].exten = fc;
488                         nentry++;
489                 }
490         }
491
492 Return:
493         if(doclip){
494                 clip = i->cmaplen/3;
495                 for(p = pic; p < pic+piclen; p++)
496                         if(*p >= clip)
497                                 *p = clip;
498         }
499         h->pic = nil;
500         return pic;
501 }
502
503 static
504 void
505 interlace(Header *h, Rawimage *image)
506 {
507         uchar *pic;
508         Rectangle r;
509         int dx, yy, y;
510         uchar *ipic;
511
512         pic = image->chans[0];
513         r = image->r;
514         dx = r.max.x-r.min.x;
515         ipic = malloc(dx*(r.max.y-r.min.y));
516         if(ipic == nil)
517                 giferror(h, nil);
518
519         /* Group 1: every 8th row, starting with row 0 */
520         yy = 0;
521         for(y=r.min.y; y<r.max.y; y+=8){
522                 memmove(&ipic[(y-r.min.y)*dx], &pic[yy*dx], dx);
523                 yy++;
524         }
525
526         /* Group 2: every 8th row, starting with row 4 */
527         for(y=r.min.y+4; y<r.max.y; y+=8){
528                 memmove(&ipic[(y-r.min.y)*dx], &pic[yy*dx], dx);
529                 yy++;
530         }
531
532         /* Group 3: every 4th row, starting with row 2 */
533         for(y=r.min.y+2; y<r.max.y; y+=4){
534                 memmove(&ipic[(y-r.min.y)*dx], &pic[yy*dx], dx);
535                 yy++;
536         }
537
538         /* Group 4: every 2nd row, starting with row 1 */
539         for(y=r.min.y+1; y<r.max.y; y+=2){
540                 memmove(&ipic[(y-r.min.y)*dx], &pic[yy*dx], dx);
541                 yy++;
542         }
543
544         free(image->chans[0]);
545         image->chans[0] = ipic;
546 }