1 /*
2 * Author: William Chia-Wei Cheng (bill.cheng@acm.org)
3 *
4 * Copyright (C) 2001-2009, William Chia-Wei Cheng.
5 *
6 * This file may be distributed under the terms of the Q Public License
7 * as defined by Trolltech AS of Norway and appearing in the file
8 * LICENSE.QPL included in the packaging of this file.
9 *
10 * THIS FILE IS PROVIDED AS IS WITH NO WARRANTY OF ANY KIND, INCLUDING
11 * THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
12 * PURPOSE. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL,
13 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
14 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
15 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
16 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 *
18 * @(#)$Header: /mm2/home/cvs/bc-src/tgif/pngtrans.c,v 1.11 2011/05/16 16:21:59 william Exp $
19 */
20
21 #define _INCLUDE_FROM_PNGTRANS_C_
22
23 #include "tgifdefs.h"
24
25 #if (!defined(_NO_ZLIB) || defined(HAVE_LIBZ))
26 #include <zlib.h>
27 #endif /* (!defined(_NO_ZLIB) || defined(HAVE_LIBZ)) */
28
29 #include "dialog.e"
30 #include "pngtrans.e"
31 #include "msg.e"
32 #include "strtbl.e"
33 #include "util.e"
34
35 static int gnDebugPngParse=0;
36
37 static unsigned char pngHeader[8] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
38
39 static
PngShowOrPrintMsg(buf,use_msgbox)40 int PngShowOrPrintMsg(buf, use_msgbox)
41 char *buf;
42 int use_msgbox;
43 {
44 if (gnDebugPngParse > 0) {
45 return ShowOrPrintMsg(buf, use_msgbox);
46 }
47 return FALSE;
48 }
49
50 static
VerifyPngHeader(fp)51 int VerifyPngHeader(fp)
52 FILE *fp;
53 {
54 unsigned char buf[8];
55
56 if (fread(buf, sizeof(char), 8, fp) != 8) {
57 strcpy(gszMsgBox, TgLoadString(STID_BADPNG_SHORT_HEADER));
58 return PngShowOrPrintMsg(gszMsgBox, TRUE);
59 }
60 if (memcmp(pngHeader, buf, 8) != 0) {
61 strcpy(gszMsgBox, TgLoadString(STID_BADPNG_BAD_HEADER));
62 return PngShowOrPrintMsg(gszMsgBox, TRUE);
63 }
64 return TRUE;
65 }
66
67 static
ReadPngDWord(pvalue32,fp)68 int ReadPngDWord(pvalue32, fp)
69 uint32_t *pvalue32;
70 FILE *fp;
71 {
72 unsigned char buf[4];
73 uint32_t value32=(uint32_t)0;
74 int bytes_read=fread(buf, sizeof(char), 4, fp);
75
76 if (bytes_read != 4) {
77 return bytes_read;
78 }
79 value32 += (uint32_t)(buf[0]);
80 value32 <<= 8;
81 value32 += (uint32_t)(buf[1]);
82 value32 <<= 8;
83 value32 += (uint32_t)(buf[2]);
84 value32 <<= 8;
85 value32 += (uint32_t)(buf[3]);
86
87 *pvalue32 = value32;
88
89 return 4;
90 }
91
92 static
ReadPngShortWord(pvalue16,fp)93 int ReadPngShortWord(pvalue16, fp)
94 uint16_t *pvalue16;
95 FILE *fp;
96 {
97 unsigned char buf[2];
98 uint16_t value16=(uint16_t)0;
99 int bytes_read=fread(buf, sizeof(char), 2, fp);
100
101 if (bytes_read != 2) {
102 return bytes_read;
103 }
104 value16 += (uint16_t)(buf[0]);
105 value16 <<= 8;
106 value16 += (uint16_t)(buf[1]);
107
108 *pvalue16 = value16;
109
110 return 2;
111 }
112
113 static
PngHexDump(buf,len,pn_index,pn_line_num)114 void PngHexDump(buf, len, pn_index, pn_line_num)
115 unsigned char *buf;
116 unsigned int len;
117 int *pn_index, *pn_line_num;
118 {
119 int index=(*pn_index), line_num=(*pn_line_num);
120
121 while (len > 0) {
122 if (index == 0) {
123 fprintf(stdout, "%06x: ", line_num<<4);
124 }
125 fprintf(stdout, "%02x ", (int)(unsigned int)(*buf));
126 index++;
127 if (index == 8) {
128 fprintf(stdout, " ");
129 } else if (index == 16) {
130 index = 0;
131 line_num++;
132 fprintf(stdout, "\n");
133 }
134 len--;
135 buf++;
136 }
137 *pn_index = index;
138 *pn_line_num = line_num;
139 }
140
141 static
DoInflateForPng(in_buf,v32_length)142 int DoInflateForPng(in_buf, v32_length)
143 unsigned char *in_buf;
144 uint32_t v32_length;
145 {
146 #if (!defined(_NO_ZLIB) || defined(HAVE_LIBZ))
147 int ret=0, has_left_over=FALSE, index=0, line_num=0;
148 int bytes_left=(int)(unsigned int)v32_length;
149 unsigned int block_sz=0x4000;
150 unsigned int have=0;
151 z_stream strm;
152 unsigned char in[0x4000], out[0x4000];
153
154 /* allocate inflate state */
155 memset(&strm, 0, sizeof(strm));
156 strm.zalloc = Z_NULL;
157 strm.zfree = Z_NULL;
158 strm.opaque = Z_NULL;
159 strm.avail_in = 0;
160 strm.next_in = Z_NULL;
161 ret = inflateInit(&strm);
162 if (ret != Z_OK) {
163 return FALSE;
164 }
165 do {
166 int bytes_to_read=min(block_sz, bytes_left);
167
168 if (bytes_to_read == 0) break;
169 memcpy(in, in_buf, bytes_to_read);
170
171 bytes_left -= bytes_to_read;
172 in_buf += bytes_to_read;
173
174 strm.avail_in = bytes_to_read;
175 strm.next_in = in;
176
177 /* run inflate() on input until output buffer not full */
178 do {
179 strm.avail_out = block_sz;
180 strm.next_out = out;
181 ret = inflate(&strm, Z_NO_FLUSH);
182 if (ret == Z_STREAM_ERROR) return FALSE;
183
184 switch (ret) {
185 case Z_NEED_DICT:
186 ret = Z_DATA_ERROR; /* and fall through */
187 case Z_DATA_ERROR:
188 case Z_MEM_ERROR:
189 (void)inflateEnd(&strm);
190 return FALSE;
191 }
192 have = block_sz - strm.avail_out;
193
194 if (gnDebugPngParse > 1) PngHexDump(out, have, &index, &line_num);
195 } while (strm.avail_out == 0);
196
197 /* done when inflate() says it's done */
198 } while (ret != Z_STREAM_END);
199
200 if (has_left_over) {
201 (void)inflateEnd(&strm);
202 return FALSE;
203 }
204 /* clean up and return */
205 inflateEnd(&strm);
206
207 if (ret == Z_STREAM_END) {
208 return TRUE;
209 }
210 return FALSE;
211 #else /* ~(!defined(_NO_ZLIB) || defined(HAVE_LIBZ)) */
212 return FALSE;
213 #endif /* (!defined(_NO_ZLIB) || defined(HAVE_LIBZ)) */
214 }
215
216 static
SetPngFileTransColor(pphi,red,green,blue)217 void SetPngFileTransColor(pphi, red, green, blue)
218 PngHeaderInfo *pphi;
219 unsigned int red, green, blue;
220 {
221 pphi->trans_color_pixel_found = TRUE;
222 pphi->trans_color_pixel_red = (unsigned char)(red & 0x00ff);
223 pphi->trans_color_pixel_green = (unsigned char)(green & 0x00ff);
224 pphi->trans_color_pixel_blue = (unsigned char)(blue & 0x00ff);
225 }
226
227 static
ReadChunk(pphi,psz_chunk_type,v32_length,fp)228 int ReadChunk(pphi, psz_chunk_type, v32_length, fp)
229 PngHeaderInfo *pphi;
230 char *psz_chunk_type;
231 uint32_t v32_length;
232 FILE *fp;
233 {
234 int i=0;
235 uint32_t bytes_read=0, bytes_left=v32_length;
236 uint16_t v16=(uint16_t)0;
237
238 if (gnDebugPngParse > 0) {
239 /* debug, do not translate */
240 fprintf(stdout, "%s: length = %1d (0x%08x)\n", psz_chunk_type,
241 (int)(unsigned int)v32_length, (int)(unsigned int)v32_length);
242 fflush(stdout);
243 }
244 if (strncmp(psz_chunk_type, "IHDR", 4) == 0) {
245 if (v32_length == 13) {
246 if ((bytes_read=ReadPngDWord(&pphi->image_w, fp)) != 4) return FALSE;
247 if ((bytes_read=ReadPngDWord(&pphi->image_h, fp)) != 4) return FALSE;
248 if ((bytes_read=fread(&pphi->bit_depth, sizeof(char), 1, fp)) != 1) return FALSE;
249 if ((bytes_read=fread(&pphi->color_type, sizeof(char), 1, fp)) != 1) return FALSE;
250 if ((bytes_read=fread(&pphi->compression_method, sizeof(char), 1, fp)) != 1) return FALSE;
251 if ((bytes_read=fread(&pphi->filter_method, sizeof(char), 1, fp)) != 1) return FALSE;
252 if ((bytes_read=fread(&pphi->interlace_method, sizeof(char), 1, fp)) != 1) return FALSE;
253
254 pphi->valid = TRUE;
255
256 if (gnDebugPngParse > 0) {
257 /* debug, do not translate */
258 fprintf(stdout, "Width = %1d (0x%08x), Height = %1d (0x%08x), Color type = %1d, Bit depth = %1d, Compression = %1d, Filter = %1d, Interlace = %1d\n",
259 (int)(unsigned int)pphi->image_w,
260 (int)(unsigned int)pphi->image_w,
261 (int)(unsigned int)pphi->image_h,
262 (int)(unsigned int)pphi->image_h,
263 (int)(unsigned int)pphi->color_type,
264 (int)(unsigned int)pphi->bit_depth,
265 (int)(unsigned int)pphi->compression_method,
266 (int)(unsigned int)pphi->filter_method,
267 (int)(unsigned int)pphi->interlace_method);
268 }
269 switch (pphi->color_type) {
270 case 0: break; /* Greyscale, allowed bit depths are 1, 2, 4, 8, 16 */
271 case 2: break; /* Truecolor, allowed bit depths are 8, 16 */
272 case 3: break; /* Indexed-colour, allowed bit depths are 1, 2, 4, 8 */
273
274 case 4: /* don't know how to handle Greyscale with alpha */
275 case 6: /* don't know how to handle Truecolour with alpha */
276 if (gnDebugPngParse > 0) {
277 /* debug, do not translate */
278 fprintf(stdout, "Transparent color type unsupported.\n");
279 }
280 return FALSE;
281 }
282 return TRUE;
283 } else {
284 if (gnDebugPngParse > 0) {
285 /* debug, do not translate */
286 fprintf(stdout, "Unexpected IHDR chunk size: %1d (0x%08x).\n",
287 (int)(unsigned int)v32_length, (int)(unsigned int)v32_length);
288 }
289 strcpy(gszMsgBox, TgLoadString(STID_BADPNG_BAD_HEADER));
290 return PngShowOrPrintMsg(gszMsgBox, TRUE);
291 }
292 } else if (strncmp(psz_chunk_type, "IDAT", 4) == 0) {
293 if (pphi->valid && pphi->compression_method == 0) {
294 if (gnDebugPngParse > 0) {
295 unsigned char *buf=(unsigned char *)malloc(v32_length);
296
297 if (buf == NULL) FailAllocMessage();
298 if ((bytes_read=fread(buf, sizeof(char), v32_length, fp)) != v32_length) return FALSE;
299 if (!DoInflateForPng(buf, v32_length)) {
300 /* debug, do not translate */
301 fprintf(stdout, "DoInflateForPng() failed.\n");
302 fflush(stdout);
303 return FALSE;
304 }
305 free(buf);
306 return TRUE;
307 }
308 }
309 } else if (strncmp(psz_chunk_type, "PLTE", 4) == 0) {
310 if (pphi->valid && v32_length % 3 == 0) {
311 int num_entries=v32_length/3;
312 unsigned char *psz=NULL;
313
314 pphi->palette = (unsigned char *)malloc(v32_length);
315 if (pphi->palette == NULL) FailAllocMessage();
316 if ((bytes_read=fread(pphi->palette, sizeof(char), v32_length, fp)) != v32_length) return FALSE;
317 pphi->palette_sz = v32_length;
318
319 if (gnDebugPngParse > 0) {
320 psz = pphi->palette;
321 for (i=0; i < num_entries; i++) {
322 /* debug, do not translate */
323 fprintf(stdout, "%04d: %02x%02x%02x\n", i, (int)psz[0], (int)psz[1], (int)psz[2]);
324 psz = &psz[3];
325 }
326 }
327 return TRUE;
328 } else {
329 strcpy(gszMsgBox, TgLoadString(STID_BADPNG_BAD_PALETTE_LEN));
330 return PngShowOrPrintMsg(gszMsgBox, TRUE);
331 }
332 } else if (strncmp(psz_chunk_type, "tRNS", 4) == 0) {
333 if (pphi->valid && pphi->color_type == 0 && v32_length == 2) {
334 /* Greyscale, allowed bit depths are 1, 2, 4, 8, 16 */
335 if ((bytes_read=ReadPngShortWord(&v16, fp)) != 2) return FALSE;
336 switch (pphi->bit_depth) {
337 case 1: v16 = ((v16 & 0x0001) == 0x0001) ? 0x00ff : 0; break;
338 case 2:
339 v16 = v16 & 0x0003;
340 v16 = v16 | ((v16<<2)&0x000c) | ((v16<<4)&0x0030) | ((v16<<6)&0x00c0);
341 break;
342 case 4:
343 v16 = v16 & 0x000f;
344 v16 = v16 | ((v16<<4)&0x00f0);
345 break;
346 case 8: v16 = v16 & 0x00ff; break;
347 case 16: v16 = (v16>>8) & 0x00ff; break;
348 default: return FALSE;
349 }
350 SetPngFileTransColor(pphi, (unsigned int)v16, (unsigned int)v16, (unsigned int)v16);
351 return TRUE;
352 } else if (pphi->valid && pphi->color_type == 2 && v32_length == 6) {
353 /* Truecolor, allowed bit depths are 8, 16 */
354 uint16_t v16r=(uint16_t)0, v16g=(uint16_t)0, v16b=(uint16_t)0;
355
356 if ((bytes_read=ReadPngShortWord(&v16r, fp)) != 2) return FALSE;
357 if ((bytes_read=ReadPngShortWord(&v16b, fp)) != 2) return FALSE;
358 if ((bytes_read=ReadPngShortWord(&v16g, fp)) != 2) return FALSE;
359 switch (pphi->bit_depth) {
360 case 8:
361 v16r = v16r & 0x00ff;
362 v16g = v16g & 0x00ff;
363 v16b = v16b & 0x00ff;
364 break;
365 case 16: v16 = (v16>>8) & 0x00ff; break;
366 default: return FALSE;
367 }
368 SetPngFileTransColor(pphi, (unsigned int)v16r, (unsigned int)v16g, (unsigned int)v16b);
369 return TRUE;
370 } else if (pphi->valid && pphi->color_type == 3 && v32_length == 1) {
371 /* Indexed-colour, allowed bit depths are 1, 2, 4, 8 */
372 unsigned char palette_index='\0', *psz=NULL;
373
374 if ((bytes_read=fread(&palette_index, sizeof(char), 1, fp)) != 1) return FALSE;
375 if (pphi->palette == NULL || palette_index >= pphi->palette_sz) return FALSE;
376 psz = &pphi->palette[palette_index*3];
377 /*
378 * the bit_depth is used to control the number of entries in the
379 * palette, not the number of bits in RGB
380 */
381 switch (pphi->bit_depth) {
382 case 1: break;
383 case 2: break;
384 case 4: break;
385 case 8: break;
386 default: return FALSE;
387 }
388 SetPngFileTransColor(pphi, (unsigned int)psz[0], (unsigned int)psz[1], (unsigned int)psz[2]);
389 return TRUE;
390 } else {
391 if (gnDebugPngParse > 0) {
392 /* debug, do not translate */
393 fprintf(stdout, "Transparent color type unsupported.\n");
394 }
395 return FALSE;
396 }
397 }
398 if (fseek(fp, (long)bytes_left, SEEK_CUR) != 0) {
399 return FALSE;
400 }
401 return TRUE;
402 }
403
404 static
ProcessChunk(pphi)405 int ProcessChunk(pphi)
406 PngHeaderInfo *pphi;
407 {
408 uint32_t v32_length=(uint32_t)0, v32_crc=(uint32_t)0;
409 int bytes_read=0;
410 char sz_chunk_type[5];
411 FILE *fp=pphi->fp;
412
413 if ((bytes_read=ReadPngDWord(&v32_length, fp)) == 0) return FALSE;
414 if (bytes_read != 4) {
415 sprintf(gszMsgBox, TgLoadString(STID_BADPNG_BAD_CHUNK_LEN), bytes_read);
416 return PngShowOrPrintMsg(gszMsgBox, TRUE);
417 }
418 if ((bytes_read=fread(sz_chunk_type, sizeof(unsigned char), 4, fp)) != 4) {
419 strcpy(gszMsgBox, TgLoadString(STID_BADPNG_SHORT_CHUNK_TYPE));
420 return PngShowOrPrintMsg(gszMsgBox, TRUE);
421 }
422 sz_chunk_type[4] = '\0';
423 if (!ReadChunk(pphi, sz_chunk_type, v32_length, fp)) return FALSE;
424
425 if ((bytes_read=ReadPngDWord(&v32_crc, fp)) != 4) {
426 strcpy(gszMsgBox, TgLoadString(STID_BADPNG_SHORT_CRC));
427 return PngShowOrPrintMsg(gszMsgBox, TRUE);
428 }
429 return TRUE;
430 }
431
432 /* ----------------------- ParseTransColor() ----------------------- */
433
ParseTransColor(char * buf,unsigned char * puch_trans_color_pixel_r,unsigned char * puch_trans_color_pixel_g,unsigned char * puch_trans_color_pixel_b)434 int ParseTransColor(char *buf, unsigned char *puch_trans_color_pixel_r,
435 unsigned char *puch_trans_color_pixel_g,
436 unsigned char *puch_trans_color_pixel_b)
437 {
438 char buf_r[5], buf_g[5], buf_b[5];
439 int r=0, g=0, b=0;
440
441 if (*buf != '#' || strlen(buf) != 7) {
442 return FALSE;
443 }
444 buf_r[0] = '0';
445 buf_r[1] = '0';
446 buf_r[2] = buf[1];
447 buf_r[3] = buf[2];
448 buf_r[4] = '\0';
449
450 buf_g[0] = '0';
451 buf_g[1] = '0';
452 buf_g[2] = buf[3];
453 buf_g[3] = buf[4];
454 buf_g[4] = '\0';
455
456 buf_b[0] = '0';
457 buf_b[1] = '0';
458 buf_b[2] = buf[5];
459 buf_b[3] = buf[6];
460 buf_b[4] = '\0';
461
462 if (sscanf(buf_r, "%x", &r) != 1) return FALSE;
463 if (sscanf(buf_g, "%x", &g) != 1) return FALSE;
464 if (sscanf(buf_b, "%x", &b) != 1) return FALSE;
465
466 *puch_trans_color_pixel_r = (unsigned char)(r & 0x0ff);
467 *puch_trans_color_pixel_g = (unsigned char)(g & 0x0ff);
468 *puch_trans_color_pixel_b = (unsigned char)(b & 0x0ff);
469
470 return TRUE;
471 }
472
473 /* ----------------------- ResetPngHeaderInfo() ----------------------- */
474
ResetPngHeaderInfo(pphi)475 void ResetPngHeaderInfo(pphi)
476 PngHeaderInfo *pphi;
477 {
478 memset(pphi, 0, sizeof(PngHeaderInfo));
479 }
480
481 /* ----------------------- SetPngHeaderInfoForTransColor() ----------------------- */
482
SetPngHeaderInfoForTransColor(pphi,trans_color_pixel_r,trans_color_pixel_g,trans_color_pixel_b)483 void SetPngHeaderInfoForTransColor(pphi, trans_color_pixel_r,
484 trans_color_pixel_g, trans_color_pixel_b)
485 PngHeaderInfo *pphi;
486 unsigned char trans_color_pixel_r, trans_color_pixel_g, trans_color_pixel_b;
487 {
488 ResetPngHeaderInfo(pphi);
489
490 pphi->valid = TRUE;
491 pphi->trans_color_pixel_found = TRUE;
492 pphi->trans_color_pixel_red = trans_color_pixel_r;
493 pphi->trans_color_pixel_green = trans_color_pixel_g;
494 pphi->trans_color_pixel_blue = trans_color_pixel_b;
495 }
496
497 /* ----------------------- PngFileGetTransColorInit() ----------------------- */
498
PngFileGetTransColorInit(pphi,fname)499 int PngFileGetTransColorInit(pphi, fname)
500 PngHeaderInfo *pphi;
501 char *fname;
502 {
503 ResetPngHeaderInfo(pphi);
504
505 pphi->fp = fopen(fname, "r");
506 if (pphi->fp == NULL) return FailToOpenMessage(fname, "r", NULL);
507 UtilStrCpyN(pphi->fname, sizeof(pphi->fname), fname);
508
509 return TRUE;
510 }
511
512 /* ----------------------- PngFileGetTransColor() ----------------------- */
513
PngFileGetTransColor(pphi)514 int PngFileGetTransColor(pphi)
515 PngHeaderInfo *pphi;
516 /*
517 * returns TRUE if transparent pixel found
518 * returns FALSE if there is a problem parsing the PNG file or if it does not contain transparent pixels
519 */
520 {
521 if (!VerifyPngHeader(pphi->fp)) {
522 if (pphi->fp != NULL) fclose(pphi->fp);
523 ResetPngHeaderInfo(pphi);
524 return FALSE;
525 }
526 while (ProcessChunk(pphi)) ;
527
528 if (pphi->fp != NULL) {
529 fclose(pphi->fp);
530 pphi->fp = NULL;
531 }
532 if (pphi->valid && pphi->palette_sz > 0 && pphi->palette != NULL) {
533 free(pphi->palette);
534 }
535 pphi->palette = NULL;
536
537 return (pphi->valid && pphi->trans_color_pixel_found);
538 }
539
540