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