1 // libpngrewrite.c - part of pngrewrite
2 // Copyright (C) 2010 by Jason Summers
3
4 #if defined(_WIN32) && !defined(__GNUC__) && !defined(PNGRW_WINDOWS)
5 #define PNGRW_WINDOWS
6 #endif
7
8 #ifdef PNGRW_WINDOWS
9 #include <tchar.h>
10 #include <strsafe.h>
11 #endif
12
13 #include <stdio.h>
14 #include <string.h>
15 #include <stdlib.h>
16 #include <stdarg.h>
17
18 #ifdef PNGRW_WINDOWS
19 #include <search.h> /* for qsort */
20 #endif
21
22 #include <png.h>
23
24 #include "libpngrewrite.h"
25
26 #ifdef PNGRW_WINDOWS
27 #define PNGRW_TEXT _T
28 #else
29 #define PNGRW_TEXT(x) x
30 #define _tfopen fopen
31 #define _ftprintf fprintf
32 #endif
33
34 #define PNGREWRITEVERSION PNGRW_TEXT("1.4.0")
35
36 struct errstruct {
37 jmp_buf jbuf;
38 PNGRW_CHAR errmsg[200];
39 };
40
41 struct pal_entry_info {
42 unsigned char red;
43 unsigned char green;
44 unsigned char blue;
45 unsigned char alpha;
46 unsigned int count;
47 };
48
49 struct pngrw_ctx {
50
51 pngrw_print_fn_type printfn;
52 pngrw_print_fn_type errorfn;
53 void *userdata;
54 int pal_sort_by_frequency;
55
56 struct pal_entry_info pal[256];
57 int pal_used;
58 int new_bit_depth;
59 int valid_gray;
60 int gray_trns_orig_palentry;
61 int gray_trns_target_palentry; // index into the grayscale fake palette
62 unsigned char gray_trns_shade;
63 struct pal_entry_info bkgd; /* RGB background color */
64 unsigned char bkgd_pal; /* new background color palette entry */
65 int ori_pal_size;
66
67 unsigned char *image1, *image2;
68 unsigned char **row_pointers1;
69 unsigned char **row_pointers2;
70 int rowbytes, channels;
71
72 png_uint_32 width, height;
73 int bit_depth, color_type, interlace_type;
74
75 int has_gAMA;
76 int has_bKGD;
77 int has_sRGB;
78 int has_tIME;
79 int has_pHYs;
80
81 png_time savechunk_time; /* S.A. */
82 double image_gamma;
83 int srgb_intent;
84 png_uint_32 res_x,res_y;
85 int res_unit_type;
86
87 int prev_entry;
88 int prev_entry_valid;
89 unsigned char prev_r;
90 unsigned char prev_g;
91 unsigned char prev_b;
92 unsigned char prev_a;
93
94 PNGRW_CHAR errmsg[200];
95 };
96
pngrw_StringCchCopy(PNGRW_CHAR * dst,int dst_len,const PNGRW_CHAR * src)97 static void pngrw_StringCchCopy(PNGRW_CHAR *dst, int dst_len, const PNGRW_CHAR *src)
98 {
99 #ifdef PNGRW_WINDOWS
100 StringCchCopy(dst,dst_len,src);
101 #else
102 strncpy(dst,src,dst_len);
103 dst[dst_len-1]='\0';
104 #endif
105 }
106
pngrw_StringCchVPrintf(PNGRW_CHAR * dst,int dst_len,const PNGRW_CHAR * fmt,va_list ap)107 static void pngrw_StringCchVPrintf(PNGRW_CHAR *dst, int dst_len, const PNGRW_CHAR *fmt, va_list ap)
108 {
109 #ifdef PNGRW_WINDOWS
110 StringCchVPrintf(dst,dst_len,fmt,ap);
111 #else
112 vsnprintf(dst,dst_len,fmt,ap);
113 dst[dst_len-1]='\0';
114 #endif
115 }
116
pngrw_StringCchPrintf(PNGRW_CHAR * dst,int dst_len,const PNGRW_CHAR * fmt,...)117 static void pngrw_StringCchPrintf(PNGRW_CHAR *dst, int dst_len, const PNGRW_CHAR *fmt, ...)
118 {
119 va_list ap;
120
121 va_start(ap, fmt);
122 pngrw_StringCchVPrintf(dst,dst_len,fmt,ap);
123 va_end(ap);
124 }
125
126
pngrw_print_error(struct pngrw_ctx * ctx,const PNGRW_CHAR * fmt,...)127 static void pngrw_print_error(struct pngrw_ctx *ctx, const PNGRW_CHAR *fmt, ...)
128 {
129 va_list ap;
130 PNGRW_CHAR buf[1000];
131
132 if(!ctx->errorfn) return;
133 va_start(ap, fmt);
134 pngrw_StringCchVPrintf(buf,1000,fmt,ap);
135 va_end(ap);
136 (*ctx->errorfn)(ctx,buf);
137 }
138
pngrw_print_info(struct pngrw_ctx * ctx,const PNGRW_CHAR * fmt,...)139 static void pngrw_print_info(struct pngrw_ctx *ctx, const PNGRW_CHAR *fmt, ...)
140 {
141 va_list ap;
142 PNGRW_CHAR buf[1000];
143
144 if(!ctx->printfn) return;
145 va_start(ap, fmt);
146 pngrw_StringCchVPrintf(buf,1000,fmt,ap);
147 va_end(ap);
148 (*ctx->printfn)(ctx,buf);
149 }
150
my_png_error_fn(png_structp png_ptr,const char * err_msg)151 static void my_png_error_fn(png_structp png_ptr, const char *err_msg)
152 {
153 struct errstruct *errinfop;
154
155 errinfop = (struct errstruct *)png_get_error_ptr(png_ptr);
156
157 pngrw_StringCchPrintf(errinfop->errmsg,200,PNGRW_TEXT("%s"),err_msg);
158
159 longjmp(errinfop->jbuf, -1);
160 }
161
my_png_warning_fn(png_structp png_ptr,const char * warn_msg)162 static void my_png_warning_fn(png_structp png_ptr, const char *warn_msg)
163 {
164 return;
165 }
166
pngrw_read_tIME_chunk(struct pngrw_ctx * ctx,png_structp png_ptr,png_infop info_ptr)167 static void pngrw_read_tIME_chunk(struct pngrw_ctx *ctx,
168 png_structp png_ptr, png_infop info_ptr)
169 {
170 png_timep in_time; /* a struct POINTER */
171
172 /* S.A. ................................. */
173 if(!ctx->has_tIME &&
174 png_get_valid(png_ptr,info_ptr,PNG_INFO_tIME))
175 {
176 if(png_get_tIME(png_ptr, info_ptr, &in_time) == PNG_INFO_tIME) {
177 ctx->savechunk_time = *in_time;
178 ctx->has_tIME = 1;
179 }
180 }
181 }
182
pngrw_record_ancillary_chunks_beforeimage(struct pngrw_ctx * ctx,png_structp png_ptr,png_infop info_ptr)183 static void pngrw_record_ancillary_chunks_beforeimage(struct pngrw_ctx *ctx,
184 png_structp png_ptr, png_infop info_ptr)
185 {
186 if (png_get_gAMA(png_ptr, info_ptr, &ctx->image_gamma)) {
187 ctx->has_gAMA=1;
188 }
189
190 if (png_get_sRGB(png_ptr, info_ptr, &ctx->srgb_intent)) {
191 ctx->has_sRGB=1;
192 }
193
194 if (png_get_valid(png_ptr,info_ptr,PNG_INFO_pHYs)) {
195 ctx->has_pHYs=1;
196 png_get_pHYs(png_ptr,info_ptr,&ctx->res_x,&ctx->res_y,&ctx->res_unit_type);
197 if(ctx->res_x<1 || ctx->res_y<1) ctx->has_pHYs=0;
198 }
199
200 pngrw_read_tIME_chunk(ctx,png_ptr,info_ptr);
201 }
202
pngrw_record_ancillary_chunks_afterimage(struct pngrw_ctx * ctx,png_structp png_ptr,png_infop info_ptr)203 static void pngrw_record_ancillary_chunks_afterimage(struct pngrw_ctx *ctx,
204 png_structp png_ptr, png_infop info_ptr)
205 {
206 pngrw_read_tIME_chunk(ctx,png_ptr,info_ptr);
207 }
208
pngrw_read_png(struct pngrw_ctx * ctx,FILE * infp)209 static int pngrw_read_png(struct pngrw_ctx *ctx, FILE *infp)
210 {
211 png_structp png_ptr = NULL;
212 png_infop info_ptr = NULL;
213 png_colorp ori_pal;
214 int ori_bpp;
215 struct errstruct errinfo;
216 int retval = 0;
217 int i;
218 png_color_16 *image_background;
219
220 pngrw_StringCchCopy(errinfo.errmsg,200,PNGRW_TEXT(""));
221
222 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
223 (void*)(&errinfo),my_png_error_fn, my_png_warning_fn);
224
225 if(!png_ptr) {
226 pngrw_print_error(ctx,PNGRW_TEXT("Error creating read_struct"));
227 goto done;
228 }
229
230 info_ptr = png_create_info_struct(png_ptr);
231 if(!info_ptr) {
232 pngrw_print_error(ctx,PNGRW_TEXT("Error creating read_info_struct"));
233 goto done;
234 }
235
236 if (setjmp(errinfo.jbuf)) {
237 pngrw_print_error(ctx,PNGRW_TEXT("PNG decode error: %s"), errinfo.errmsg);
238 goto done;
239 }
240
241 png_init_io(png_ptr, infp);
242
243 png_read_info(png_ptr, info_ptr);
244
245 png_get_IHDR(png_ptr, info_ptr, &ctx->width, &ctx->height, &ctx->bit_depth, &ctx->color_type,
246 &ctx->interlace_type, NULL, NULL);
247
248 if(ctx->bit_depth>8) {
249 // TODO: Some (rare?) 16-bpp images *can* be perfectly converted to palette images.
250 pngrw_print_error(ctx,PNGRW_TEXT("This image can't be converted because it has 16 bits/sample."));
251 goto done;
252 }
253
254
255 if (ctx->color_type == PNG_COLOR_TYPE_PALETTE) {
256 ctx->ori_pal_size=0;
257 ori_bpp = ctx->bit_depth;
258
259 /* we only do this to get the palette size so we can print it */
260 png_get_PLTE(png_ptr,info_ptr,&ori_pal,&ctx->ori_pal_size);
261
262 pngrw_print_info(ctx, PNGRW_TEXT("original palette size: %3d, %2d bpp"),ctx->ori_pal_size,ori_bpp);
263
264 png_set_expand(png_ptr);
265 }
266 else {
267 /* figure out bits per pixel so we can print it */
268 ori_bpp= ctx->bit_depth*png_get_channels(png_ptr,info_ptr);
269 pngrw_print_info(ctx, PNGRW_TEXT("original palette size: [n/a], %2d bpp"),ori_bpp);
270 }
271
272 if (ctx->color_type == PNG_COLOR_TYPE_GRAY && ctx->bit_depth < 8)
273 png_set_expand(png_ptr);
274 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
275 png_set_expand(png_ptr);
276 /* if (bit_depth == 16)
277 png_set_strip_16(png_ptr); */
278 if (ctx->color_type == PNG_COLOR_TYPE_GRAY ||
279 ctx->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
280 png_set_gray_to_rgb(png_ptr);
281 }
282
283 if (png_get_bKGD(png_ptr, info_ptr, &image_background)) {
284 ctx->has_bKGD=1;
285 ctx->bkgd.red= (unsigned char)image_background->red;
286 ctx->bkgd.green= (unsigned char)image_background->green;
287 ctx->bkgd.blue= (unsigned char)image_background->blue;
288 ctx->bkgd.alpha=255;
289 }
290
291 pngrw_record_ancillary_chunks_beforeimage(ctx,png_ptr,info_ptr);
292
293 png_read_update_info(png_ptr, info_ptr);
294
295 ctx->rowbytes=png_get_rowbytes(png_ptr, info_ptr);
296 ctx->channels=(int)png_get_channels(png_ptr, info_ptr);
297
298 if(ctx->channels<3 || ctx->channels>4) {
299 pngrw_print_error(ctx,PNGRW_TEXT("internal error: channels=%d"),ctx->channels);
300 goto done;
301 }
302
303 pngrw_print_info(ctx,PNGRW_TEXT("Image size is %dx%d memory required=%.3fMB"),
304 (int)ctx->width, (int)ctx->height,
305 (ctx->height*ctx->rowbytes + ctx->height*sizeof(unsigned char*) + ctx->width*ctx->height) / (1024. * 1024.) );
306
307 ctx->image1= (unsigned char*)malloc(ctx->height*ctx->rowbytes);
308 ctx->row_pointers1 = (unsigned char**)malloc(ctx->height * sizeof(unsigned char*));
309 if(!ctx->image1 || !ctx->row_pointers1) {
310 pngrw_print_error(ctx,PNGRW_TEXT("Unable to allocate memory for image"));
311 goto done;
312 }
313
314 for(i=0;i<(int)ctx->height;i++) {
315 ctx->row_pointers1[i] = &ctx->image1[ctx->rowbytes*i];
316 }
317
318 png_read_image(png_ptr, ctx->row_pointers1);
319
320 png_read_end(png_ptr, info_ptr);
321
322 pngrw_record_ancillary_chunks_afterimage(ctx,png_ptr,info_ptr);
323
324 retval = 1;
325
326 done:
327 if(png_ptr) {
328 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
329 }
330
331 return retval;
332 }
333
pngrw_write_ancillary_chunks(struct pngrw_ctx * ctx,png_structp png_ptr,png_infop info_ptr)334 static void pngrw_write_ancillary_chunks(struct pngrw_ctx *ctx,
335 png_structp png_ptr, png_infop info_ptr)
336 {
337 if(ctx->has_gAMA) png_set_gAMA(png_ptr, info_ptr, ctx->image_gamma);
338 if(ctx->has_sRGB) png_set_sRGB(png_ptr, info_ptr, ctx->srgb_intent);
339 if(ctx->has_pHYs) png_set_pHYs(png_ptr, info_ptr, ctx->res_x, ctx->res_y, ctx->res_unit_type);
340
341 /* S.A. ............................ */
342 if(ctx->has_tIME) {
343 png_set_tIME(png_ptr, info_ptr, &ctx->savechunk_time);
344 }
345 }
346
pngrw_write_new_png(struct pngrw_ctx * ctx,FILE * outfp)347 static int pngrw_write_new_png(struct pngrw_ctx *ctx, FILE *outfp)
348 {
349 png_structp png_ptr = NULL;
350 png_infop info_ptr = NULL;
351 png_color palette[256];
352 unsigned char trans[256];
353 int num_trans;
354 int i;
355 png_color_16 newtrns;
356 png_color_16 newbackground;
357 struct errstruct errinfo;
358 int retval = 0;
359
360 memset(&newtrns ,0,sizeof(png_color_16));
361 memset(&newbackground,0,sizeof(png_color_16));
362
363 pngrw_StringCchCopy(errinfo.errmsg,200,PNGRW_TEXT(""));
364
365 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
366 (void*)(&errinfo),my_png_error_fn, my_png_warning_fn);
367 if (!png_ptr) {
368 pngrw_print_error(ctx,PNGRW_TEXT("Error creating write_struct"));
369 goto done;
370 }
371
372 info_ptr = png_create_info_struct(png_ptr);
373 if (!info_ptr) {
374 pngrw_print_error(ctx,PNGRW_TEXT("Error creating write_info_struct"));
375 goto done;
376 }
377
378 if (setjmp(errinfo.jbuf)) {
379 pngrw_print_error(ctx,PNGRW_TEXT("PNG encode error: %s"), errinfo.errmsg);
380 goto done;
381 }
382
383 png_set_compression_level(png_ptr,9);
384 png_set_compression_buffer_size(png_ptr, 1024*1024);
385 png_init_io(png_ptr, outfp);
386
387 if( ctx->valid_gray ) {
388 png_set_IHDR(png_ptr, info_ptr, ctx->width, ctx->height, ctx->new_bit_depth,
389 PNG_COLOR_TYPE_GRAY, ctx->interlace_type,
390 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
391
392 if(ctx->gray_trns_target_palentry>=0) {
393 newtrns.gray = ctx->gray_trns_target_palentry;
394 png_set_tRNS(png_ptr, info_ptr, NULL, 1, &newtrns);
395 }
396
397 }
398 else {
399 png_set_IHDR(png_ptr, info_ptr, ctx->width, ctx->height, ctx->new_bit_depth,
400 PNG_COLOR_TYPE_PALETTE, ctx->interlace_type,
401 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
402
403 /* ... set palette colors ... */
404
405 num_trans=0; /* number of (fully or partially) transparent palette entries */
406
407 for(i=0;i<ctx->pal_used;i++) {
408 palette[i].red=ctx->pal[i].red;
409 palette[i].green=ctx->pal[i].green;
410 palette[i].blue=ctx->pal[i].blue;
411
412 trans[i]=ctx->pal[i].alpha;
413 if(trans[i]<255) num_trans=i+1;
414 }
415 png_set_PLTE(png_ptr, info_ptr, palette, ctx->pal_used);
416
417 if(num_trans>0) {
418 png_set_tRNS(png_ptr, info_ptr, trans, num_trans, 0);
419 }
420 }
421
422 if(ctx->has_bKGD) {
423 if(ctx->valid_gray)
424 newbackground.gray = ctx->bkgd_pal;
425 else
426 newbackground.index = ctx->bkgd_pal;
427 png_set_bKGD(png_ptr, info_ptr, &newbackground);
428 }
429
430 pngrw_write_ancillary_chunks(ctx,png_ptr,info_ptr);
431
432 png_write_info(png_ptr, info_ptr);
433
434 png_set_packing(png_ptr);
435
436 png_write_image(png_ptr, ctx->row_pointers2);
437
438 png_write_end(png_ptr, info_ptr);
439
440 retval = 1;
441
442 done:
443
444 if(png_ptr) {
445 png_destroy_write_struct(&png_ptr, &info_ptr);
446 }
447 return retval;
448 }
449
450
451 /* set ignorealpha to 1 if you don't care if the alpha value matches
452 * (for the background) */
453 static int
add_to_palette(struct pngrw_ctx * ctx,const struct pal_entry_info * color,int ignorealpha)454 add_to_palette(struct pngrw_ctx *ctx, const struct pal_entry_info *color, int ignorealpha)
455 {
456 if(ctx->pal_used>=256) {
457 pngrw_print_error(ctx,PNGRW_TEXT("This image can't be converted because it has more than 256 colors."));
458 return 0;
459 }
460 ctx->pal[ctx->pal_used].red = color->red;
461 ctx->pal[ctx->pal_used].green = color->green;
462 ctx->pal[ctx->pal_used].blue = color->blue;
463 ctx->pal[ctx->pal_used].alpha = ignorealpha?255:color->alpha;
464 ctx->pal[ctx->pal_used].count = 1;
465 ctx->pal_used++;
466 return 1;
467 }
468
469 #if 0
470 static void debug_print_pal(struct pngrw_ctx *ctx)
471 {
472 int i;
473 for(i=0;i<ctx->pal_used;i++) {
474 _ftprintf(stderr, PNGRW_TEXT("%d. %d %d %d %d\n"),i,
475 ctx->pal[i].red,ctx->pal[i].green,ctx->pal[i].blue,ctx->pal[i].alpha);
476 }
477 }
478 #endif
479
480
481 /* Sort the palette to put transparent colors first,
482 * then sort by how frequently the color is used. Sorting by frequency
483 * will often help the png filters make the image more compressible.
484 * It also makes it easier for people to see which colors aren't used much
485 * and allow them to manually reduce the color palette. */
palsortfunc_internal(const void * p1,const void * p2,int byfreq)486 static int palsortfunc_internal(const void* p1, const void* p2, int byfreq)
487 {
488 struct pal_entry_info *e1,*e2;
489 int s1,s2;
490
491 e1=(struct pal_entry_info*)p1;
492 e2=(struct pal_entry_info*)p2;
493
494 if(e1->alpha==255 && e2->alpha<255) return 1;
495 if(e1->alpha<255 && e2->alpha==255) return -1;
496
497 if(byfreq) {
498 if(e1->count<e2->count) return 1;
499 if(e1->count>e2->count) return -1;
500 }
501
502 s1=e1->red+e1->green+e1->blue;
503 s2=e2->red+e2->green+e2->blue;
504 if(s1>s2) return 1;
505 if(s1<s2) return -1;
506 return 0;
507 }
508
palsortfunc(const void * p1,const void * p2)509 static int palsortfunc(const void* p1, const void* p2)
510 {
511 return palsortfunc_internal(p1,p2,0);
512 }
513
palsortfunc_byfreq(const void * p1,const void * p2)514 static int palsortfunc_byfreq(const void* p1, const void* p2)
515 {
516 return palsortfunc_internal(p1,p2,1);
517 }
518
reset_pal_entry_cache(struct pngrw_ctx * ctx)519 static void reset_pal_entry_cache(struct pngrw_ctx *ctx)
520 {
521 ctx->prev_entry_valid = 0;
522 }
523
524 #define PNGRW_ALPHA_MUST_MATCH 0
525 #define PNGRW_IGNORE_ALPHA 1
526
find_pal_entry(struct pngrw_ctx * ctx,unsigned char r,unsigned char g,unsigned char b,unsigned char a,int ignorealpha)527 static int find_pal_entry(struct pngrw_ctx *ctx, unsigned char r,
528 unsigned char g, unsigned char b, unsigned char a, int ignorealpha)
529 {
530 int i;
531
532 if(ctx->prev_entry_valid && ctx->prev_r==r && ctx->prev_g==g &&
533 ctx->prev_b==b && ctx->prev_a==a)
534 {
535 return ctx->prev_entry;
536 }
537
538 for(i=0;i<ctx->pal_used;i++) {
539 if(ctx->pal[i].red==r && ctx->pal[i].green==g && ctx->pal[i].blue==b
540 && (ctx->pal[i].alpha==a || ignorealpha)) {
541
542 ctx->prev_r = r;
543 ctx->prev_g = g;
544 ctx->prev_b = b;
545 ctx->prev_a = a;
546 ctx->prev_entry = i;
547
548 return i;
549 }
550 }
551 return (-1);
552 }
553
554 // Same as find_pal_entry(), but with different parameters.
find_pal_entry_p(struct pngrw_ctx * ctx,struct pal_entry_info * e,int ignorealpha)555 static int find_pal_entry_p(struct pngrw_ctx *ctx, struct pal_entry_info *e,
556 int ignorealpha)
557 {
558 return find_pal_entry(ctx,e->red,e->green,e->blue,e->alpha,ignorealpha);
559 }
560
561 // Call this when encountering a pixel or background color.
562 // Adds the color to the palette if necessary, and does other
563 // bookkeeping.
pngrw_process_new_color(struct pngrw_ctx * ctx,struct pal_entry_info * e,int ignorealpha)564 static int pngrw_process_new_color(struct pngrw_ctx *ctx, struct pal_entry_info *e,
565 int ignorealpha)
566 {
567 int palent;
568
569 palent = find_pal_entry_p(ctx,e,ignorealpha);
570 if(palent<0) {
571 if(!add_to_palette(ctx,e,ignorealpha))
572 return 0;
573 }
574 else
575 ctx->pal[palent].count++;
576
577 return 1;
578 }
579
580 // Scans the pixels to figure out what colors are used, and
581 // adds the colors found to the target palette.
582 // Returns 0 on failure (e.g. too many colors for the palette).
pngrw_process_pixels(struct pngrw_ctx * ctx)583 static int pngrw_process_pixels(struct pngrw_ctx *ctx)
584 {
585 int x,y;
586 unsigned char *p;
587 struct pal_entry_info thispix;
588
589 for(y=0;y<(int)ctx->height;y++) {
590 for(x=0;x<(int)ctx->width;x++) {
591 p=&ctx->row_pointers1[y][x*ctx->channels];
592 thispix.red=p[0];
593 thispix.green=p[1];
594 thispix.blue=p[2];
595 if(ctx->channels==4) thispix.alpha=p[3];
596 else thispix.alpha=255;
597
598 if(!pngrw_process_new_color(ctx,&thispix,0)) return 0;
599 }
600 }
601 return 1;
602 }
603
pngrw_process_bkgd_color(struct pngrw_ctx * ctx)604 static int pngrw_process_bkgd_color(struct pngrw_ctx *ctx)
605 {
606 if(!ctx->has_bKGD) return 1;
607 if(!pngrw_process_new_color(ctx,&ctx->bkgd,1)) return 0;
608 return 1;
609 }
610
611 // Figure out if this is a valid grayscale image.
612 // If it is returns nonzero, and may set ctx->gray_trns_orig_palentry.
pngrw_is_valid_grayscale(struct pngrw_ctx * ctx)613 static int pngrw_is_valid_grayscale(struct pngrw_ctx *ctx)
614 {
615 int i;
616 unsigned char gray_shade; // Gray shade of the palette entry being examined.
617
618 ctx->gray_trns_orig_palentry = -1;
619
620 for( i = 0; i < ctx->pal_used; i++ ) {
621
622 if( ctx->pal[i].red != ctx->pal[i].green
623 || ctx->pal[i].red != ctx->pal[i].blue)
624 {
625 // Found a pixel that's not gray.
626 return 0;
627 }
628
629 gray_shade = ctx->pal[i].red;
630
631 // Check for partial transparency, which isn't supported in grayscale images
632 // (we don't support writing grayscale+alpha images).
633 if(ctx->pal[i].alpha!=255 && ctx->pal[i].alpha!=0) {
634 return 0;
635 }
636
637 // Check for binary transparency.
638 if(ctx->pal[i].alpha == 0) {
639 if(ctx->gray_trns_orig_palentry != -1) {
640 // Multiple transparent colors.
641 return 0;
642 }
643 ctx->gray_trns_orig_palentry = i; // binary transparency ok (so far)
644 ctx->gray_trns_shade = gray_shade;
645 }
646
647 // Check if the gray shade is one that can be represented at the
648 // target bit depth.
649 switch(ctx->new_bit_depth) {
650 case 1:
651 if(gray_shade%255) return 0;
652 break;
653 case 2:
654 if(gray_shade%85) return 0;
655 break;
656 case 4:
657 if(gray_shade%17) return 0;
658 }
659 }
660
661 // One thing the above doesn't check for is a nontransparent
662 // grayscale pixel that's the same as the transparent grayscale color.
663 // In this case we have to use palette color.
664 if(ctx->gray_trns_orig_palentry != -1) {
665 if(-1 != find_pal_entry(ctx,ctx->gray_trns_shade,ctx->gray_trns_shade,ctx->gray_trns_shade,255,PNGRW_ALPHA_MUST_MATCH)) {
666 return 0;
667 }
668 }
669 return 1;
670 }
671
pngrw_create_grayscale_palette(struct pngrw_ctx * ctx)672 static int pngrw_create_grayscale_palette(struct pngrw_ctx *ctx)
673 {
674 int i;
675 int step=1;
676
677 switch(ctx->new_bit_depth) {
678 case 1: step=255; ctx->pal_used=2; break;
679 case 2: step=85; ctx->pal_used=4; break;
680 case 4: step=17; ctx->pal_used=16; break;
681 default: step=1; ctx->pal_used=256;
682 }
683
684 for(i=0;i<ctx->pal_used;i++) {
685 ctx->pal[i].red = ctx->pal[i].green = ctx->pal[i].blue = i*step;
686 ctx->pal[i].alpha = 255;
687 }
688
689 // Translate the binary transparency setting to the new palette.
690 if(ctx->gray_trns_orig_palentry != -1) {
691 ctx->gray_trns_target_palentry=find_pal_entry(ctx,ctx->gray_trns_shade,ctx->gray_trns_shade,ctx->gray_trns_shade,255,PNGRW_ALPHA_MUST_MATCH);
692 if(ctx->gray_trns_target_palentry == -1) {
693 pngrw_print_error(ctx,PNGRW_TEXT("internal error: can't find transparent grayscale color"));
694 return 0;
695 }
696 ctx->pal[ctx->gray_trns_target_palentry].alpha = 0;
697 }
698
699 return 1;
700 }
701
702 // The core function that creates the new image.
703 // Allocates and writes to ctx->image2, and sets ctx->bkgd_pal.
pngrw_generate_optimized_image(struct pngrw_ctx * ctx)704 static int pngrw_generate_optimized_image(struct pngrw_ctx *ctx)
705 {
706 unsigned char *p;
707 int x,y;
708 int palent;
709
710 ctx->image2 = (unsigned char*)malloc(ctx->width*ctx->height);
711 ctx->row_pointers2 = (unsigned char**)malloc(ctx->height * sizeof(unsigned char*));
712 if(!ctx->image2 || !ctx->row_pointers2) {
713 pngrw_print_error(ctx,PNGRW_TEXT("out of memory"));
714 return 0;
715 }
716
717 for(y=0;y<(int)ctx->height;y++) {
718 ctx->row_pointers2[y]= &ctx->image2[y*ctx->width];
719 }
720
721 for(y=0;y<(int)ctx->height;y++) {
722 for(x=0;x<(int)ctx->width;x++) {
723 p=&ctx->row_pointers1[y][x*ctx->channels];
724
725 palent = find_pal_entry(ctx,p[0],p[1],p[2],(unsigned char)((ctx->channels==4)?p[3]:255),PNGRW_ALPHA_MUST_MATCH);
726 if(palent<0) {
727 pngrw_print_error(ctx,PNGRW_TEXT("internal error: can't locate palette entry"));
728 return 0;
729 }
730 ctx->row_pointers2[y][x] = (unsigned char)palent;
731 }
732 }
733
734 if(ctx->has_bKGD) {
735 palent = find_pal_entry_p(ctx,&ctx->bkgd,PNGRW_IGNORE_ALPHA);
736 if(palent<0) {
737 pngrw_print_error(ctx,PNGRW_TEXT("internal error: can't locate palette entry for bkgd"));
738 return 0;
739 }
740 ctx->bkgd_pal = (unsigned char)palent;
741 }
742
743 return 1;
744 }
pngrw_optimize_image(struct pngrw_ctx * ctx)745 int pngrw_optimize_image(struct pngrw_ctx *ctx)
746 {
747 ctx->gray_trns_target_palentry = -1;
748 ctx->gray_trns_shade=0;
749 ctx->pal_used=0;
750
751 if(!pngrw_process_pixels(ctx)) return 0;
752
753 if(!pngrw_process_bkgd_color(ctx)) return 0;
754
755 /* Decide on the target bit depth. */
756 ctx->new_bit_depth=8;
757 if(ctx->pal_used<=16) ctx->new_bit_depth=4;
758 if(ctx->pal_used<=4) ctx->new_bit_depth=2;
759 if(ctx->pal_used<=2) ctx->new_bit_depth=1;
760
761 /* figure out if this is a valid grayscale image */
762 ctx->valid_gray = pngrw_is_valid_grayscale(ctx);
763
764 if(ctx->valid_gray) {
765 // If grayscale, create a "fake" palette consisting of all
766 // available gray shades.
767 if(!pngrw_create_grayscale_palette(ctx)) return 0;
768
769 }
770 else {
771 // If color, put the palette in a good order.
772 qsort((void*)ctx->pal,ctx->pal_used,sizeof(struct pal_entry_info),
773 ctx->pal_sort_by_frequency?palsortfunc_byfreq:palsortfunc);
774 }
775
776 if(ctx->valid_gray)
777 pngrw_print_info(ctx, PNGRW_TEXT("saving as grayscale: %2d bpp"),ctx->new_bit_depth);
778 else
779 pngrw_print_info(ctx, PNGRW_TEXT("new palette size: %3d, %2d bpp"),ctx->pal_used,ctx->new_bit_depth);
780
781 reset_pal_entry_cache(ctx);
782
783 /* debug_print_pal(ctx); */
784
785 if(!pngrw_generate_optimized_image(ctx)) return 0;
786
787 return 1;
788 }
789
790 // Sets default values for everything.
init_ctx(struct pngrw_ctx * ctx)791 static void init_ctx(struct pngrw_ctx *ctx)
792 {
793 memset(ctx,0,sizeof(struct pngrw_ctx));
794 ctx->pal_sort_by_frequency=1;
795 }
796
free_ctx_contents(struct pngrw_ctx * ctx)797 static void free_ctx_contents(struct pngrw_ctx *ctx)
798 {
799 if(ctx->row_pointers1) free(ctx->row_pointers1);
800 if(ctx->row_pointers2) free(ctx->row_pointers2);
801 if(ctx->image1) free(ctx->image1);
802 if(ctx->image2) free(ctx->image2);
803 }
804
pngrw_create(void)805 struct pngrw_ctx *pngrw_create(void)
806 {
807 struct pngrw_ctx *ctx;
808 ctx = (struct pngrw_ctx*)calloc(sizeof(struct pngrw_ctx),1);
809 if(!ctx) return NULL;
810 init_ctx(ctx);
811 return ctx;
812 }
813
pngrw_destroy(struct pngrw_ctx * ctx)814 void pngrw_destroy(struct pngrw_ctx *ctx)
815 {
816 if(ctx) {
817 free_ctx_contents(ctx);
818 free(ctx);
819 }
820 }
821
pngrw_read_stdio(struct pngrw_ctx * ctx,FILE * infp)822 int pngrw_read_stdio(struct pngrw_ctx *ctx, FILE *infp)
823 {
824 return pngrw_read_png(ctx,infp);
825 }
826
pngrw_write_stdio(struct pngrw_ctx * ctx,FILE * outfp)827 int pngrw_write_stdio(struct pngrw_ctx *ctx, FILE *outfp)
828 {
829 return pngrw_write_new_png(ctx,outfp);
830 }
831
pngrw_read_filename(struct pngrw_ctx * ctx,const PNGRW_CHAR * in_filename)832 int pngrw_read_filename(struct pngrw_ctx *ctx, const PNGRW_CHAR *in_filename)
833 {
834 FILE *infp = NULL;
835 int ret;
836
837 infp = _tfopen(in_filename, PNGRW_TEXT("rb"));
838 if(!infp) {
839 pngrw_print_error(ctx,PNGRW_TEXT("Failed to open file for reading"));
840 return 0;
841 }
842
843 ret = pngrw_read_png(ctx,infp);
844 fclose(infp);
845 return ret;
846 }
847
pngrw_write_filename(struct pngrw_ctx * ctx,const PNGRW_CHAR * out_filename)848 int pngrw_write_filename(struct pngrw_ctx *ctx, const PNGRW_CHAR *out_filename)
849 {
850 FILE *outfp = NULL;
851 int ret;
852
853 outfp = _tfopen(out_filename, PNGRW_TEXT("wb"));
854 if(!outfp) {
855 pngrw_print_error(ctx,PNGRW_TEXT("Failed to open file for writing"));
856 return 0;
857 }
858
859 ret = pngrw_write_new_png(ctx,outfp);
860 fclose(outfp);
861 return ret;
862 }
863
pngrw_set_print_fn(struct pngrw_ctx * ctx,pngrw_print_fn_type prntfn)864 void pngrw_set_print_fn(struct pngrw_ctx *ctx, pngrw_print_fn_type prntfn)
865 {
866 ctx->printfn = prntfn;
867 }
868
pngrw_set_error_fn(struct pngrw_ctx * ctx,pngrw_print_fn_type errfn)869 void pngrw_set_error_fn(struct pngrw_ctx *ctx, pngrw_print_fn_type errfn)
870 {
871 ctx->errorfn = errfn;
872 }
873
pngrw_set_userdata(struct pngrw_ctx * ctx,void * u)874 void pngrw_set_userdata(struct pngrw_ctx *ctx, void *u)
875 {
876 ctx->userdata = u;
877 }
878
pngrw_get_userdata(struct pngrw_ctx * ctx)879 void *pngrw_get_userdata(struct pngrw_ctx *ctx)
880 {
881 return ctx->userdata;
882 }
883
pngrw_set_sort_by_frequency(struct pngrw_ctx * ctx,int n)884 void pngrw_set_sort_by_frequency(struct pngrw_ctx *ctx, int n)
885 {
886 ctx->pal_sort_by_frequency = n;
887 }
888
pngrw_get_version_string(void)889 const PNGRW_CHAR *pngrw_get_version_string(void)
890 {
891 return PNGREWRITEVERSION;
892 }
893