Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4680 | right-hear | 1 | #include "fitz.h" |
2 | #include "muxps.h" |
||
3 | |||
4 | #include |
||
5 | |||
6 | struct info |
||
7 | { |
||
8 | int width, height, depth, n; |
||
9 | int interlace, indexed; |
||
10 | int size; |
||
11 | unsigned char *samples; |
||
12 | unsigned char palette[256*4]; |
||
13 | int transparency; |
||
14 | int trns[3]; |
||
15 | int xres, yres; |
||
16 | }; |
||
17 | |||
18 | static inline int getint(unsigned char *p) |
||
19 | { |
||
20 | return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; |
||
21 | } |
||
22 | |||
23 | static inline int getcomp(unsigned char *line, int x, int bpc) |
||
24 | { |
||
25 | switch (bpc) |
||
26 | { |
||
27 | case 1: return (line[x >> 3] >> ( 7 - (x & 7) ) ) & 1; |
||
28 | case 2: return (line[x >> 2] >> ( ( 3 - (x & 3) ) << 1 ) ) & 3; |
||
29 | case 4: return (line[x >> 1] >> ( ( 1 - (x & 1) ) << 2 ) ) & 15; |
||
30 | case 8: return line[x]; |
||
31 | case 16: return line[x << 1] << 8 | line[(x << 1) + 1]; |
||
32 | } |
||
33 | return 0; |
||
34 | } |
||
35 | |||
36 | static inline void putcomp(unsigned char *line, int x, int bpc, int value) |
||
37 | { |
||
38 | int maxval = (1 << bpc) - 1; |
||
39 | |||
40 | switch (bpc) |
||
41 | { |
||
42 | case 1: line[x >> 3] &= ~(maxval << (7 - (x & 7))); break; |
||
43 | case 2: line[x >> 2] &= ~(maxval << ((3 - (x & 3)) << 1)); break; |
||
44 | case 4: line[x >> 1] &= ~(maxval << ((1 - (x & 1)) << 2)); break; |
||
45 | } |
||
46 | |||
47 | switch (bpc) |
||
48 | { |
||
49 | case 1: line[x >> 3] |= value << (7 - (x & 7)); break; |
||
50 | case 2: line[x >> 2] |= value << ((3 - (x & 3)) << 1); break; |
||
51 | case 4: line[x >> 1] |= value << ((1 - (x & 1)) << 2); break; |
||
52 | case 8: line[x] = value; break; |
||
53 | case 16: line[x << 1] = value >> 8; line[(x << 1) + 1] = value & 0xFF; break; |
||
54 | } |
||
55 | } |
||
56 | |||
57 | static const unsigned char png_signature[8] = |
||
58 | { |
||
59 | 137, 80, 78, 71, 13, 10, 26, 10 |
||
60 | }; |
||
61 | |||
62 | static void *zalloc(void *opaque, unsigned int items, unsigned int size) |
||
63 | { |
||
64 | return fz_calloc(items, size); |
||
65 | } |
||
66 | |||
67 | static void zfree(void *opaque, void *address) |
||
68 | { |
||
69 | fz_free(address); |
||
70 | } |
||
71 | |||
72 | static inline int paeth(int a, int b, int c) |
||
73 | { |
||
74 | /* The definitions of ac and bc are correct, not a typo. */ |
||
75 | int ac = b - c, bc = a - c, abcc = ac + bc; |
||
76 | int pa = (ac < 0 ? -ac : ac); |
||
77 | int pb = (bc < 0 ? -bc : bc); |
||
78 | int pc = (abcc < 0 ? -abcc : abcc); |
||
79 | return pa <= pb && pa <= pc ? a : pb <= pc ? b : c; |
||
80 | } |
||
81 | |||
82 | static void |
||
83 | png_predict(unsigned char *samples, int width, int height, int n, int depth) |
||
84 | { |
||
85 | int stride = (width * n * depth + 7) / 8; |
||
86 | int bpp = (n * depth + 7) / 8; |
||
87 | int i, row; |
||
88 | |||
89 | for (row = 0; row < height; row ++) |
||
90 | { |
||
91 | unsigned char *src = samples + (stride + 1) * row; |
||
92 | unsigned char *dst = samples + stride * row; |
||
93 | |||
94 | unsigned char *a = dst; |
||
95 | unsigned char *b = dst - stride; |
||
96 | unsigned char *c = dst - stride; |
||
97 | |||
98 | switch (*src++) |
||
99 | { |
||
100 | default: |
||
101 | case 0: /* None */ |
||
102 | for (i = 0; i < stride; i++) |
||
103 | *dst++ = *src++; |
||
104 | break; |
||
105 | |||
106 | case 1: /* Sub */ |
||
107 | for (i = 0; i < bpp; i++) |
||
108 | *dst++ = *src++; |
||
109 | for (i = bpp; i < stride; i++) |
||
110 | *dst++ = *src++ + *a++; |
||
111 | break; |
||
112 | |||
113 | case 2: /* Up */ |
||
114 | if (row == 0) |
||
115 | for (i = 0; i < stride; i++) |
||
116 | *dst++ = *src++; |
||
117 | else |
||
118 | for (i = 0; i < stride; i++) |
||
119 | *dst++ = *src++ + *b++; |
||
120 | break; |
||
121 | |||
122 | case 3: /* Average */ |
||
123 | if (row == 0) |
||
124 | { |
||
125 | for (i = 0; i < bpp; i++) |
||
126 | *dst++ = *src++; |
||
127 | for (i = bpp; i < stride; i++) |
||
128 | *dst++ = *src++ + (*a++ >> 1); |
||
129 | } |
||
130 | else |
||
131 | { |
||
132 | for (i = 0; i < bpp; i++) |
||
133 | *dst++ = *src++ + (*b++ >> 1); |
||
134 | for (i = bpp; i < stride; i++) |
||
135 | *dst++ = *src++ + ((*b++ + *a++) >> 1); |
||
136 | } |
||
137 | break; |
||
138 | |||
139 | case 4: /* Paeth */ |
||
140 | if (row == 0) |
||
141 | { |
||
142 | for (i = 0; i < bpp; i++) |
||
143 | *dst++ = *src++ + paeth(0, 0, 0); |
||
144 | for (i = bpp; i < stride; i++) |
||
145 | *dst++ = *src++ + paeth(*a++, 0, 0); |
||
146 | } |
||
147 | else |
||
148 | { |
||
149 | for (i = 0; i < bpp; i++) |
||
150 | *dst++ = *src++ + paeth(0, *b++, 0); |
||
151 | for (i = bpp; i < stride; i++) |
||
152 | *dst++ = *src++ + paeth(*a++, *b++, *c++); |
||
153 | } |
||
154 | break; |
||
155 | } |
||
156 | } |
||
157 | } |
||
158 | |||
159 | static const int adam7_ix[7] = { 0, 4, 0, 2, 0, 1, 0 }; |
||
160 | static const int adam7_dx[7] = { 8, 8, 4, 4, 2, 2, 1 }; |
||
161 | static const int adam7_iy[7] = { 0, 0, 4, 0, 2, 0, 1 }; |
||
162 | static const int adam7_dy[7] = { 8, 8, 8, 4, 4, 2, 2 }; |
||
163 | |||
164 | static void |
||
165 | png_deinterlace_passes(struct info *info, int *w, int *h, int *ofs) |
||
166 | { |
||
167 | int p, bpp = info->depth * info->n; |
||
168 | ofs[0] = 0; |
||
169 | for (p = 0; p < 7; p++) |
||
170 | { |
||
171 | w[p] = (info->width + adam7_dx[p] - adam7_ix[p] - 1) / adam7_dx[p]; |
||
172 | h[p] = (info->height + adam7_dy[p] - adam7_iy[p] - 1) / adam7_dy[p]; |
||
173 | if (w[p] == 0) h[p] = 0; |
||
174 | if (h[p] == 0) w[p] = 0; |
||
175 | if (w[p] && h[p]) |
||
176 | ofs[p + 1] = ofs[p] + h[p] * (1 + (w[p] * bpp + 7) / 8); |
||
177 | else |
||
178 | ofs[p + 1] = ofs[p]; |
||
179 | } |
||
180 | } |
||
181 | |||
182 | static void |
||
183 | png_deinterlace(struct info *info, int *passw, int *passh, int *passofs) |
||
184 | { |
||
185 | int n = info->n; |
||
186 | int depth = info->depth; |
||
187 | int stride = (info->width * n * depth + 7) / 8; |
||
188 | unsigned char *output; |
||
189 | int p, x, y, k; |
||
190 | |||
191 | output = fz_calloc(info->height, stride); |
||
192 | |||
193 | for (p = 0; p < 7; p++) |
||
194 | { |
||
195 | unsigned char *sp = info->samples + passofs[p]; |
||
196 | int w = passw[p]; |
||
197 | int h = passh[p]; |
||
198 | |||
199 | png_predict(sp, w, h, n, depth); |
||
200 | for (y = 0; y < h; y++) |
||
201 | { |
||
202 | for (x = 0; x < w; x++) |
||
203 | { |
||
204 | int outx = x * adam7_dx[p] + adam7_ix[p]; |
||
205 | int outy = y * adam7_dy[p] + adam7_iy[p]; |
||
206 | unsigned char *dp = output + outy * stride; |
||
207 | for (k = 0; k < n; k++) |
||
208 | { |
||
209 | int v = getcomp(sp, x * n + k, depth); |
||
210 | putcomp(dp, outx * n + k, depth, v); |
||
211 | } |
||
212 | } |
||
213 | sp += (w * depth * n + 7) / 8; |
||
214 | } |
||
215 | } |
||
216 | |||
217 | fz_free(info->samples); |
||
218 | info->samples = output; |
||
219 | } |
||
220 | |||
221 | static int |
||
222 | png_read_ihdr(struct info *info, unsigned char *p, int size) |
||
223 | { |
||
224 | int color, compression, filter; |
||
225 | |||
226 | if (size != 13) |
||
227 | return fz_throw("IHDR chunk is the wrong size"); |
||
228 | |||
229 | info->width = getint(p + 0); |
||
230 | info->height = getint(p + 4); |
||
231 | info->depth = p[8]; |
||
232 | |||
233 | color = p[9]; |
||
234 | compression = p[10]; |
||
235 | filter = p[11]; |
||
236 | info->interlace = p[12]; |
||
237 | |||
238 | if (info->width <= 0) |
||
239 | return fz_throw("image width must be > 0"); |
||
240 | if (info->height <= 0) |
||
241 | return fz_throw("image height must be > 0"); |
||
242 | |||
243 | if (info->depth != 1 && info->depth != 2 && info->depth != 4 && |
||
244 | info->depth != 8 && info->depth != 16) |
||
245 | return fz_throw("image bit depth must be one of 1, 2, 4, 8, 16"); |
||
246 | if (color == 2 && info->depth < 8) |
||
247 | return fz_throw("illegal bit depth for truecolor"); |
||
248 | if (color == 3 && info->depth > 8) |
||
249 | return fz_throw("illegal bit depth for indexed"); |
||
250 | if (color == 4 && info->depth < 8) |
||
251 | return fz_throw("illegal bit depth for grayscale with alpha"); |
||
252 | if (color == 6 && info->depth < 8) |
||
253 | return fz_throw("illegal bit depth for truecolor with alpha"); |
||
254 | |||
255 | info->indexed = 0; |
||
256 | if (color == 0) /* gray */ |
||
257 | info->n = 1; |
||
258 | else if (color == 2) /* rgb */ |
||
259 | info->n = 3; |
||
260 | else if (color == 4) /* gray alpha */ |
||
261 | info->n = 2; |
||
262 | else if (color == 6) /* rgb alpha */ |
||
263 | info->n = 4; |
||
264 | else if (color == 3) /* indexed */ |
||
265 | { |
||
266 | info->indexed = 1; |
||
267 | info->n = 1; |
||
268 | } |
||
269 | else |
||
270 | return fz_throw("unknown color type"); |
||
271 | |||
272 | if (compression != 0) |
||
273 | return fz_throw("unknown compression method"); |
||
274 | if (filter != 0) |
||
275 | return fz_throw("unknown filter method"); |
||
276 | if (info->interlace != 0 && info->interlace != 1) |
||
277 | return fz_throw("interlace method not supported"); |
||
278 | |||
279 | return fz_okay; |
||
280 | } |
||
281 | |||
282 | static int |
||
283 | png_read_plte(struct info *info, unsigned char *p, int size) |
||
284 | { |
||
285 | int n = size / 3; |
||
286 | int i; |
||
287 | |||
288 | if (n > 256 || n > (1 << info->depth)) |
||
289 | return fz_throw("too many samples in palette"); |
||
290 | |||
291 | for (i = 0; i < n; i++) |
||
292 | { |
||
293 | info->palette[i * 4] = p[i * 3]; |
||
294 | info->palette[i * 4 + 1] = p[i * 3 + 1]; |
||
295 | info->palette[i * 4 + 2] = p[i * 3 + 2]; |
||
296 | } |
||
297 | |||
298 | return fz_okay; |
||
299 | } |
||
300 | |||
301 | static int |
||
302 | png_read_trns(struct info *info, unsigned char *p, int size) |
||
303 | { |
||
304 | int i; |
||
305 | |||
306 | info->transparency = 1; |
||
307 | |||
308 | if (info->indexed) |
||
309 | { |
||
310 | if (size > 256 || size > (1 << info->depth)) |
||
311 | return fz_throw("too many samples in transparency table"); |
||
312 | for (i = 0; i < size; i++) |
||
313 | info->palette[i * 4 + 3] = p[i]; |
||
314 | } |
||
315 | else |
||
316 | { |
||
317 | if (size != info->n * 2) |
||
318 | return fz_throw("tRNS chunk is the wrong size"); |
||
319 | for (i = 0; i < info->n; i++) |
||
320 | info->trns[i] = (p[i * 2] << 8 | p[i * 2 + 1]) & ((1 << info->depth) - 1); |
||
321 | } |
||
322 | |||
323 | return fz_okay; |
||
324 | } |
||
325 | |||
326 | static int |
||
327 | png_read_idat(struct info *info, unsigned char *p, int size, z_stream *stm) |
||
328 | { |
||
329 | int code; |
||
330 | |||
331 | stm->next_in = p; |
||
332 | stm->avail_in = size; |
||
333 | |||
334 | code = inflate(stm, Z_SYNC_FLUSH); |
||
335 | if (code != Z_OK && code != Z_STREAM_END) |
||
336 | return fz_throw("zlib error: %s", stm->msg); |
||
337 | if (stm->avail_in != 0) |
||
338 | { |
||
339 | if (stm->avail_out == 0) |
||
340 | return fz_throw("ran out of output before input"); |
||
341 | return fz_throw("inflate did not consume buffer (%d remaining)", stm->avail_in); |
||
342 | } |
||
343 | |||
344 | return fz_okay; |
||
345 | } |
||
346 | |||
347 | static int |
||
348 | png_read_phys(struct info *info, unsigned char *p, int size) |
||
349 | { |
||
350 | if (size != 9) |
||
351 | return fz_throw("pHYs chunk is the wrong size"); |
||
352 | if (p[8] == 1) |
||
353 | { |
||
354 | info->xres = getint(p) * 254 / 10000; |
||
355 | info->yres = getint(p + 4) * 254 / 10000; |
||
356 | } |
||
357 | return fz_okay; |
||
358 | } |
||
359 | |||
360 | static int |
||
361 | png_read_image(struct info *info, unsigned char *p, int total) |
||
362 | { |
||
363 | int passw[7], passh[7], passofs[8]; |
||
364 | int code, size; |
||
365 | z_stream stm; |
||
366 | |||
367 | memset(info, 0, sizeof (struct info)); |
||
368 | memset(info->palette, 255, sizeof(info->palette)); |
||
369 | info->xres = 96; |
||
370 | info->yres = 96; |
||
371 | |||
372 | /* Read signature */ |
||
373 | |||
374 | if (total < 8 + 12 || memcmp(p, png_signature, 8)) |
||
375 | return fz_throw("not a png image (wrong signature)"); |
||
376 | |||
377 | p += 8; |
||
378 | total -= 8; |
||
379 | |||
380 | /* Read IHDR chunk (must come first) */ |
||
381 | |||
382 | size = getint(p); |
||
383 | |||
384 | if (size + 12 > total) |
||
385 | return fz_throw("premature end of data in png image"); |
||
386 | |||
387 | if (!memcmp(p + 4, "IHDR", 4)) |
||
388 | { |
||
389 | code = png_read_ihdr(info, p + 8, size); |
||
390 | if (code) |
||
391 | return fz_rethrow(code, "cannot read png header"); |
||
392 | } |
||
393 | else |
||
394 | return fz_throw("png file must start with IHDR chunk"); |
||
395 | |||
396 | p += size + 12; |
||
397 | total -= size + 12; |
||
398 | |||
399 | /* Prepare output buffer */ |
||
400 | |||
401 | if (!info->interlace) |
||
402 | { |
||
403 | info->size = info->height * (1 + (info->width * info->n * info->depth + 7) / 8); |
||
404 | } |
||
405 | else |
||
406 | { |
||
407 | png_deinterlace_passes(info, passw, passh, passofs); |
||
408 | info->size = passofs[7]; |
||
409 | } |
||
410 | |||
411 | info->samples = fz_malloc(info->size); |
||
412 | |||
413 | stm.zalloc = zalloc; |
||
414 | stm.zfree = zfree; |
||
415 | stm.opaque = NULL; |
||
416 | |||
417 | stm.next_out = info->samples; |
||
418 | stm.avail_out = info->size; |
||
419 | |||
420 | code = inflateInit(&stm); |
||
421 | if (code != Z_OK) |
||
422 | return fz_throw("zlib error: %s", stm.msg); |
||
423 | |||
424 | /* Read remaining chunks until IEND */ |
||
425 | |||
426 | while (total > 8) |
||
427 | { |
||
428 | size = getint(p); |
||
429 | |||
430 | if (size + 12 > total) |
||
431 | return fz_throw("premature end of data in png image"); |
||
432 | |||
433 | if (!memcmp(p + 4, "PLTE", 4)) |
||
434 | { |
||
435 | code = png_read_plte(info, p + 8, size); |
||
436 | if (code) |
||
437 | return fz_rethrow(code, "cannot read png palette"); |
||
438 | } |
||
439 | |||
440 | if (!memcmp(p + 4, "tRNS", 4)) |
||
441 | { |
||
442 | code = png_read_trns(info, p + 8, size); |
||
443 | if (code) |
||
444 | return fz_rethrow(code, "cannot read png transparency"); |
||
445 | } |
||
446 | |||
447 | if (!memcmp(p + 4, "pHYs", 4)) |
||
448 | { |
||
449 | code = png_read_phys(info, p + 8, size); |
||
450 | if (code) |
||
451 | return fz_rethrow(code, "cannot read png resolution"); |
||
452 | } |
||
453 | |||
454 | if (!memcmp(p + 4, "IDAT", 4)) |
||
455 | { |
||
456 | code = png_read_idat(info, p + 8, size, &stm); |
||
457 | if (code) |
||
458 | return fz_rethrow(code, "cannot read png image data"); |
||
459 | } |
||
460 | |||
461 | if (!memcmp(p + 4, "IEND", 4)) |
||
462 | break; |
||
463 | |||
464 | p += size + 12; |
||
465 | total -= size + 12; |
||
466 | } |
||
467 | |||
468 | code = inflateEnd(&stm); |
||
469 | if (code != Z_OK) |
||
470 | return fz_throw("zlib error: %s", stm.msg); |
||
471 | |||
472 | /* Apply prediction filter and deinterlacing */ |
||
473 | |||
474 | if (!info->interlace) |
||
475 | png_predict(info->samples, info->width, info->height, info->n, info->depth); |
||
476 | else |
||
477 | png_deinterlace(info, passw, passh, passofs); |
||
478 | |||
479 | return fz_okay; |
||
480 | } |
||
481 | |||
482 | static fz_pixmap * |
||
483 | png_expand_palette(struct info *info, fz_pixmap *src) |
||
484 | { |
||
485 | fz_pixmap *dst = fz_new_pixmap(fz_device_rgb, src->w, src->h); |
||
486 | unsigned char *sp = src->samples; |
||
487 | unsigned char *dp = dst->samples; |
||
488 | int x, y; |
||
489 | |||
490 | dst->xres = src->xres; |
||
491 | dst->yres = src->yres; |
||
492 | |||
493 | for (y = 0; y < info->height; y++) |
||
494 | { |
||
495 | for (x = 0; x < info->width; x++) |
||
496 | { |
||
497 | int v = *sp << 2; |
||
498 | *dp++ = info->palette[v]; |
||
499 | *dp++ = info->palette[v + 1]; |
||
500 | *dp++ = info->palette[v + 2]; |
||
501 | *dp++ = info->palette[v + 3]; |
||
502 | sp += 2; |
||
503 | } |
||
504 | } |
||
505 | |||
506 | fz_drop_pixmap(src); |
||
507 | return dst; |
||
508 | } |
||
509 | |||
510 | static void |
||
511 | png_mask_transparency(struct info *info, fz_pixmap *dst) |
||
512 | { |
||
513 | int stride = (info->width * info->n * info->depth + 7) / 8; |
||
514 | int depth = info->depth; |
||
515 | int n = info->n; |
||
516 | int x, y, k, t; |
||
517 | |||
518 | for (y = 0; y < info->height; y++) |
||
519 | { |
||
520 | unsigned char *sp = info->samples + y * stride; |
||
521 | unsigned char *dp = dst->samples + y * dst->w * dst->n; |
||
522 | for (x = 0; x < info->width; x++) |
||
523 | { |
||
524 | t = 1; |
||
525 | for (k = 0; k < n; k++) |
||
526 | if (getcomp(sp, x * n + k, depth) != info->trns[k]) |
||
527 | t = 0; |
||
528 | if (t) |
||
529 | dp[x * dst->n + dst->n - 1] = 0; |
||
530 | } |
||
531 | } |
||
532 | } |
||
533 | |||
534 | int |
||
535 | xps_decode_png(fz_pixmap **imagep, byte *p, int total) |
||
536 | { |
||
537 | fz_pixmap *image; |
||
538 | fz_colorspace *colorspace; |
||
539 | struct info png; |
||
540 | int code; |
||
541 | int stride; |
||
542 | |||
543 | code = png_read_image(&png, p, total); |
||
544 | if (code) |
||
545 | return fz_rethrow(code, "cannot read png image"); |
||
546 | |||
547 | if (png.n == 3 || png.n == 4) |
||
548 | colorspace = fz_device_rgb; |
||
549 | else |
||
550 | colorspace = fz_device_gray; |
||
551 | |||
552 | stride = (png.width * png.n * png.depth + 7) / 8; |
||
553 | |||
554 | image = fz_new_pixmap_with_limit(colorspace, png.width, png.height); |
||
555 | if (!image) |
||
556 | { |
||
557 | fz_free(png.samples); |
||
558 | return fz_throw("out of memory"); |
||
559 | } |
||
560 | |||
561 | image->xres = png.xres; |
||
562 | image->yres = png.yres; |
||
563 | |||
564 | fz_unpack_tile(image, png.samples, png.n, png.depth, stride, png.indexed); |
||
565 | |||
566 | if (png.indexed) |
||
567 | image = png_expand_palette(&png, image); |
||
568 | else if (png.transparency) |
||
569 | png_mask_transparency(&png, image); |
||
570 | |||
571 | if (png.transparency || png.n == 2 || png.n == 4) |
||
572 | fz_premultiply_pixmap(image); |
||
573 | |||
574 | fz_free(png.samples); |
||
575 | |||
576 | *imagep = image; |
||
577 | return fz_okay; |
||
578 | }>>>><>>>>><>><>>>><>>><>>>>=>=>>>>>>>>>>>>>>>>>>>>=>=>=>>>>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><>><> |