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