]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/jpg/readpng.c
cwfs: back to previous version
[plan9front.git] / sys / src / cmd / jpg / readpng.c
1 #include <u.h>
2 #include <libc.h>
3 #include <ctype.h>
4 #include <bio.h>
5 #include <flate.h>
6 #include <draw.h>
7 #include "imagefile.h"
8
9 int debug;
10
11 enum
12 {
13         IDATSIZE = 1000000,
14
15         /* filtering algorithms */
16         FilterNone =    0,      /* new[x][y] = buf[x][y] */
17         FilterSub =     1,      /* new[x][y] = buf[x][y] + new[x-1][y] */ 
18         FilterUp =              2,      /* new[x][y] = buf[x][y] + new[x][y-1] */ 
19         FilterAvg =     3,      /* new[x][y] = buf[x][y] + (new[x-1][y]+new[x][y-1])/2 */ 
20         FilterPaeth =   4,      /* new[x][y] = buf[x][y] + paeth(new[x-1][y],new[x][y-1],new[x-1][y-1]) */
21         FilterLast =    5,
22
23         PropertyBit = 1<<5,
24 };
25
26 typedef struct ZlibR ZlibR;
27 typedef struct ZlibW ZlibW;
28
29 struct ZlibW
30 {
31         uchar *data;            /* Rawimage data */
32         int ndata;
33         int noutchan;
34         int chandesc;
35         int nchan;
36
37         uchar *scan;            /* new scanline */
38         uchar *lastscan;        /* previous scan line */
39         int scanlen;            /* scan line length */
40         int scanpos;            /* scan position */
41
42         int dx;                 /* width of image */
43         int dy;                 /* height of image */
44         int bpc;                        /* bits per channel (per pixel) */
45         int y;                          /* current scan line */
46         int pass;                       /* adam7 pass#; 0 means no adam7 */
47         uchar palette[3*256];   /* color palette */
48         int palsize;            /* number of palette entries */
49 };
50
51 struct ZlibR
52 {
53         Biobuf *io;             /* input buffer */
54         uchar *buf;             /* malloc'ed staging buffer */
55         uchar *p;                       /* next byte to decompress */
56         uchar *e;                       /* end of buffer */
57         ZlibW *w;
58 };
59
60 static ulong *crctab;
61 static uchar PNGmagic[] = { 137, 'P', 'N', 'G', '\r', '\n', 26, '\n'};
62
63 static ulong
64 get4(uchar *a)
65 {
66         return (a[0]<<24) | (a[1]<<16) | (a[2]<<8) | a[3];
67 }
68
69 static
70 void
71 pnginit(void)
72 {
73         static int inited;
74
75         if(inited)
76                 return;
77         inited = 1;
78         crctab = mkcrctab(0xedb88320);
79         if(crctab == nil)
80                 sysfatal("mkcrctab error");
81         inflateinit();
82 }
83
84 static
85 void*
86 pngmalloc(ulong n, int clear)
87 {
88         void *p;
89
90         p = mallocz(n, clear);
91         if(p == nil)
92                 sysfatal("malloc: %r");
93         return p;
94 }
95
96 static int
97 getchunk(Biobuf *b, char *type, uchar *d, int m)
98 {
99         uchar buf[8];
100         ulong crc = 0, crc2;
101         int n, nr;
102
103         if(Bread(b, buf, 8) != 8)
104                 return -1;
105         n = get4(buf);
106         memmove(type, buf+4, 4);
107         type[4] = 0;
108         if(n > m)
109                 sysfatal("getchunk needed %d, had %d", n, m);
110         nr = Bread(b, d, n);
111         if(nr != n)
112                 sysfatal("getchunk read %d, expected %d", nr, n);
113         crc = blockcrc(crctab, crc, type, 4);
114         crc = blockcrc(crctab, crc, d, n);
115         if(Bread(b, buf, 4) != 4)
116                 sysfatal("getchunk tlr failed");
117         crc2 = get4(buf);
118         if(crc != crc2)
119                 sysfatal("getchunk crc failed");
120         return n;
121 }
122
123 static int
124 zread(void *va)
125 {
126         ZlibR *z = va;
127         char type[5];
128         int n;
129
130         if(z->p >= z->e){
131         Again:
132                 z->p = z->buf;
133                 z->e = z->p;
134                 n = getchunk(z->io, type, z->p, IDATSIZE);
135                 if(n < 0 || strcmp(type, "IEND") == 0)
136                         return -1;
137                 z->e = z->p + n;
138                 if(!strcmp(type,"PLTE")){
139                         if(n < 3 || n > 3*256 || n%3)
140                                 sysfatal("invalid PLTE chunk len %d", n);
141                         memcpy(z->w->palette, z->p, n);
142                         z->w->palsize = 256;
143                         goto Again;
144                 }
145                 if(type[0] & PropertyBit)
146                         goto Again;  /* skip auxiliary chunks fornow */
147                 if(strcmp(type,"IDAT")){
148                         sysfatal("unrecognized mandatory chunk %s", type);
149                         goto Again;
150                 }
151         }
152         return *z->p++;
153 }
154
155 static uchar 
156 paeth(uchar a, uchar b, uchar c)
157 {
158         int p, pa, pb, pc;
159
160         p = a + b - c;
161         pa = abs(p - a);
162         pb = abs(p - b);
163         pc = abs(p - c);
164
165         if(pa <= pb && pa <= pc)
166                 return a;
167         else if(pb <= pc)
168                 return b;
169         return c;
170 }
171
172 static void
173 unfilter(int alg, uchar *buf, uchar *up, int len, int bypp)
174 {
175         int i;
176
177         switch(alg){
178         case FilterNone:
179                 break;
180
181         case FilterSub:
182                 for(i = bypp; i < len; ++i)
183                         buf[i] += buf[i-bypp];
184                 break;
185
186         case FilterUp:
187                 for(i = 0; i < len; ++i)
188                         buf[i] += up[i];
189                 break;
190
191         case FilterAvg:
192                 for(i = 0; i < bypp; ++i)
193                         buf[i] += (0+up[i])/2;
194                 for(; i < len; ++i)
195                         buf[i] += (buf[i-bypp]+up[i])/2;
196                 break;
197
198         case FilterPaeth:
199                 for(i = 0; i < bypp; ++i)
200                         buf[i] += paeth(0, up[i], 0);
201                 for(; i < len; ++i)
202                         buf[i] += paeth(buf[i-bypp], up[i], up[i-bypp]);
203                 break;
204
205         default:
206                 sysfatal("unknown filtering scheme %d\n", alg);
207         }
208 }
209
210 struct {
211         int x;
212         int y;
213         int dx;
214         int dy;
215 } adam7[] = {
216         {0,0,1,1},      /* eve alone */
217         {0,0,8,8},      /* pass 1 */
218         {4,0,8,8},      /* pass 2 */
219         {0,4,4,8},      /* pass 3 */
220         {2,0,4,4},      /* pass 4 */
221         {0,2,2,4},      /* pass 5 */
222         {1,0,2,2},      /* pass 6 */
223         {0,1,1,2},      /* pass 7 */
224 };
225
226 static void
227 scan(int len, ZlibW *z)
228 {
229         int chan, i, j, nbit, off, val;
230         uchar pixel[4], *p, *w;
231
232         unfilter(z->scan[0], z->scan+1, z->lastscan+1, len-1, (z->nchan*z->bpc+7)/8);
233
234         /*
235          * loop over raw bits extracting pixel values and converting to 8-bit
236          */
237         nbit = 0;
238         chan = 0;
239         val = 0;
240         off = z->y*z->dx + adam7[z->pass].x;
241         w = z->data + z->noutchan*off;
242         p = z->scan+1;  /* skip alg byte */
243         len--;
244         for(i=0; i<len*8; i++){
245                 val <<= 1;
246                 if(p[i>>3] & (1<<(7-(i&7))))
247                         val++;
248                 if(++nbit == z->bpc){
249                         /* finished the value */
250                         pixel[chan++] = (val*255)/((1<<z->bpc)-1);
251                         val = 0;
252                         nbit = 0;
253                         if(chan == z->nchan){
254                                 /* finished the pixel */
255                                 if(off < z->dx*z->dy){
256                                         if(z->nchan < 3 && z->palsize){
257                                                 j = pixel[0];
258                                                 if(z->bpc < 8)
259                                                         j >>= 8-z->bpc;
260                                                 if(j >= z->palsize)
261                                                         sysfatal("index %d >= palette size %d", j, z->palsize);
262                                                 pixel[3] = pixel[1];    /* alpha */
263                                                 pixel[0] = z->palette[3*j];
264                                                 pixel[1] = z->palette[3*j+1];
265                                                 pixel[2] = z->palette[3*j+2];
266                                         }
267                                         switch(z->chandesc){
268                                         case CYA16:
269                                         //      print("%.2x%.2x ", pixel[0], pixel[1]);
270                                                 *w++ = pixel[1];
271                                                 *w++ += (pixel[0]*pixel[1])/255;
272                                                 break;
273                                         case CRGBA32:
274                                         //      print("%.2x%.2x%.2x%.2x ", pixel[0], pixel[1], pixel[2], pixel[3]);
275                                                 *w++ += pixel[3];
276                                                 *w++ += (pixel[2]*pixel[3])/255;
277                                                 *w++ += (pixel[1]*pixel[3])/255;
278                                                 *w++ += (pixel[0]*pixel[3])/255;
279                                                 break;
280                                         case CRGB24:
281                                                 *w++ = pixel[2];
282                                                 *w++ = pixel[1];
283                                         case CY:
284                                                 *w++ = pixel[0];
285                                                 break;
286                                         }
287                                         w += (adam7[z->pass].dx-1)*z->noutchan;
288                                 }
289                                 off += adam7[z->pass].dx;
290                                 if(off >= (z->y+1)*z->dx){
291                                         /* finished the line */
292                                         return;
293                                 }
294                                 chan = 0;
295                         }
296                 }
297         }
298         sysfatal("scan line too short");
299 }
300
301 static int
302 scanbytes(ZlibW *z)
303 {
304         int bits, n, adx, dx;
305
306         if(adam7[z->pass].y >= z->dy || adam7[z->pass].x >= z->dx)
307                 return 0;
308         adx = adam7[z->pass].dx;
309         dx = z->dx - adam7[z->pass].x;
310         if(dx <= 0)
311                 n = 1;
312         else
313                 n = (dx+adx-1)/adx;
314         if(n != 1 + (z->dx - (adam7[z->pass].x+1)) / adam7[z->pass].dx){
315                 print("%d/%d != 1+(%d-1)/%d = %d\n",
316                         z->dx - adam7[z->pass].x - 1 + adx, adx,
317                         z->dx - (adam7[z->pass].x+1), adam7[z->pass].dx,
318                         1 + (z->dx - (adam7[z->pass].x+1)) / adam7[z->pass].dx);
319         }
320         bits = n*z->bpc*z->nchan;
321         return 1 + (bits+7)/8;
322 }
323
324 static int
325 nextpass(ZlibW *z)
326 {
327         int len;
328
329         memset(z->lastscan, 0, z->scanlen);
330         do{
331                 z->pass = (z->pass+1)%8;
332                 z->y = adam7[z->pass].y;
333                 len = scanbytes(z);
334         }while(len < 2);
335         return len;
336 }
337
338 static int
339 zwrite(void *vz, void *vbuf, int n)
340 {
341         int oldn, m, len;
342         uchar *buf, *t;
343         ZlibW *z;
344
345         z = vz;
346         buf = vbuf;
347         oldn = n;
348
349         len = scanbytes(z);
350         if(len < 2)
351                 len = nextpass(z);
352
353         while(n > 0){
354                 m = len - z->scanpos;
355                 if(m > n){
356                         /* save final partial line */
357                         memmove(z->scan+z->scanpos, buf, n);
358                         z->scanpos += n;
359                         break;
360                 }
361
362                 /* fill line */
363                 memmove(z->scan+z->scanpos, buf, m);
364                 buf += m;
365                 n -= m;
366
367                 /* process line */
368                 scan(len, z);
369                 t = z->scan;
370                 z->scan = z->lastscan;
371                 z->lastscan = t;
372
373                 z->scanpos = 0;
374                 z->y += adam7[z->pass].dy;
375                 if(z->y >= z->dy)
376                         len = nextpass(z);
377         }
378         return oldn;
379 }
380
381 static Rawimage*
382 readslave(Biobuf *b)
383 {
384         char type[5];
385         int bpc, colorfmt, dx, dy, err, n, nchan, nout, useadam7;
386         uchar *buf, *h;
387         Rawimage *image;
388         ZlibR zr;
389         ZlibW zw;
390
391         buf = pngmalloc(IDATSIZE, 0);
392         Bread(b, buf, sizeof PNGmagic);
393         if(memcmp(PNGmagic, buf, sizeof PNGmagic) != 0)
394                 sysfatal("bad PNGmagic");
395
396         n = getchunk(b, type, buf, IDATSIZE);
397         if(n < 13 || strcmp(type,"IHDR") != 0)
398                 sysfatal("missing IHDR chunk");
399         h = buf;
400         dx = get4(h);
401         h += 4;
402         dy = get4(h);
403         h += 4;
404         if(dx <= 0 || dy <= 0)
405                 sysfatal("impossible image size %dx%d", dx, dy);
406         if(debug)
407                 fprint(2, "readpng %dx%d\n", dx, dy);
408
409         bpc = *h++;
410         colorfmt = *h++;
411         nchan = 0;
412         if(*h++ != 0)
413                 sysfatal("only deflate supported for now [%d]", h[-1]);
414         if(*h++ != FilterNone)
415                 sysfatal("only FilterNone supported for now [%d]", h[-1]);
416         useadam7 = *h++;
417         USED(h);
418
419         image = pngmalloc(sizeof(Rawimage), 1);
420         image->r = Rect(0, 0, dx, dy);
421         nout = 0;
422         switch(colorfmt){
423         case 0: /* grey */
424                 image->nchans = 1;
425                 image->chandesc = CY;
426                 nout = 1;
427                 nchan = 1;
428                 break;
429         case 2: /* rgb */
430                 image->nchans = 1;
431                 image->chandesc = CRGB24;
432                 nout = 3;
433                 nchan = 3;
434                 break;
435         case 3: /* indexed rgb with PLTE */
436                 image->nchans = 1;
437                 image->chandesc = CRGB24;
438                 nout = 3;
439                 nchan = 1;
440                 break;
441         case 4: /* grey+alpha */
442                 image->nchans = 1;
443                 image->chandesc = CYA16;
444                 nout = 2;
445                 nchan = 2;
446                 break;
447         case 6: /* rgb+alpha */
448                 image->nchans = 1;
449                 image->chandesc = CRGBA32;
450                 nout = 4;
451                 nchan = 4;
452                 break;
453         default:
454                 sysfatal("unsupported color scheme %d", h[-1]);
455         }
456         image->chanlen = dx*dy*nout;
457         image->chans[0] = pngmalloc(image->chanlen, 0);
458         memset(image->chans[0], 0, image->chanlen);
459
460         memset(&zr, 0, sizeof zr);
461         zr.w = &zw;
462         zr.io = b;
463         zr.buf = buf;
464
465         memset(&zw, 0, sizeof zw);
466         if(useadam7)
467                 zw.pass = 1;
468         zw.data = image->chans[0];
469         zw.ndata = image->chanlen;
470         zw.chandesc = image->chandesc;
471         zw.noutchan = nout;
472
473         zw.dx = dx;
474         zw.dy = dy;
475         zw.scanlen = (nchan*dx*bpc+7)/8+1;
476         zw.scan = pngmalloc(zw.scanlen, 1);
477         zw.lastscan = pngmalloc(zw.scanlen, 1);
478         zw.nchan = nchan;
479         zw.bpc = bpc;
480
481         err = inflatezlib(&zw, zwrite, &zr, zread);
482
483         if(err)
484                 sysfatal("inflatezlib %s\n", flateerr(err));
485
486         free(buf);
487         free(zw.scan);
488         free(zw.lastscan);
489         return image;
490 }
491
492 Rawimage**
493 Breadpng(Biobuf *b, int colorspace)
494 {
495         Rawimage **array, *r;
496
497         if(colorspace != CRGB){
498                 werrstr("ReadPNG: unknown color space %d", colorspace);
499                 return nil;
500         }
501         pnginit();
502         array = malloc(2*sizeof(*array));
503         if(array==nil)
504                 return nil;
505         r = readslave(b);
506         array[0] = r;
507         array[1] = nil;
508         return array;
509 }
510
511 Rawimage**
512 readpng(int fd, int colorspace)
513 {
514         Biobuf b;
515         Rawimage **a;
516
517         if(Binit(&b, fd, OREAD) < 0)
518                 return nil;
519         a = Breadpng(&b, colorspace);
520         Bterm(&b);
521         return a;
522 }