]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/jpg/readgif.c
kernel: keep segment locked for data2txt
[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(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
160         array = malloc(sizeof(Rawimage**));
161         if(array == nil)
162                 giferror(h, memerr);
163         nimages = 0;
164         array[0] = nil;
165         h->array = array;
166                 
167         for(;;){
168                 switch(c = Bgetc(h->fd)){
169                 case Beof:
170                         goto Return;
171
172                 case 0x21:      /* Extension (ignored) */
173                         skipextension(h);
174                         break;
175
176                 case 0x2C:      /* Image Descriptor */
177                         inittbl(h);
178                         new = readone(h);
179                         if(new->fields & 0x80){
180                                 new->cmaplen = 3*(1<<((new->fields&7)+1));
181                                 new->cmap = readcmap(h, (new->fields&7)+1);
182                         }else{
183                                 new->cmaplen = 3*(1<<((h->fields&7)+1));
184                                 new->cmap = malloc(new->cmaplen);
185                                 memmove(new->cmap, h->globalcmap, new->cmaplen);
186                         }
187                         h->new = new;
188                         new->chans[0] = decode(h, new, tbl);
189                         if(new->fields & 0x40)
190                                 interlace(h, new);
191                         new->gifflags = h->flags;
192                         new->gifdelay = h->delay;
193                         new->giftrindex = h->trindex;
194                         new->gifloopcount = h->loopcount;
195                         array = realloc(h->array, (nimages+2)*sizeof(Rawimage*));
196                         if(array == nil)
197                                 giferror(h, memerr);
198                         array[nimages++] = new;
199                         array[nimages] = nil;
200                         h->array = array;
201                         h->new = nil;
202                         break;
203
204                 case 0x3B:      /* Trailer */
205                         goto Return;
206
207                 default:
208                         fprint(2, "ReadGIF: unknown block type: 0x%.2x\n", c);
209                         goto Return;
210                 }
211         }
212
213    Return:
214         if(array[0]==nil || array[0]->chans[0] == nil)
215                 giferror(h, "ReadGIF: no picture in file");
216
217         return array;
218 }
219
220 static
221 void
222 readheader(Header *h)
223 {
224         if(Breadn(h->fd, h->buf, 13) != 13)
225                 giferror(h, "ReadGIF: can't read header: %r");
226         memmove(h->vers, h->buf, 6);
227         if(strcmp(h->vers, "GIF87a")!=0 &&  strcmp(h->vers, "GIF89a")!=0)
228                 giferror(h, "ReadGIF: can't recognize format %s", h->vers);
229         h->screenw = h->buf[6]+(h->buf[7]<<8);
230         h->screenh = h->buf[8]+(h->buf[9]<<8);
231         h->fields = h->buf[10];
232         h->bgrnd = h->buf[11];
233         h->aspect = h->buf[12];
234         h->flags = 0;
235         h->delay = 0;
236         h->trindex = 0;
237         h->loopcount = -1;
238 }
239
240 static
241 uchar*
242 readcmap(Header *h, int size)
243 {
244         uchar *map;
245
246         if(size > 8)
247                 giferror(h, "ReadGIF: can't handles %d bits per pixel", size);
248         size = 3*(1<<size);
249         if(Breadn(h->fd, h->buf, size) != size)
250                 giferror(h, "ReadGIF: short read on color map");
251         map = malloc(size);
252         if(map == nil)
253                 giferror(h, memerr);
254         memmove(map, h->buf, size);
255         return map;
256 }
257
258 static
259 Rawimage*
260 readone(Header *h)
261 {
262         Rawimage *i;
263         int left, top, width, height;
264
265         if(Breadn(h->fd, h->buf, 9) != 9)
266                 giferror(h, "ReadGIF: can't read image descriptor: %r");
267         i = malloc(sizeof(Rawimage));
268         if(i == nil)
269                 giferror(h, memerr);
270         left = h->buf[0]+(h->buf[1]<<8);
271         top = h->buf[2]+(h->buf[3]<<8);
272         width = h->buf[4]+(h->buf[5]<<8);
273         height = h->buf[6]+(h->buf[7]<<8);
274         i->fields = h->buf[8];
275         i->r.min.x = left;
276         i->r.min.y = top;
277         i->r.max.x = left+width;
278         i->r.max.y = top+height;
279         i->nchans = 1;
280         i->chandesc = CRGB1;
281         memset(i->chans, 0, sizeof(i->chans));
282         return i;
283 }
284
285
286 static
287 int
288 readdata(Header *h, uchar *data)
289 {
290         int nbytes, n;
291
292         nbytes = Bgetc(h->fd);
293         if(nbytes < 0)
294                 giferror(h, "ReadGIF: can't read data: %r");
295         if(nbytes == 0)
296                 return 0;
297         n = Breadn(h->fd, data, nbytes);
298         if(n < 0)
299                 giferror(h, "ReadGIF: can't read data: %r");
300         if(n != nbytes)
301                 fprint(2, "ReadGIF: short data subblock\n");
302         return n;
303 }
304
305 static
306 void
307 graphiccontrol(Header *h)
308 {
309         if(Breadn(h->fd, h->buf, 5+1) != 5+1)
310                 giferror(h, readerr);
311         h->flags = h->buf[1];
312         h->delay = h->buf[2]+(h->buf[3]<<8);
313         h->trindex = h->buf[4];
314 }
315
316 static
317 void
318 skipextension(Header *h)
319 {
320         int type, hsize, hasdata, n;
321         uchar data[256];
322
323         hsize = 0;
324         hasdata = 0;
325
326         type = Bgetc(h->fd);
327         switch(type){
328         case Beof:
329                 giferror(h, extreaderr);
330                 break;
331         case 0x01:      /* Plain Text Extension */
332                 hsize = 13;
333                 hasdata = 1;
334                 break;
335         case 0xF9:      /* Graphic Control Extension */
336                 graphiccontrol(h);
337                 return;
338         case 0xFE:      /* Comment Extension */
339                 hasdata = 1;
340                 break;
341         case 0xFF:      /* Application Extension */
342                 hsize = Bgetc(h->fd);
343                 /* standard says this must be 11, but Adobe likes to put out 10-byte ones,
344                  * so we pay attention to the field. */
345                 hasdata = 1;
346                 break;
347         default:
348                 giferror(h, "ReadGIF: unknown extension");
349         }
350         if(hsize>0 && Breadn(h->fd, h->buf, hsize) != hsize)
351                 giferror(h, extreaderr);
352         if(!hasdata){
353                 /*
354                  * This code used to check h->buf[hsize-1] != 0
355                  * and giferror if so, but if !hasdata, hsize == 0.
356                  */
357                 return;
358         }
359
360         /* loop counter: Application Extension with NETSCAPE2.0 as string and 1 <loop.count> in data */
361         if(type == 0xFF && hsize==11 && memcmp(h->buf, "NETSCAPE2.0", 11)==0){
362                 n = readdata(h, data);
363                 if(n == 0)
364                         return;
365                 if(n==3 && data[0]==1)
366                         h->loopcount = data[1] | (data[2]<<8);
367         }
368         while(readdata(h, data) != 0)
369                 ;
370 }
371
372 static
373 uchar*
374 decode(Header *h, Rawimage *i, Entry *tbl)
375 {
376         int c, doclip, incode, codesize, CTM, EOD, pici, datai, stacki, nbits, sreg, fc, code, piclen;
377         int csize, nentry, maxentry, first, ocode, ndata, nb;
378         uchar clip, *p, *pic;
379         uchar stack[4096], data[256];
380
381         if(Bread(h->fd, h->buf, 1) != 1)
382                 giferror(h, "ReadGIF: can't read data: %r");
383         codesize = h->buf[0];
384         if(codesize>8 || 0>codesize)
385                 giferror(h, "ReadGIF: can't handle codesize %d", codesize);
386         doclip = 0;
387         if(i->cmap!=nil && i->cmaplen!=3*(1<<codesize)
388           && (codesize!=2 || i->cmaplen!=3*2))                  /* peculiar GIF bitmap files... */
389                 doclip = 1;
390
391         CTM =1<<codesize;
392         EOD = CTM+1;
393
394         piclen = (i->r.max.x-i->r.min.x)*(i->r.max.y-i->r.min.y);
395         i->chanlen = piclen;
396         pic = malloc(piclen);
397         if(pic == nil)
398                 giferror(h, memerr);
399         h->pic = pic;
400         pici = 0;
401         ndata = 0;
402         datai = 0;
403         nbits = 0;
404         sreg = 0;
405         fc = 0;
406
407     Loop:
408         for(;;){
409                 csize = codesize+1;
410                 nentry = EOD+1;
411                 maxentry = (1<<csize)-1;
412                 first = 1;
413                 ocode = -1;
414
415                 for(;; ocode = incode) {
416                         while(nbits < csize) {
417                                 if(datai == ndata){
418                                         ndata = readdata(h, data);
419                                         if(ndata == 0)
420                                                 goto Return;
421                                         datai = 0;
422                                 }
423                                 c = data[datai++];
424                                 sreg |= c<<nbits;
425                                 nbits += 8;
426                         }
427                         code = sreg & ((1<<csize) - 1);
428                         sreg >>= csize;
429                         nbits -= csize;
430
431                         if(code == EOD){
432                                 ndata = readdata(h, data);
433                                 if(ndata != 0)
434                                         fprint(2, "ReadGIF: unexpected data past EOD\n");
435                                 goto Return;
436                         }
437
438                         if(code == CTM)
439                                 goto Loop;
440
441                         stacki = (sizeof stack)-1;
442
443                         incode = code;
444
445                         /* special case for KwKwK */
446                         if(code == nentry) {
447                                 stack[stacki--] = fc;
448                                 code = ocode;
449                         }
450
451                         if(code > nentry){
452                                 fprint(2, "ReadGIF: GIF invalid, code out of range, %x > %x\n", code, nentry);
453                                 code = nentry;
454                         }
455                         for(c=code; stacki>0 && c>=0; c=tbl[c].prefix)
456                                 stack[stacki--] = tbl[c].exten;
457
458                         nb = (sizeof stack)-(stacki+1);
459                         if(pici+nb > piclen){
460                                 /* this common error is harmless
461                                  * we have to keep reading to keep the blocks in sync */
462                                 ;
463                         }else{
464                                 memmove(pic+pici, stack+stacki+1, sizeof stack - (stacki+1));
465                                 pici += nb;
466                         }
467
468                         fc = stack[stacki+1];
469
470                         if(first){
471                                 first = 0;
472                                 continue;
473                         }
474                         #define early 0 /* peculiar tiff feature here for reference */
475                         if(nentry == maxentry-early) {
476                                 if(csize >= 12)
477                                         continue;
478                                 csize++;
479                                 maxentry = (1<<csize);
480                                 if(csize < 12)
481                                         maxentry--;
482                         }
483                         tbl[nentry].prefix = ocode;
484                         tbl[nentry].exten = fc;
485                         nentry++;
486                 }
487         }
488
489 Return:
490         if(doclip){
491                 clip = i->cmaplen/3;
492                 for(p = pic; p < pic+piclen; p++)
493                         if(*p >= clip)
494                                 *p = clip;
495         }
496         h->pic = nil;
497         return pic;
498 }
499
500 static
501 void
502 interlace(Header *h, Rawimage *image)
503 {
504         uchar *pic;
505         Rectangle r;
506         int dx, yy, y;
507         uchar *ipic;
508
509         pic = image->chans[0];
510         r = image->r;
511         dx = r.max.x-r.min.x;
512         ipic = malloc(dx*(r.max.y-r.min.y));
513         if(ipic == nil)
514                 giferror(h, nil);
515
516         /* Group 1: every 8th row, starting with row 0 */
517         yy = 0;
518         for(y=r.min.y; y<r.max.y; y+=8){
519                 memmove(&ipic[(y-r.min.y)*dx], &pic[yy*dx], dx);
520                 yy++;
521         }
522
523         /* Group 2: every 8th row, starting with row 4 */
524         for(y=r.min.y+4; y<r.max.y; y+=8){
525                 memmove(&ipic[(y-r.min.y)*dx], &pic[yy*dx], dx);
526                 yy++;
527         }
528
529         /* Group 3: every 4th row, starting with row 2 */
530         for(y=r.min.y+2; y<r.max.y; y+=4){
531                 memmove(&ipic[(y-r.min.y)*dx], &pic[yy*dx], dx);
532                 yy++;
533         }
534
535         /* Group 4: every 2nd row, starting with row 1 */
536         for(y=r.min.y+1; y<r.max.y; y+=2){
537                 memmove(&ipic[(y-r.min.y)*dx], &pic[yy*dx], dx);
538                 yy++;
539         }
540
541         free(image->chans[0]);
542         image->chans[0] = ipic;
543 }