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