Blob


1 // work in progress... this version only good enough to read
2 // non-interleaved, 24bit RGB images
4 #include <u.h>
5 #include <libc.h>
6 #include <ctype.h>
7 #include <bio.h>
8 #include <flate.h>
9 #include <draw.h>
10 #include "imagefile.h"
12 int debug;
14 enum{ IDATSIZE=1000000,
15 /* filtering algorithms, supposedly increase compression */
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 PropertyBit = 1<<5,
23 };
25 typedef struct ZlibR{
26 Biobuf *bi;
27 uchar *buf;
28 uchar *b; // next byte to decompress
29 uchar *e; // past end of buf
30 } ZlibR;
32 typedef struct ZlibW{
33 uchar *r, *g, *b; // Rawimage channels
34 int chan; // next channel to write
35 int col; // column index of current pixel
36 // -1 = one-byte pseudo-column for filter spec
37 int row; // row index of current pixel
38 int ncol, nrow; // image width, height
39 int filter; // algorithm for current scanline
40 } ZlibW;
42 static ulong *crctab;
43 static uchar PNGmagic[] = {137,80,78,71,13,10,26,10};
44 static char memerr[] = "ReadPNG: malloc failed: %r";
46 static ulong
47 get4(uchar *a)
48 {
49 return (a[0]<<24) | (a[1]<<16) | (a[2]<<8) | a[3];
50 }
52 static
53 void
54 pnginit(void)
55 {
56 static int inited;
58 if(inited)
59 return;
60 inited = 1;
61 crctab = mkcrctab(0xedb88320);
62 if(crctab == nil)
63 sysfatal("mkcrctab error");
64 inflateinit();
65 }
67 static
68 void*
69 pngmalloc(ulong n, int clear)
70 {
71 void *p;
73 p = malloc(n);
74 if(p == nil)
75 sysfatal(memerr);
76 if(clear)
77 memset(p, 0, n);
78 return p;
79 }
81 static int
82 getchunk(Biobuf *b, char *type, uchar *d, int m)
83 {
84 uchar buf[8];
85 ulong crc = 0, crc2;
86 int n, nr;
88 if(Bread(b, buf, 8) != 8)
89 return -1;
90 n = get4(buf);
91 memmove(type, buf+4, 4);
92 type[4] = 0;
93 if(n > m)
94 sysfatal("getchunk needed %d, had %d", n, m);
95 nr = Bread(b, d, n);
96 if(nr != n)
97 sysfatal("getchunk read %d, expected %d", nr, n);
98 crc = blockcrc(crctab, crc, type, 4);
99 crc = blockcrc(crctab, crc, d, n);
100 if(Bread(b, buf, 4) != 4)
101 sysfatal("getchunk tlr failed");
102 crc2 = get4(buf);
103 if(crc != crc2)
104 sysfatal("getchunk crc failed");
105 return n;
108 static int
109 zread(void *va)
111 ZlibR *z = va;
112 char type[5];
113 int n;
115 if(z->b >= z->e){
116 refill_buffer:
117 z->b = z->buf;
118 n = getchunk(z->bi, type, z->b, IDATSIZE);
119 if(n < 0 || strcmp(type, "IEND") == 0)
120 return -1;
121 z->e = z->b + n;
122 if(type[0] & PropertyBit)
123 goto refill_buffer; /* skip auxiliary chunks for now */
124 if(strcmp(type,"IDAT") != 0)
125 sysfatal("unrecognized mandatory chunk %s", type);
127 return *z->b++;
130 static uchar
131 paeth(uchar a, uchar b, uchar c)
133 int p, pa, pb, pc;
135 p = (int)a + (int)b - (int)c;
136 pa = abs(p - (int)a);
137 pb = abs(p - (int)b);
138 pc = abs(p - (int)c);
140 if(pa <= pb && pa <= pc)
141 return a;
142 else if(pb <= pc)
143 return b;
144 return c;
147 static void
148 unfilter(int alg, uchar *buf, uchar *ebuf, int up)
150 switch(alg){
151 case FilterSub:
152 while (++buf < ebuf)
153 *buf += buf[-1];
154 break;
155 case FilterUp:
156 if (up != 0)
157 do
158 *buf += buf[up];
159 while (++buf < ebuf);
160 break;
161 case FilterAvg:
162 if (up == 0)
163 while (++buf < ebuf)
164 *buf += buf[-1]/2;
165 else{
166 *buf += buf[up]/2;
167 while (++buf < ebuf)
168 *buf += (buf[-1]+buf[up])/2;
170 break;
171 case FilterPaeth:
172 if (up == 0)
173 while (++buf < ebuf)
174 *buf += buf[-1];
175 else{
176 *buf += paeth(0, buf[up], 0);
177 while (++buf < ebuf)
178 *buf += paeth(buf[-1], buf[up], buf[up-1]);
180 break;
184 static int
185 zwrite(void *va, void *vb, int n)
187 ZlibW *z = va;
188 uchar *buf = vb;
189 int i, up;
190 for(i=0; i<n; i++){
191 if(z->col == -1){
192 // set filter byte
193 z->filter = *buf++;
194 if (z->filter >= FilterLast)
195 sysfatal("unknown filter algorithm %d for row %d", z->row, z->filter);
196 z->col++;
197 continue;
199 switch(z->chan){
200 case 0:
201 *z->r++ = *buf++;
202 z->chan = 1;
203 break;
204 case 1:
205 *z->g++ = *buf++;
206 z->chan = 2;
207 break;
208 case 2:
209 *z->b++ = *buf++;
210 z->chan = 0;
211 z->col++;
212 if(z->col == z->ncol){
213 if (z->filter){
214 if(z->row == 0)
215 up = 0;
216 else
217 up = -z->ncol;
218 unfilter(z->filter, z->r - z->col, z->r, up);
219 unfilter(z->filter, z->g - z->col, z->g, up);
220 unfilter(z->filter, z->b - z->col, z->b, up);
222 z->col = -1;
223 z->row++;
224 if((z->row >= z->nrow) && (i < n-1) )
225 sysfatal("header said %d rows; data goes further", z->nrow);
227 break;
230 return n;
233 static Rawimage*
234 readslave(Biobuf *b)
236 ZlibR zr;
237 ZlibW zw;
238 Rawimage *image;
239 char type[5];
240 uchar *buf, *h;
241 int k, n, nrow, ncol, err;
243 buf = pngmalloc(IDATSIZE, 0);
244 Bread(b, buf, sizeof PNGmagic);
245 if(memcmp(PNGmagic, buf, sizeof PNGmagic) != 0)
246 sysfatal("bad PNGmagic");
248 n = getchunk(b, type, buf, IDATSIZE);
249 if(n < 13 || strcmp(type,"IHDR") != 0)
250 sysfatal("missing IHDR chunk");
251 h = buf;
252 ncol = get4(h); h += 4;
253 nrow = get4(h); h += 4;
254 if(ncol <= 0 || nrow <= 0)
255 sysfatal("impossible image size nrow=%d ncol=%d", nrow, ncol);
256 if(debug)
257 fprint(2, "readpng nrow=%d ncol=%d\n", nrow, ncol);
258 if(*h++ != 8)
259 sysfatal("only 24 bit per pixel supported for now [%d]", h[-1]);
260 if(*h++ != 2)
261 sysfatal("only rgb supported for now [%d]", h[-1]);
262 if(*h++ != 0)
263 sysfatal("only deflate supported for now [%d]", h[-1]);
264 if(*h++ != FilterNone)
265 sysfatal("only FilterNone supported for now [%d]", h[-1]);
266 if(*h != 0)
267 sysfatal("only non-interlaced supported for now [%d]", h[-1]);
269 image = pngmalloc(sizeof(Rawimage), 1);
270 image->r = Rect(0, 0, ncol, nrow);
271 image->cmap = nil;
272 image->cmaplen = 0;
273 image->chanlen = ncol*nrow;
274 image->fields = 0;
275 image->gifflags = 0;
276 image->gifdelay = 0;
277 image->giftrindex = 0;
278 image->chandesc = CRGB;
279 image->nchans = 3;
280 for(k=0; k<3; k++)
281 image->chans[k] = pngmalloc(ncol*nrow, 0);
282 zr.bi = b;
283 zr.buf = buf;
284 zr.b = zr.e = buf + IDATSIZE;
285 zw.r = image->chans[0];
286 zw.g = image->chans[1];
287 zw.b = image->chans[2];
288 zw.chan = 0;
289 zw.col = -1;
290 zw.row = 0;
291 zw.ncol = ncol;
292 zw.nrow = nrow;
293 err = inflatezlib(&zw, zwrite, &zr, zread);
294 if(err)
295 sysfatal("inflatezlib %s\n", flateerr(err));
296 free(buf);
297 return image;
300 Rawimage**
301 Breadpng(Biobuf *b, int colorspace)
303 Rawimage *r, **array;
304 char buf[ERRMAX];
306 buf[0] = '\0';
307 if(colorspace != CRGB){
308 errstr(buf, sizeof buf); /* throw it away */
309 werrstr("ReadPNG: unknown color space %d", colorspace);
310 return nil;
312 pnginit();
313 array = malloc(2*sizeof(*array));
314 if(array==nil)
315 return nil;
316 errstr(buf, sizeof buf); /* throw it away */
317 r = readslave(b);
318 array[0] = r;
319 array[1] = nil;
320 return array;
323 Rawimage**
324 readpng(int fd, int colorspace)
326 Rawimage** a;
327 Biobuf b;
329 if(Binit(&b, fd, OREAD) < 0)
330 return nil;
331 a = Breadpng(&b, colorspace);
332 Bterm(&b);
333 return a;