1 /*
2 * opngreduc.c - libpng extension: lossless image reductions.
3 *
4 * Copyright (C) 2003-2014 Cosmin Truta.
5 * This software is distributed under the same licensing and warranty terms
6 * as libpng.
7 */
8
9 /* CAUTION:
10 * Image reductions do not work well under certain transformations.
11 *
12 * Transformations like PNG_BGR, PNG_SWAP_BYTES, PNG_FILLER, PNG_INVERT_ALPHA,
13 * and possibly others, require special treatment. However, the libpng API
14 * does not currently convey the effect of transformations on its internal
15 * state or on the layout of pixel data.
16 *
17 * Transformations which affect pixel depth (e.g. PNG_FILLER) are especially
18 * dangerous when used in conjunction with this code, and should be avoided.
19 */
20
21 #include "opngreduc.h"
22
23 #include <string.h>
24
25 #ifndef OPNG_ASSERT
26 #include <assert.h>
27 #define OPNG_ASSERT(cond) assert(cond)
28 #define OPNG_ASSERT_MSG(cond, msg) assert(cond)
29 #endif
30
31 #ifdef png_debug
32 #define opng_debug(level, msg) png_debug(level, msg)
33 #else
34 #define opng_debug(level, msg) ((void)0)
35 #endif
36
37
38 #ifdef PNG_INFO_IMAGE_SUPPORTED
39
40 /*
41 * Check if the image information is valid.
42 * The image information is said to be valid if all the required
43 * critical chunk data is present in the png structures.
44 * The function returns 1 if this information is valid, and 0 otherwise.
45 */
46 int PNGAPI
opng_validate_image(png_structp png_ptr,png_infop info_ptr)47 opng_validate_image(png_structp png_ptr, png_infop info_ptr)
48 {
49 opng_debug(1, "in opng_validate_image");
50
51 /* Validate IHDR. */
52 if (png_get_bit_depth(png_ptr, info_ptr) == 0)
53 return 0;
54
55 /* Validate PLTE. */
56 if (png_get_color_type(png_ptr, info_ptr) & PNG_COLOR_MASK_PALETTE)
57 {
58 if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_PLTE))
59 return 0;
60 }
61
62 /* Validate IDAT. */
63 if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_IDAT))
64 return 0;
65
66 return 1;
67 }
68
69 #endif /* PNG_INFO_IMAGE_SUPPORTED */
70
71
72 #ifdef OPNG_IMAGE_REDUCTIONS_SUPPORTED
73
74 #define OPNG_CMP_RGB(R1, G1, B1, R2, G2, B2) \
75 (((int)(R1) != (int)(R2)) ? \
76 ((int)(R1) - (int)(R2)) : \
77 (((int)(G1) != (int)(G2)) ? \
78 ((int)(G1) - (int)(G2)) : \
79 ((int)(B1) - (int)(B2))))
80
81 #define OPNG_CMP_ARGB(A1, R1, G1, B1, A2, R2, G2, B2) \
82 (((int)(A1) != (int)(A2)) ? \
83 ((int)(A1) - (int)(A2)) : \
84 (((int)(R1) != (R2)) ? \
85 ((int)(R1) - (int)(R2)) : \
86 (((int)(G1) != (int)(G2)) ? \
87 ((int)(G1) - (int)(G2)) : \
88 ((int)(B1) - (int)(B2)))))
89
90 /*
91 * Build a color+alpha palette in which the entries are sorted by
92 * (alpha, red, green, blue), in this particular order.
93 * Use the insertion sort algorithm.
94 * The alpha value is ignored if it is not in the range [0 .. 255].
95 * The function returns:
96 * 1 if the insertion is successful; *index = position of new entry.
97 * 0 if the insertion is unnecessary; *index = position of crt entry.
98 * -1 if overflow; *num_palette = *num_trans = *index = -1.
99 */
100 static int /* PRIVATE */
opng_insert_palette_entry(png_colorp palette,int * num_palette,png_bytep trans_alpha,int * num_trans,int max_tuples,unsigned int red,unsigned int green,unsigned int blue,unsigned int alpha,int * index)101 opng_insert_palette_entry(png_colorp palette, int *num_palette,
102 png_bytep trans_alpha, int *num_trans, int max_tuples,
103 unsigned int red, unsigned int green, unsigned int blue, unsigned int alpha,
104 int *index)
105 {
106 int low, high, mid, cmp;
107 int i;
108
109 OPNG_ASSERT(*num_palette >= 0 && *num_palette <= max_tuples);
110 OPNG_ASSERT(*num_trans >= 0 && *num_trans <= *num_palette);
111
112 if (alpha < 255)
113 {
114 /* Do a binary search among transparent tuples. */
115 low = 0;
116 high = *num_trans - 1;
117 while (low <= high)
118 {
119 mid = (low + high) / 2;
120 cmp = OPNG_CMP_ARGB(alpha, red, green, blue,
121 trans_alpha[mid],
122 palette[mid].red, palette[mid].green, palette[mid].blue);
123 if (cmp < 0)
124 high = mid - 1;
125 else if (cmp > 0)
126 low = mid + 1;
127 else
128 {
129 *index = mid;
130 return 0;
131 }
132 }
133 }
134 else /* alpha == 255 || alpha not in [0 .. 255] */
135 {
136 /* Do a (faster) binary search among opaque tuples. */
137 low = *num_trans;
138 high = *num_palette - 1;
139 while (low <= high)
140 {
141 mid = (low + high) / 2;
142 cmp = OPNG_CMP_RGB(red, green, blue,
143 palette[mid].red, palette[mid].green, palette[mid].blue);
144 if (cmp < 0)
145 high = mid - 1;
146 else if (cmp > 0)
147 low = mid + 1;
148 else
149 {
150 *index = mid;
151 return 0;
152 }
153 }
154 }
155 if (alpha > 255)
156 {
157 /* The binary search among opaque tuples has failed. */
158 /* Do a linear search among transparent tuples, ignoring alpha. */
159 for (i = 0; i < *num_trans; ++i)
160 {
161 cmp = OPNG_CMP_RGB(red, green, blue,
162 palette[i].red, palette[i].green, palette[i].blue);
163 if (cmp == 0)
164 {
165 *index = i;
166 return 0;
167 }
168 }
169 }
170
171 /* Check for overflow. */
172 if (*num_palette >= max_tuples)
173 {
174 *num_palette = *num_trans = *index = -1;
175 return -1;
176 }
177
178 /* Insert new tuple at [low]. */
179 OPNG_ASSERT(low >= 0 && low <= *num_palette);
180 for (i = *num_palette; i > low; --i)
181 palette[i] = palette[i - 1];
182 palette[low].red = (png_byte)red;
183 palette[low].green = (png_byte)green;
184 palette[low].blue = (png_byte)blue;
185 ++(*num_palette);
186 if (alpha < 255)
187 {
188 OPNG_ASSERT(low <= *num_trans);
189 for (i = *num_trans; i > low; --i)
190 trans_alpha[i] = trans_alpha[i - 1];
191 trans_alpha[low] = (png_byte)alpha;
192 ++(*num_trans);
193 }
194 *index = low;
195 return 1;
196 }
197
198 /*
199 * Change the size of the palette buffer.
200 * Changing info_ptr->num_palette directly, avoiding reallocation, should
201 * have been sufficient, but can't be done using the current libpng API.
202 */
203 static void /* PRIVATE */
opng_realloc_PLTE(png_structp png_ptr,png_infop info_ptr,int num_palette)204 opng_realloc_PLTE(png_structp png_ptr, png_infop info_ptr, int num_palette)
205 {
206 png_color buffer[PNG_MAX_PALETTE_LENGTH];
207 png_colorp palette;
208 int src_num_palette;
209
210 opng_debug(1, "in opng_realloc_PLTE");
211
212 OPNG_ASSERT(num_palette > 0);
213 src_num_palette = 0;
214 png_get_PLTE(png_ptr, info_ptr, &palette, &src_num_palette);
215 if (num_palette == src_num_palette)
216 return;
217 memcpy(buffer, palette, num_palette * sizeof(png_color));
218 if (num_palette > src_num_palette)
219 memset(buffer + src_num_palette, 0,
220 (num_palette - src_num_palette) * sizeof(png_color));
221 png_set_PLTE(png_ptr, info_ptr, buffer, num_palette);
222 }
223
224 /*
225 * Change the size of the transparency buffer.
226 * Changing info_ptr->num_trans directly, avoiding reallocation, should
227 * have been sufficient, but can't be done using the current libpng API.
228 */
229 static void /* PRIVATE */
opng_realloc_tRNS(png_structp png_ptr,png_infop info_ptr,int num_trans)230 opng_realloc_tRNS(png_structp png_ptr, png_infop info_ptr, int num_trans)
231 {
232 png_byte buffer[PNG_MAX_PALETTE_LENGTH];
233 png_bytep trans_alpha;
234 int src_num_trans;
235
236 opng_debug(1, "in opng_realloc_tRNS");
237
238 OPNG_ASSERT(num_trans > 0); /* tRNS should be invalidated in this case */
239 src_num_trans = 0;
240 png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &src_num_trans, NULL);
241 if (num_trans == src_num_trans)
242 return;
243 memcpy(buffer, trans_alpha, (size_t)num_trans);
244 if (num_trans > src_num_trans)
245 memset(buffer + src_num_trans, 0, num_trans - src_num_trans);
246 png_set_tRNS(png_ptr, info_ptr, buffer, num_trans, NULL);
247 }
248
249 /*
250 * Retrieve the alpha samples from the given image row.
251 */
252 static void /* PRIVATE */
opng_get_alpha_row(png_row_infop row_info_ptr,png_color_16p trans_color,png_bytep row,png_bytep alpha_row)253 opng_get_alpha_row(png_row_infop row_info_ptr, png_color_16p trans_color,
254 png_bytep row, png_bytep alpha_row)
255 {
256 png_bytep sample_ptr;
257 png_uint_32 width;
258 int color_type, bit_depth, channels;
259 png_byte trans_red, trans_green, trans_blue, trans_gray;
260 png_uint_32 i;
261
262 width = row_info_ptr->width;
263 color_type = row_info_ptr->color_type;
264 bit_depth = row_info_ptr->bit_depth;
265 channels = row_info_ptr->channels;
266
267 OPNG_ASSERT(!(color_type & PNG_COLOR_MASK_PALETTE));
268 OPNG_ASSERT(bit_depth == 8);
269
270 if (!(color_type & PNG_COLOR_MASK_ALPHA))
271 {
272 if (trans_color == NULL)
273 {
274 /* All pixels are fully opaque. */
275 memset(alpha_row, 255, (size_t)width);
276 return;
277 }
278 if (color_type == PNG_COLOR_TYPE_RGB)
279 {
280 OPNG_ASSERT(channels == 3);
281 trans_red = (png_byte)trans_color->red;
282 trans_green = (png_byte)trans_color->green;
283 trans_blue = (png_byte)trans_color->blue;
284 sample_ptr = row;
285 for (i = 0; i < width; ++i, sample_ptr += 3)
286 alpha_row[i] = (png_byte)
287 ((sample_ptr[0] == trans_red &&
288 sample_ptr[1] == trans_green &&
289 sample_ptr[2] == trans_blue) ? 0 : 255);
290 }
291 else
292 {
293 OPNG_ASSERT(color_type == PNG_COLOR_TYPE_GRAY);
294 OPNG_ASSERT(channels == 1);
295 trans_gray = (png_byte)trans_color->gray;
296 for (i = 0; i < width; ++i)
297 alpha_row[i] = (png_byte)((row[i] == trans_gray) ? 0 : 255);
298 }
299 return;
300 }
301
302 /* There is a real alpha channel. The alpha sample is last in RGBA tuple. */
303 OPNG_ASSERT(channels > 1);
304 sample_ptr = row + (channels - 1);
305 for (i = 0; i < width; ++i, sample_ptr += channels, ++alpha_row)
306 *alpha_row = *sample_ptr;
307 }
308
309 /*
310 * Analyze the redundancy of bits inside the image.
311 * The parameter reductions indicates the intended reductions.
312 * The function returns the possible reductions.
313 */
314 static png_uint_32 /* PRIVATE */
opng_analyze_bits(png_structp png_ptr,png_infop info_ptr,png_uint_32 reductions)315 opng_analyze_bits(png_structp png_ptr, png_infop info_ptr,
316 png_uint_32 reductions)
317 {
318 png_bytepp row_ptr;
319 png_bytep component_ptr;
320 png_uint_32 height, width;
321 int bit_depth, color_type, byte_depth, channels, sample_size, offset_alpha;
322 #ifdef PNG_bKGD_SUPPORTED
323 png_color_16p background;
324 #endif
325 png_uint_32 i, j;
326
327 opng_debug(1, "in opng_analyze_bits");
328
329 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
330 NULL, NULL, NULL);
331 if (bit_depth < 8)
332 return OPNG_REDUCE_NONE; /* not applicable */
333 if (color_type & PNG_COLOR_MASK_PALETTE)
334 return OPNG_REDUCE_NONE; /* let opng_reduce_palette() handle it */
335
336 byte_depth = bit_depth / 8;
337 channels = png_get_channels(png_ptr, info_ptr);
338 sample_size = channels * byte_depth;
339 offset_alpha = (channels - 1) * byte_depth;
340
341 /* Select the applicable reductions. */
342 reductions &= (OPNG_REDUCE_16_TO_8 |
343 OPNG_REDUCE_RGB_TO_GRAY | OPNG_REDUCE_STRIP_ALPHA);
344 if (bit_depth <= 8)
345 reductions &= ~OPNG_REDUCE_16_TO_8;
346 if (!(color_type & PNG_COLOR_MASK_COLOR))
347 reductions &= ~OPNG_REDUCE_RGB_TO_GRAY;
348 if (!(color_type & PNG_COLOR_MASK_ALPHA))
349 reductions &= ~OPNG_REDUCE_STRIP_ALPHA;
350
351 /* Check if the ancillary information allows these reductions. */
352 #ifdef PNG_bKGD_SUPPORTED
353 if (png_get_bKGD(png_ptr, info_ptr, &background))
354 {
355 if (reductions & OPNG_REDUCE_16_TO_8)
356 {
357 if (background->red % 257 != 0 ||
358 background->green % 257 != 0 ||
359 background->blue % 257 != 0 ||
360 background->gray % 257 != 0)
361 reductions &= ~OPNG_REDUCE_16_TO_8;
362 }
363 if (reductions & OPNG_REDUCE_RGB_TO_GRAY)
364 {
365 if (background->red != background->green ||
366 background->red != background->blue)
367 reductions &= ~OPNG_REDUCE_RGB_TO_GRAY;
368 }
369 }
370 #endif
371
372 /* Check for each possible reduction, row by row. */
373 row_ptr = png_get_rows(png_ptr, info_ptr);
374 for (i = 0; i < height; ++i, ++row_ptr)
375 {
376 if (reductions == OPNG_REDUCE_NONE)
377 return OPNG_REDUCE_NONE; /* no need to go any further */
378
379 /* Check if it is possible to reduce the bit depth to 8. */
380 if (reductions & OPNG_REDUCE_16_TO_8)
381 {
382 component_ptr = *row_ptr;
383 for (j = 0; j < channels * width; ++j, component_ptr += 2)
384 {
385 if (component_ptr[0] != component_ptr[1])
386 {
387 reductions &= ~OPNG_REDUCE_16_TO_8;
388 break;
389 }
390 }
391 }
392
393 if (bit_depth == 8)
394 {
395 /* Check if it is possible to reduce rgb --> gray. */
396 if (reductions & OPNG_REDUCE_RGB_TO_GRAY)
397 {
398 component_ptr = *row_ptr;
399 for (j = 0; j < width; ++j, component_ptr += sample_size)
400 {
401 if (component_ptr[0] != component_ptr[1] ||
402 component_ptr[0] != component_ptr[2])
403 {
404 reductions &= ~OPNG_REDUCE_RGB_TO_GRAY;
405 break;
406 }
407 }
408 }
409
410 /* Check if it is possible to strip the alpha channel. */
411 if (reductions & OPNG_REDUCE_STRIP_ALPHA)
412 {
413 component_ptr = *row_ptr + offset_alpha;
414 for (j = 0; j < width; ++j, component_ptr += sample_size)
415 {
416 if (component_ptr[0] != 255)
417 {
418 reductions &= ~OPNG_REDUCE_STRIP_ALPHA;
419 break;
420 }
421 }
422 }
423 }
424 else /* bit_depth == 16 */
425 {
426 /* Check if it is possible to reduce rgb --> gray. */
427 if (reductions & OPNG_REDUCE_RGB_TO_GRAY)
428 {
429 component_ptr = *row_ptr;
430 for (j = 0; j < width; ++j, component_ptr += sample_size)
431 {
432 if (component_ptr[0] != component_ptr[2] ||
433 component_ptr[0] != component_ptr[4] ||
434 component_ptr[1] != component_ptr[3] ||
435 component_ptr[1] != component_ptr[5])
436 {
437 reductions &= ~OPNG_REDUCE_RGB_TO_GRAY;
438 break;
439 }
440 }
441 }
442
443 /* Check if it is possible to strip the alpha channel. */
444 if (reductions & OPNG_REDUCE_STRIP_ALPHA)
445 {
446 component_ptr = *row_ptr + offset_alpha;
447 for (j = 0; j < width; ++j, component_ptr += sample_size)
448 {
449 if (component_ptr[0] != 255 || component_ptr[1] != 255)
450 {
451 reductions &= ~OPNG_REDUCE_STRIP_ALPHA;
452 break;
453 }
454 }
455 }
456 }
457 }
458
459 return reductions;
460 }
461
462 /*
463 * Reduce the image type to a lower bit depth and color type,
464 * by removing redundant bits.
465 * Possible reductions: 16bpp to 8bpp; RGB to gray; strip alpha.
466 * The parameter reductions indicates the intended reductions.
467 * The function returns the successful reductions.
468 * All reductions are performed in a single step.
469 */
470 static png_uint_32 /* PRIVATE */
opng_reduce_bits(png_structp png_ptr,png_infop info_ptr,png_uint_32 reductions)471 opng_reduce_bits(png_structp png_ptr, png_infop info_ptr,
472 png_uint_32 reductions)
473 {
474 png_bytepp row_ptr;
475 png_bytep src_ptr, dest_ptr;
476 png_uint_32 width, height;
477 int interlace_type, compression_type, filter_type;
478 int src_bit_depth, dest_bit_depth;
479 int src_byte_depth, dest_byte_depth;
480 int src_color_type, dest_color_type;
481 int src_channels, dest_channels;
482 int src_sample_size, dest_sample_size;
483 int tran_tbl[8];
484 png_color_16p trans_color;
485 #ifdef PNG_bKGD_SUPPORTED
486 png_color_16p background;
487 #endif
488 #ifdef PNG_sBIT_SUPPORTED
489 png_color_8p sig_bits;
490 #endif
491 png_uint_32 i, j;
492 int k;
493
494 opng_debug(1, "in opng_reduce_bits");
495
496 /* See which reductions may be performed. */
497 reductions = opng_analyze_bits(png_ptr, info_ptr, reductions);
498 if (reductions == OPNG_REDUCE_NONE)
499 return OPNG_REDUCE_NONE; /* exit early */
500
501 png_get_IHDR(png_ptr, info_ptr, &width, &height,
502 &src_bit_depth, &src_color_type,
503 &interlace_type, &compression_type, &filter_type);
504
505 /* Compute the new image parameters bit_depth, color_type, etc. */
506 OPNG_ASSERT(src_bit_depth >= 8);
507 if (reductions & OPNG_REDUCE_16_TO_8)
508 {
509 OPNG_ASSERT(src_bit_depth == 16);
510 dest_bit_depth = 8;
511 }
512 else
513 dest_bit_depth = src_bit_depth;
514
515 src_byte_depth = src_bit_depth / 8;
516 dest_byte_depth = dest_bit_depth / 8;
517
518 dest_color_type = src_color_type;
519 if (reductions & OPNG_REDUCE_RGB_TO_GRAY)
520 {
521 OPNG_ASSERT(src_color_type & PNG_COLOR_MASK_COLOR);
522 dest_color_type &= ~PNG_COLOR_MASK_COLOR;
523 }
524 if (reductions & OPNG_REDUCE_STRIP_ALPHA)
525 {
526 OPNG_ASSERT(src_color_type & PNG_COLOR_MASK_ALPHA);
527 dest_color_type &= ~PNG_COLOR_MASK_ALPHA;
528 }
529
530 src_channels = png_get_channels(png_ptr, info_ptr);
531 dest_channels =
532 ((dest_color_type & PNG_COLOR_MASK_COLOR) ? 3 : 1) +
533 ((dest_color_type & PNG_COLOR_MASK_ALPHA) ? 1 : 0);
534
535 src_sample_size = src_channels * src_byte_depth;
536 dest_sample_size = dest_channels * dest_byte_depth;
537
538 /* Pre-compute the intra-sample translation table. */
539 for (k = 0; k < 4 * dest_byte_depth; ++k)
540 tran_tbl[k] = k * src_bit_depth / dest_bit_depth;
541 /* If rgb --> gray, shift the alpha component two positions to the left. */
542 if ((reductions & OPNG_REDUCE_RGB_TO_GRAY) &&
543 (dest_color_type & PNG_COLOR_MASK_ALPHA))
544 {
545 tran_tbl[dest_byte_depth] = tran_tbl[3 * dest_byte_depth];
546 if (dest_byte_depth == 2)
547 tran_tbl[dest_byte_depth + 1] = tran_tbl[3 * dest_byte_depth + 1];
548 }
549
550 /* Translate the samples to the new image type. */
551 OPNG_ASSERT(src_sample_size > dest_sample_size);
552 row_ptr = png_get_rows(png_ptr, info_ptr);
553 for (i = 0; i < height; ++i, ++row_ptr)
554 {
555 src_ptr = dest_ptr = *row_ptr;
556 for (j = 0; j < width; ++j)
557 {
558 for (k = 0; k < dest_sample_size; ++k)
559 dest_ptr[k] = src_ptr[tran_tbl[k]];
560 src_ptr += src_sample_size;
561 dest_ptr += dest_sample_size;
562 }
563 }
564
565 /* Update the ancillary information. */
566 if (png_get_tRNS(png_ptr, info_ptr, NULL, NULL, &trans_color))
567 {
568 if (reductions & OPNG_REDUCE_16_TO_8)
569 {
570 if (trans_color->red % 257 == 0 &&
571 trans_color->green % 257 == 0 &&
572 trans_color->blue % 257 == 0 &&
573 trans_color->gray % 257 == 0)
574 {
575 trans_color->red &= 255;
576 trans_color->green &= 255;
577 trans_color->blue &= 255;
578 trans_color->gray &= 255;
579 }
580 else
581 {
582 /* 16-bit tRNS in 8-bit samples: all pixels are 100% opaque. */
583 png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, -1);
584 png_set_invalid(png_ptr, info_ptr, PNG_INFO_tRNS);
585 }
586 }
587 if (reductions & OPNG_REDUCE_RGB_TO_GRAY)
588 {
589 if (trans_color->red == trans_color->green ||
590 trans_color->red == trans_color->blue)
591 trans_color->gray = trans_color->red;
592 else
593 {
594 /* Non-gray tRNS in grayscale image: all pixels are 100% opaque. */
595 png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, -1);
596 png_set_invalid(png_ptr, info_ptr, PNG_INFO_tRNS);
597 }
598 }
599 }
600 #ifdef PNG_bKGD_SUPPORTED
601 if (png_get_bKGD(png_ptr, info_ptr, &background))
602 {
603 if (reductions & OPNG_REDUCE_16_TO_8)
604 {
605 background->red &= 255;
606 background->green &= 255;
607 background->blue &= 255;
608 background->gray &= 255;
609 }
610 if (reductions & OPNG_REDUCE_RGB_TO_GRAY)
611 background->gray = background->red;
612 }
613 #endif
614 #ifdef PNG_sBIT_SUPPORTED
615 if (png_get_sBIT(png_ptr, info_ptr, &sig_bits))
616 {
617 if (reductions & OPNG_REDUCE_16_TO_8)
618 {
619 if (sig_bits->red > 8)
620 sig_bits->red = 8;
621 if (sig_bits->green > 8)
622 sig_bits->green = 8;
623 if (sig_bits->blue > 8)
624 sig_bits->blue = 8;
625 if (sig_bits->gray > 8)
626 sig_bits->gray = 8;
627 if (sig_bits->alpha > 8)
628 sig_bits->alpha = 8;
629 }
630 if (reductions & OPNG_REDUCE_RGB_TO_GRAY)
631 {
632 png_byte max_sig_bits = sig_bits->red;
633 if (max_sig_bits < sig_bits->green)
634 max_sig_bits = sig_bits->green;
635 if (max_sig_bits < sig_bits->blue)
636 max_sig_bits = sig_bits->blue;
637 sig_bits->gray = max_sig_bits;
638 }
639 }
640 #endif
641
642 /* Update the image information. */
643 png_set_IHDR(png_ptr, info_ptr, width, height,
644 dest_bit_depth, dest_color_type,
645 interlace_type, compression_type, filter_type);
646
647 return reductions;
648 }
649
650 /*
651 * Reduce the bit depth of a palette image to the lowest possible value.
652 * The parameter reductions should contain OPNG_REDUCE_8_TO_4_2_1.
653 * The function returns OPNG_REDUCE_8_TO_4_2_1 if successful.
654 */
655 static png_uint_32 /* PRIVATE */
opng_reduce_palette_bits(png_structp png_ptr,png_infop info_ptr,png_uint_32 reductions)656 opng_reduce_palette_bits(png_structp png_ptr, png_infop info_ptr,
657 png_uint_32 reductions)
658 {
659 png_bytepp row_ptr;
660 png_bytep src_sample_ptr, dest_sample_ptr;
661 png_uint_32 width, height;
662 int color_type, interlace_type, compression_type, filter_type;
663 int src_bit_depth, dest_bit_depth;
664 unsigned int src_mask_init, src_mask, src_shift, dest_shift;
665 unsigned int sample, dest_buf;
666 png_colorp palette;
667 int num_palette;
668 png_uint_32 i, j;
669
670 opng_debug(1, "in opng_reduce_palette_bits");
671
672 /* Check if the reduction applies. */
673 if (!(reductions & OPNG_REDUCE_8_TO_4_2_1))
674 return OPNG_REDUCE_NONE;
675 png_get_IHDR(png_ptr, info_ptr, &width, &height, &src_bit_depth,
676 &color_type, &interlace_type, &compression_type, &filter_type);
677 if (color_type != PNG_COLOR_TYPE_PALETTE)
678 return OPNG_REDUCE_NONE;
679 if (!png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette))
680 num_palette = 0;
681
682 /* Find the smallest possible bit depth. */
683 if (num_palette > 16)
684 return OPNG_REDUCE_NONE;
685 else if (num_palette > 4) /* 5 .. 16 entries */
686 dest_bit_depth = 4;
687 else if (num_palette > 2) /* 3 or 4 entries */
688 dest_bit_depth = 2;
689 else /* 1 or 2 entries */
690 {
691 OPNG_ASSERT(num_palette > 0);
692 dest_bit_depth = 1;
693 }
694
695 if (src_bit_depth <= dest_bit_depth)
696 {
697 OPNG_ASSERT(src_bit_depth == dest_bit_depth);
698 return OPNG_REDUCE_NONE;
699 }
700
701 /* Iterate through all sample values. */
702 row_ptr = png_get_rows(png_ptr, info_ptr);
703 if (src_bit_depth == 8)
704 {
705 for (i = 0; i < height; ++i, ++row_ptr)
706 {
707 src_sample_ptr = dest_sample_ptr = *row_ptr;
708 dest_shift = 8;
709 dest_buf = 0;
710 for (j = 0; j < width; ++j)
711 {
712 dest_shift -= dest_bit_depth;
713 if (dest_shift > 0)
714 dest_buf |= *src_sample_ptr << dest_shift;
715 else
716 {
717 *dest_sample_ptr++ = (png_byte)(dest_buf | *src_sample_ptr);
718 dest_shift = 8;
719 dest_buf = 0;
720 }
721 ++src_sample_ptr;
722 }
723 if (dest_shift != 0)
724 *dest_sample_ptr = (png_byte)dest_buf;
725 }
726 }
727 else /* src_bit_depth < 8 */
728 {
729 src_mask_init = (1 << (8 + src_bit_depth)) - (1 << 8);
730 for (i = 0; i < height; ++i, ++row_ptr)
731 {
732 src_sample_ptr = dest_sample_ptr = *row_ptr;
733 src_shift = dest_shift = 8;
734 src_mask = src_mask_init;
735 dest_buf = 0;
736 for (j = 0; j < width; ++j)
737 {
738 src_shift -= src_bit_depth;
739 src_mask >>= src_bit_depth;
740 sample = (*src_sample_ptr & src_mask) >> src_shift;
741 dest_shift -= dest_bit_depth;
742 if (dest_shift > 0)
743 dest_buf |= sample << dest_shift;
744 else
745 {
746 *dest_sample_ptr++ = (png_byte)(dest_buf | sample);
747 dest_shift = 8;
748 dest_buf = 0;
749 }
750 if (src_shift == 0)
751 {
752 src_shift = 8;
753 src_mask = src_mask_init;
754 ++src_sample_ptr;
755 }
756 }
757 if (dest_shift != 0)
758 *dest_sample_ptr = (png_byte)dest_buf;
759 }
760 }
761
762 /* Update the image information. */
763 png_set_IHDR(png_ptr, info_ptr, width, height, dest_bit_depth,
764 color_type, interlace_type, compression_type, filter_type);
765 return OPNG_REDUCE_8_TO_4_2_1;
766 }
767
768 /*
769 * Reduce the image type from grayscale(+alpha) or RGB(+alpha) to palette,
770 * if possible.
771 * The parameter reductions indicates the intended reductions.
772 * The function returns the successful reductions.
773 */
774 static png_uint_32 /* PRIVATE */
opng_reduce_to_palette(png_structp png_ptr,png_infop info_ptr,png_uint_32 reductions)775 opng_reduce_to_palette(png_structp png_ptr, png_infop info_ptr,
776 png_uint_32 reductions)
777 {
778 png_uint_32 result;
779 png_row_info row_info;
780 png_bytepp row_ptr;
781 png_bytep sample_ptr, alpha_row;
782 png_uint_32 height, width;
783 int color_type, interlace_type, compression_type, filter_type;
784 int src_bit_depth, dest_bit_depth, channels;
785 png_color palette[256];
786 png_byte trans_alpha[256];
787 png_color_16p trans_color;
788 int num_palette, num_trans, index;
789 unsigned int gray, red, green, blue, alpha;
790 unsigned int prev_gray, prev_red, prev_green, prev_blue, prev_alpha;
791 #ifdef PNG_bKGD_SUPPORTED
792 png_color_16p background;
793 #endif
794 png_uint_32 i, j;
795
796 opng_debug(1, "in opng_reduce_to_palette");
797
798 png_get_IHDR(png_ptr, info_ptr, &width, &height, &src_bit_depth,
799 &color_type, &interlace_type, &compression_type, &filter_type);
800 if (src_bit_depth != 8)
801 return OPNG_REDUCE_NONE; /* nothing is done in this case */
802 OPNG_ASSERT(!(color_type & PNG_COLOR_MASK_PALETTE));
803
804 row_ptr = png_get_rows(png_ptr, info_ptr);
805 channels = png_get_channels(png_ptr, info_ptr);
806 alpha_row = (png_bytep)png_malloc(png_ptr, width);
807
808 row_info.width = width;
809 row_info.rowbytes = 0; /* not used */
810 row_info.color_type = (png_byte)color_type;
811 row_info.bit_depth = (png_byte)src_bit_depth;
812 row_info.channels = (png_byte)channels;
813 row_info.pixel_depth = 0; /* not used */
814
815 /* Analyze the possibility of this reduction. */
816 num_palette = num_trans = 0;
817 trans_color = NULL;
818 png_get_tRNS(png_ptr, info_ptr, NULL, NULL, &trans_color);
819 prev_gray = prev_red = prev_green = prev_blue = prev_alpha = 256;
820 for (i = 0; i < height; ++i, ++row_ptr)
821 {
822 sample_ptr = *row_ptr;
823 opng_get_alpha_row(&row_info, trans_color, *row_ptr, alpha_row);
824 if (color_type & PNG_COLOR_MASK_COLOR)
825 {
826 for (j = 0; j < width; ++j, sample_ptr += channels)
827 {
828 red = sample_ptr[0];
829 green = sample_ptr[1];
830 blue = sample_ptr[2];
831 alpha = alpha_row[j];
832 /* Check the cache first. */
833 if (red != prev_red || green != prev_green || blue != prev_blue ||
834 alpha != prev_alpha)
835 {
836 prev_red = red;
837 prev_green = green;
838 prev_blue = blue;
839 prev_alpha = alpha;
840 if (opng_insert_palette_entry(palette, &num_palette,
841 trans_alpha, &num_trans, 256,
842 red, green, blue, alpha, &index) < 0) /* overflow */
843 {
844 OPNG_ASSERT(num_palette < 0);
845 i = height; /* forced exit from outer loop */
846 break;
847 }
848 }
849 }
850 }
851 else /* grayscale */
852 {
853 for (j = 0; j < width; ++j, sample_ptr += channels)
854 {
855 gray = sample_ptr[0];
856 alpha = alpha_row[j];
857 /* Check the cache first. */
858 if (gray != prev_gray || alpha != prev_alpha)
859 {
860 prev_gray = gray;
861 prev_alpha = alpha;
862 if (opng_insert_palette_entry(palette, &num_palette,
863 trans_alpha, &num_trans, 256,
864 gray, gray, gray, alpha, &index) < 0) /* overflow */
865 {
866 OPNG_ASSERT(num_palette < 0);
867 i = height; /* forced exit from outer loop */
868 break;
869 }
870 }
871 }
872 }
873 }
874 #ifdef PNG_bKGD_SUPPORTED
875 if ((num_palette >= 0) && png_get_bKGD(png_ptr, info_ptr, &background))
876 {
877 /* bKGD has an alpha-agnostic palette entry. */
878 if (color_type & PNG_COLOR_MASK_COLOR)
879 {
880 red = background->red;
881 green = background->green;
882 blue = background->blue;
883 }
884 else
885 red = green = blue = background->gray;
886 opng_insert_palette_entry(palette, &num_palette,
887 trans_alpha, &num_trans, 256,
888 red, green, blue, 256, &index);
889 if (index >= 0)
890 background->index = (png_byte)index;
891 }
892 #endif
893
894 /* Continue only if the uncompressed indexed image (pixels + PLTE + tRNS)
895 * is smaller than the uncompressed RGB(A) image.
896 * Casual overhead (headers, CRCs, etc.) is ignored.
897 *
898 * Compare:
899 * num_pixels * (src_bit_depth * channels - dest_bit_depth) / 8
900 * vs.
901 * sizeof(PLTE) + sizeof(tRNS)
902 */
903 if (num_palette >= 0)
904 {
905 OPNG_ASSERT(num_palette > 0 && num_palette <= 256);
906 OPNG_ASSERT(num_trans >= 0 && num_trans <= num_palette);
907 if (num_palette <= 2)
908 dest_bit_depth = 1;
909 else if (num_palette <= 4)
910 dest_bit_depth = 2;
911 else if (num_palette <= 16)
912 dest_bit_depth = 4;
913 else
914 dest_bit_depth = 8;
915 /* Do the comparison in a way that does not cause overflow. */
916 if (channels * 8 == dest_bit_depth ||
917 (3 * num_palette + num_trans) * 8 / (channels * 8 - dest_bit_depth)
918 / width / height >= 1)
919 num_palette = -1;
920 }
921
922 if (num_palette < 0) /* can't reduce */
923 {
924 png_free(png_ptr, alpha_row);
925 return OPNG_REDUCE_NONE;
926 }
927
928 /* Reduce. */
929 row_ptr = png_get_rows(png_ptr, info_ptr);
930 index = -1;
931 prev_red = prev_green = prev_blue = prev_alpha = (unsigned int)(-1);
932 for (i = 0; i < height; ++i, ++row_ptr)
933 {
934 sample_ptr = *row_ptr;
935 opng_get_alpha_row(&row_info, trans_color, *row_ptr, alpha_row);
936 if (color_type & PNG_COLOR_MASK_COLOR)
937 {
938 for (j = 0; j < width; ++j, sample_ptr += channels)
939 {
940 red = sample_ptr[0];
941 green = sample_ptr[1];
942 blue = sample_ptr[2];
943 alpha = alpha_row[j];
944 /* Check the cache first. */
945 if (red != prev_red || green != prev_green || blue != prev_blue ||
946 alpha != prev_alpha)
947 {
948 prev_red = red;
949 prev_green = green;
950 prev_blue = blue;
951 prev_alpha = alpha;
952 if (opng_insert_palette_entry(palette, &num_palette,
953 trans_alpha, &num_trans, 256,
954 red, green, blue, alpha, &index) != 0)
955 index = -1; /* this should not happen */
956 }
957 OPNG_ASSERT(index >= 0);
958 (*row_ptr)[j] = (png_byte)index;
959 }
960 }
961 else /* grayscale */
962 {
963 for (j = 0; j < width; ++j, sample_ptr += channels)
964 {
965 gray = sample_ptr[0];
966 alpha = alpha_row[j];
967 /* Check the cache first. */
968 if (gray != prev_gray || alpha != prev_alpha)
969 {
970 prev_gray = gray;
971 prev_alpha = alpha;
972 if (opng_insert_palette_entry(palette, &num_palette,
973 trans_alpha, &num_trans, 256,
974 gray, gray, gray, alpha, &index) != 0)
975 index = -1; /* this should not happen */
976 }
977 OPNG_ASSERT(index >= 0);
978 (*row_ptr)[j] = (png_byte)index;
979 }
980 }
981 }
982
983 /* Update the image information. */
984 png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_PALETTE,
985 interlace_type, compression_type, filter_type);
986 png_set_PLTE(png_ptr, info_ptr, palette, num_palette);
987 if (num_trans > 0)
988 png_set_tRNS(png_ptr, info_ptr, trans_alpha, num_trans, NULL);
989 /* bKGD (if present) is automatically updated. */
990
991 png_free(png_ptr, alpha_row);
992
993 result = OPNG_REDUCE_RGB_TO_PALETTE;
994 if (reductions & OPNG_REDUCE_8_TO_4_2_1)
995 result |= opng_reduce_palette_bits(png_ptr, info_ptr, reductions);
996 return result;
997 }
998
999 /*
1000 * Analyze the usage of samples.
1001 * The output value usage_map[n] indicates whether the sample n
1002 * is used. The usage_map[] array must have 256 entries.
1003 * The function requires a valid bit depth between 1 and 8.
1004 */
1005 static void /* PRIVATE */
opng_analyze_sample_usage(png_structp png_ptr,png_infop info_ptr,png_bytep usage_map)1006 opng_analyze_sample_usage(png_structp png_ptr, png_infop info_ptr,
1007 png_bytep usage_map)
1008 {
1009 png_bytepp row_ptr;
1010 png_bytep sample_ptr;
1011 png_uint_32 width, height;
1012 int bit_depth, init_shift, init_mask, shift, mask;
1013 #ifdef PNG_bKGD_SUPPORTED
1014 png_color_16p background;
1015 #endif
1016 png_uint_32 i, j;
1017
1018 opng_debug(1, "in opng_analyze_sample_usage");
1019
1020 height = png_get_image_height(png_ptr, info_ptr);
1021 width = png_get_image_width(png_ptr, info_ptr);
1022 bit_depth = png_get_bit_depth(png_ptr, info_ptr);
1023 row_ptr = png_get_rows(png_ptr, info_ptr);
1024
1025 /* Initialize the output entries with 0. */
1026 memset(usage_map, 0, 256);
1027
1028 /* Iterate through all sample values. */
1029 if (bit_depth == 8)
1030 {
1031 for (i = 0; i < height; ++i, ++row_ptr)
1032 {
1033 for (j = 0, sample_ptr = *row_ptr; j < width; ++j, ++sample_ptr)
1034 usage_map[*sample_ptr] = 1;
1035 }
1036 }
1037 else
1038 {
1039 OPNG_ASSERT(bit_depth < 8);
1040 init_shift = 8 - bit_depth;
1041 init_mask = (1 << 8) - (1 << init_shift);
1042 for (i = 0; i < height; ++i, ++row_ptr)
1043 {
1044 for (j = 0, sample_ptr = *row_ptr; j < width; ++sample_ptr)
1045 {
1046 mask = init_mask;
1047 shift = init_shift;
1048 do
1049 {
1050 usage_map[(*sample_ptr & mask) >> shift] = 1;
1051 mask >>= bit_depth;
1052 shift -= bit_depth;
1053 ++j;
1054 } while (mask > 0 && j < width);
1055 }
1056 }
1057 }
1058
1059 #ifdef PNG_bKGD_SUPPORTED
1060 /* bKGD also counts as a used sample. */
1061 if (png_get_bKGD(png_ptr, info_ptr, &background))
1062 usage_map[background->index] = 1;
1063 #endif
1064 }
1065
1066 /*
1067 * Reduce the palette. (Only the fast method is implemented.)
1068 * The parameter reductions indicates the intended reductions.
1069 * The function returns the successful reductions.
1070 */
1071 static png_uint_32 /* PRIVATE */
opng_reduce_palette(png_structp png_ptr,png_infop info_ptr,png_uint_32 reductions)1072 opng_reduce_palette(png_structp png_ptr, png_infop info_ptr,
1073 png_uint_32 reductions)
1074 {
1075 png_uint_32 result;
1076 png_colorp palette;
1077 png_bytep trans_alpha;
1078 png_bytepp row_ptr;
1079 png_uint_32 width, height;
1080 int bit_depth, color_type, interlace_type, compression_type, filter_type;
1081 int num_palette, num_trans;
1082 int last_color_index, last_trans_index;
1083 png_byte crt_trans_value, last_trans_value;
1084 png_byte is_used[256];
1085 png_color_16 gray_trans;
1086 int is_gray;
1087 #ifdef PNG_bKGD_SUPPORTED
1088 png_color_16p background;
1089 #endif
1090 #ifdef PNG_hIST_SUPPORTED
1091 png_uint_16p hist;
1092 #endif
1093 #ifdef PNG_sBIT_SUPPORTED
1094 png_color_8p sig_bits;
1095 #endif
1096 png_uint_32 i, j;
1097 int k;
1098
1099 opng_debug(1, "in opng_reduce_palette");
1100
1101 result = OPNG_REDUCE_NONE;
1102
1103 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth,
1104 &color_type, &interlace_type, &compression_type, &filter_type);
1105 row_ptr = png_get_rows(png_ptr, info_ptr);
1106 if (!png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette))
1107 {
1108 palette = NULL;
1109 num_palette = 0;
1110 }
1111 if (!png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, NULL))
1112 {
1113 trans_alpha = NULL;
1114 num_trans = 0;
1115 }
1116 else
1117 OPNG_ASSERT(trans_alpha != NULL && num_trans > 0);
1118
1119 opng_analyze_sample_usage(png_ptr, info_ptr, is_used);
1120 /* Palette-to-gray does not work (yet) if the bit depth is below 8. */
1121 is_gray = (reductions & OPNG_REDUCE_PALETTE_TO_GRAY) && (bit_depth == 8);
1122 last_color_index = last_trans_index = -1;
1123 for (k = 0; k < 256; ++k)
1124 {
1125 if (!is_used[k])
1126 continue;
1127 last_color_index = k;
1128 if (k < num_trans && trans_alpha[k] < 255)
1129 last_trans_index = k;
1130 if (is_gray)
1131 if (palette[k].red != palette[k].green ||
1132 palette[k].red != palette[k].blue)
1133 is_gray = 0;
1134 }
1135 OPNG_ASSERT(last_color_index >= 0);
1136 OPNG_ASSERT(last_color_index >= last_trans_index);
1137
1138 /* Check the integrity of PLTE and tRNS. */
1139 if (last_color_index >= num_palette)
1140 {
1141 png_warning(png_ptr, "Too few colors in PLTE");
1142 /* Fix the palette by adding blank entries at the end. */
1143 opng_realloc_PLTE(png_ptr, info_ptr, last_color_index + 1);
1144 png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
1145 OPNG_ASSERT(num_palette == last_color_index + 1);
1146 result |= OPNG_REDUCE_REPAIR;
1147 }
1148 if (num_trans > num_palette)
1149 {
1150 png_warning(png_ptr, "Too many alpha values in tRNS");
1151 /* Transparency will be fixed further below. */
1152 result |= OPNG_REDUCE_REPAIR;
1153 }
1154
1155 /* Check if tRNS can be reduced to grayscale. */
1156 if (is_gray && last_trans_index >= 0)
1157 {
1158 gray_trans.gray = palette[last_trans_index].red;
1159 last_trans_value = trans_alpha[last_trans_index];
1160 for (k = 0; k <= last_color_index; ++k)
1161 {
1162 if (!is_used[k])
1163 continue;
1164 if (k <= last_trans_index)
1165 {
1166 crt_trans_value = trans_alpha[k];
1167 /* Cannot reduce if different colors have transparency. */
1168 if (crt_trans_value < 255 && palette[k].red != gray_trans.gray)
1169 {
1170 is_gray = 0;
1171 break;
1172 }
1173 }
1174 else
1175 crt_trans_value = 255;
1176 /* Cannot reduce if same color has multiple transparency levels. */
1177 if (palette[k].red == gray_trans.gray &&
1178 crt_trans_value != last_trans_value)
1179 {
1180 is_gray = 0;
1181 break;
1182 }
1183 }
1184 }
1185
1186 /* Remove tRNS if it is entirely sterile. */
1187 if (num_trans > 0 && last_trans_index < 0)
1188 {
1189 num_trans = 0;
1190 png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, -1);
1191 png_set_invalid(png_ptr, info_ptr, PNG_INFO_tRNS);
1192 result |= OPNG_REDUCE_PALETTE_FAST;
1193 }
1194
1195 if (reductions & OPNG_REDUCE_PALETTE_FAST)
1196 {
1197 if (num_palette != last_color_index + 1)
1198 {
1199 /* Reduce PLTE. */
1200 /* hIST is reduced automatically. */
1201 opng_realloc_PLTE(png_ptr, info_ptr, last_color_index + 1);
1202 png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
1203 OPNG_ASSERT(num_palette == last_color_index + 1);
1204 result |= OPNG_REDUCE_PALETTE_FAST;
1205 }
1206
1207 if (num_trans > 0 && num_trans != last_trans_index + 1)
1208 {
1209 /* Reduce tRNS. */
1210 opng_realloc_tRNS(png_ptr, info_ptr, last_trans_index + 1);
1211 png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, NULL);
1212 OPNG_ASSERT(num_trans == last_trans_index + 1);
1213 result |= OPNG_REDUCE_PALETTE_FAST;
1214 }
1215 }
1216
1217 if (reductions & OPNG_REDUCE_8_TO_4_2_1)
1218 {
1219 result |= opng_reduce_palette_bits(png_ptr, info_ptr, reductions);
1220 /* Refresh the image information. */
1221 bit_depth = png_get_bit_depth(png_ptr, info_ptr);
1222 }
1223 if ((bit_depth < 8) || !is_gray)
1224 return result;
1225
1226 /* Reduce palette --> grayscale. */
1227 for (i = 0; i < height; ++i)
1228 {
1229 for (j = 0; j < width; ++j)
1230 row_ptr[i][j] = palette[row_ptr[i][j]].red;
1231 }
1232
1233 /* Update the ancillary information. */
1234 if (num_trans > 0)
1235 png_set_tRNS(png_ptr, info_ptr, NULL, 0, &gray_trans);
1236 #ifdef PNG_bKGD_SUPPORTED
1237 if (png_get_bKGD(png_ptr, info_ptr, &background))
1238 background->gray = palette[background->index].red;
1239 #endif
1240 #ifdef PNG_hIST_SUPPORTED
1241 if (png_get_hIST(png_ptr, info_ptr, &hist))
1242 {
1243 png_free_data(png_ptr, info_ptr, PNG_FREE_HIST, -1);
1244 png_set_invalid(png_ptr, info_ptr, PNG_INFO_hIST);
1245 }
1246 #endif
1247 #ifdef PNG_sBIT_SUPPORTED
1248 if (png_get_sBIT(png_ptr, info_ptr, &sig_bits))
1249 {
1250 png_byte max_sig_bits = sig_bits->red;
1251 if (max_sig_bits < sig_bits->green)
1252 max_sig_bits = sig_bits->green;
1253 if (max_sig_bits < sig_bits->blue)
1254 max_sig_bits = sig_bits->blue;
1255 sig_bits->gray = max_sig_bits;
1256 }
1257 #endif
1258
1259 /* Update the image information. */
1260 png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth,
1261 PNG_COLOR_TYPE_GRAY, interlace_type, compression_type, filter_type);
1262 png_free_data(png_ptr, info_ptr, PNG_FREE_PLTE, -1);
1263 png_set_invalid(png_ptr, info_ptr, PNG_INFO_PLTE);
1264 return OPNG_REDUCE_PALETTE_TO_GRAY; /* ignore the former result */
1265 }
1266
1267 /*
1268 * Reduce the image (bit depth + color type + palette) without
1269 * losing any information. The palette (if applicable) and the
1270 * image data must be present, e.g., by calling png_set_rows(),
1271 * or by loading IDAT.
1272 * The parameter reductions indicates the intended reductions.
1273 * The function returns the successful reductions.
1274 */
1275 png_uint_32 PNGAPI
opng_reduce_image(png_structp png_ptr,png_infop info_ptr,png_uint_32 reductions)1276 opng_reduce_image(png_structp png_ptr, png_infop info_ptr,
1277 png_uint_32 reductions)
1278 {
1279 png_uint_32 result;
1280 int color_type;
1281
1282 opng_debug(1, "in opng_reduce_image_type");
1283
1284 if (!opng_validate_image(png_ptr, info_ptr))
1285 {
1286 png_warning(png_ptr,
1287 "Image reduction requires the presence of all critical information");
1288 return OPNG_REDUCE_NONE;
1289 }
1290
1291 color_type = png_get_color_type(png_ptr, info_ptr);
1292
1293 /* The reductions below must be applied in this particular order. */
1294
1295 /* Try to reduce the high bits and color/alpha channels. */
1296 result = opng_reduce_bits(png_ptr, info_ptr, reductions);
1297
1298 /* Try to reduce the palette image. */
1299 if (color_type == PNG_COLOR_TYPE_PALETTE &&
1300 (reductions &
1301 (OPNG_REDUCE_PALETTE_TO_GRAY |
1302 OPNG_REDUCE_PALETTE_FAST |
1303 OPNG_REDUCE_8_TO_4_2_1)))
1304 result |= opng_reduce_palette(png_ptr, info_ptr, reductions);
1305
1306 /* Try to reduce RGB to palette or grayscale to palette. */
1307 if (((color_type & ~PNG_COLOR_MASK_ALPHA) == PNG_COLOR_TYPE_GRAY &&
1308 (reductions & OPNG_REDUCE_GRAY_TO_PALETTE)) ||
1309 ((color_type & ~PNG_COLOR_MASK_ALPHA) == PNG_COLOR_TYPE_RGB &&
1310 (reductions & OPNG_REDUCE_RGB_TO_PALETTE)))
1311 {
1312 if (!(result & OPNG_REDUCE_PALETTE_TO_GRAY))
1313 result |= opng_reduce_to_palette(png_ptr, info_ptr, reductions);
1314 }
1315
1316 return result;
1317 }
1318
1319 #endif /* OPNG_IMAGE_REDUCTIONS_SUPPORTED */
1320