diff --git a/png.h b/png.h index e714f7262..1cad539ed 100644 --- a/png.h +++ b/png.h @@ -1661,22 +1661,26 @@ PNG_EXPORT(66, void, png_set_crc_action, (png_structrp png_ptr, int crit_action, * ignored if SUB is set; this is because these filter pairs are equivalent * when there is no previous row. * - * PNG_WRITE_OPTIMIZE_FITLER_SUPPORTED: - * 2) If WRITE_OPTIMIZE_FILTER is supported and it has not been disabled by - * png_set_option(PNG_DISABLE_OPTIMIZE_FILTER, PNG_OPTION_ON) libpng tries - * all the filters in the list and selects the one which gives the shortest - * compressed row, favoring earlier filters. + * 2) PNG_SELECT_FILTER_METHODICALLY_SUPPORTED: + * If SELECT_FILTER_METHODICALLY is 'on' libpng tries all the filters in the + * list and selects the one which gives the shortest compressed row, favoring + * earlier filters. * - * PNG_WRITE_HEURISTIC_FITLER_SUPPORTED: - * 3) If not (2) an WRITE_HEURISTIC_FILTER is supported and has not been - * disabled by png_set_option(PNG_DISABLE_HEURISTIC_FILTER, PNG_OPTION_ON) - * libpng tests the start of each row (a few thousand bytes at most) to see - * which filter is likely to produce best compression. + * 3) PNG_SELECT_FILTER_HEURISTICALLY_SUPPORTED: + * If SELECT_FILTER_HEURISTICALLY is 'on' libpng tests the start of each row + * (a few thousand bytes at most) to see which filter is likely to produce + * best compression. * * 4) If neither (2) nor (3) libpng selects the first filter in the list (there * is no warning that this will happen - check the #defines if you need to * know.) * + * The seletion options are turned 'on' using png_set_option(method, + * PNG_OPTION_ON) and turned off with PNG_OPTION_OFF. If a selection method is + * turned off it will never be used, if neither option is turned on or off (i.e. + * if png_set_option is not called) the first supported option (2) or (3) will + * be used. + * * If you intend to use 'previous row' filters in an image set either the UP or * PAETH filter before the first call to png_write_row, depending on whether you * want to use NONE or SUB on the first row. @@ -1685,11 +1689,11 @@ PNG_EXPORT(66, void, png_set_crc_action, (png_structrp png_ptr, int crit_action, * preceding byte as a predictor and is not likely to have very good * performance. * - * The WRITE_OPTIMIZE_FILTER option is slow and memory intensive, but it is - * likely to produce the smallest PNG file. Depending on the image data the - * HEURISTIC option may improve results and has little overall effect on - * compression speed, however it can sometimes produce larger files than not - * using any filtering. + * The SELECT_FILTER_METHODICALLY option is slow and memory intensive, but it is + * almost certain to produce the smallest PNG file. Depending on the image data + * the HEURISTIC option may improve results significantly over the NONE filter + * and it has little overall effect on compression speed, however it can + * sometimes produce larger files than not using any filtering. */ PNG_EXPORT(67, void, png_set_filter, (png_structrp png_ptr, int method, int filters)); @@ -3491,8 +3495,8 @@ PNG_EXPORT(240, int, png_image_write_to_stdio, (png_imagep image, FILE *file, #define PNG_EXTENSIONS 0 /* BOTH: enable or disable extensions */ #define PNG_MAXIMUM_INFLATE_WINDOW 2 /* SOFTWARE: force maximum window */ #define PNG_SKIP_sRGB_CHECK_PROFILE 4 /* SOFTWARE: Check ICC profile for sRGB */ -#define PNG_DISABLE_HEURISTIC_FILTER 6 /* SOFTWARE: see png_set_filter */ -#define PNG_DISABLE_OPTIMIZE_FILTER 8 /* SOFTWARE: see png_set_filter */ +#define PNG_SELECT_FILTER_HEURISTICALLY 6 /* SOFTWARE: see png_set_filter */ +#define PNG_SELECT_FILTER_METHODICALLY 8 /* SOFTWARE: see png_set_filter */ #define PNG_OPTION_NEXT 10 /* Next option - numbers must be even */ /* Return values: NOTE: there are four values and 'off' is *not* zero */ diff --git a/pngstruct.h b/pngstruct.h index eadd5b9e2..09d47a40a 100644 --- a/pngstruct.h +++ b/pngstruct.h @@ -751,6 +751,9 @@ struct png_struct_def png_compression_bufferp *zbuffer_end; /* 'next' field of current buffer */ png_uint_32 zbuffer_len; /* Length of data in list */ unsigned int zbuffer_start; /* Bytes written from start */ +# ifdef PNG_SELECT_FILTER_METHODICALLY_SUPPORTED + png_voidp zbuffer_select; +# endif /* SELECT_FILTER_METHODICALLY */ # endif /* WRITE */ # ifdef PNG_READ_SUPPORTED unsigned int zstream_ended:1; /* no more zlib output available [read] */ diff --git a/pngwutil.c b/pngwutil.c index 03aa6a270..3eff9d1cb 100644 --- a/pngwutil.c +++ b/pngwutil.c @@ -2375,16 +2375,16 @@ filter_row(png_structrp png_ptr, png_const_bytep prev_row, /* These two #defines simplify writing code that depends on one or the other of * the options being both supported and on: */ -#ifdef PNG_WRITE_OPTIMIZE_FILTER_SUPPORTED -# define optimize_filters\ - (((png_ptr->options >> PNG_DISABLE_OPTIMIZE_FILTER)&3) != PNG_OPTION_ON) +#ifdef PNG_SELECT_FILTER_METHODICALLY_SUPPORTED +# define methodical_option\ + ((png_ptr->options >> PNG_SELECT_FILTER_METHODICALLY) & 3U) #else -# define optimize_filters 0 +# define methodical_option PNG_OPTION_OFF #endif -#ifdef PNG_WRITE_HEURISTIC_FILTER_SUPPORTED -# define heuristic_filters\ - (((png_ptr->options >> PNG_DISABLE_HEURISTIC_FILTER)&3) != PNG_OPTION_ON) +#ifdef PNG_SELECT_FILTER_HEURISTICALLY_SUPPORTED +# define heuristic_option\ + ((png_ptr->options >> PNG_SELECT_FILTER_HEURISTICALLY) & 3U) static unsigned int select_filter_heuristically(png_structrp png_ptr, png_const_bytep prev_row, @@ -2451,9 +2451,41 @@ select_filter_heuristically(png_structrp png_ptr, png_const_bytep prev_row, return PNG_FILTER_MASK(best_filter); } } -#else /* !WRITE_HEURISTIC_FILTER */ -# define heuristic_filters 0 -#endif /* !WRITE_HEURISTIC_FILTER */ +#else /* !SELECT_FILTER_HEURISTICALLY */ +# define heuristic_option PNG_OPTION_OFF +#endif /* !SELECT_FILTER_HEURISTICALLY */ + +#ifdef PNG_SELECT_FILTER_METHODICALLY_SUPPORTED +static void +select_filters_methodically_init(png_structrp png_ptr) +{ + affirm(png_ptr->zbuffer_select == NULL); +} + +static int +select_filter_methodically(png_structrp png_ptr, png_const_bytep prev_row, + png_bytep prev_pixels, png_const_bytep unfiltered_row, + unsigned int row_bits, unsigned int bpp, unsigned int filters_to_try, + int end_of_image) +{ + const unsigned int row_bytes = (row_bits+7U) >> 3; + png_byte test_buffers[4][PNG_ROW_BUFFER_SIZE]; /* for each filter */ + + affirm(row_bytes <= PNG_ROW_BUFFER_SIZE); + debug((row_bits % bpp) == 0U); + + filter_block(prev_row, prev_pixels, unfiltered_row, row_bits, bpp, + test_buffers[PNG_FILTER_VALUE_SUB-1U], + test_buffers[PNG_FILTER_VALUE_UP-1U], + test_buffers[PNG_FILTER_VALUE_AVG-1U], + test_buffers[PNG_FILTER_VALUE_PAETH-1U]); + + write_unfiltered_rowbits(png_ptr, unfiltered_row, row_bits, + PNG_FILTER_VALUE_LAST, end_of_image); + + return filters_to_try; +} +#endif /* SELECT_FILTER_METHODICALLY */ /* This filters the row, chooses which filter to use, if it has not already * been specified by the application, and then writes the row out with the @@ -2481,8 +2513,10 @@ png_write_filter_row(png_structrp png_ptr, png_bytep prev_pixels, { /* Delaying initialization of the filter stuff. */ if (png_ptr->filter_mask == 0U) - png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, (optimize_filters || - heuristic_filters) ? PNG_ALL_FILTERS : PNG_NO_FILTERS); + png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, + (methodical_option != PNG_OPTION_OFF || + heuristic_option != PNG_OPTION_OFF) ? + PNG_ALL_FILTERS : PNG_NO_FILTERS); /* Now work out the filters to try for this row: */ filters_to_try = png_ptr->filter_mask; /* else caller must preserve */ @@ -2539,19 +2573,20 @@ png_write_filter_row(png_structrp png_ptr, png_bytep prev_pixels, # undef match } - /* Is this a list of filters which can be simplified to a single filter? - * If there is no selection algorithm enabled do so now: - * - * (Errors in the logic here trigger the 'impossible' else below.) + /* If there is no selection algorithm enabled choose the first filter + * in the list, otherwise do algorithm-specific initialization. */ -# ifdef PNG_WRITE_OPTIMIZE_FILTER_SUPPORTED - if (((png_ptr->options >> PNG_DISABLE_OPTIMIZE_FILTER) & 3) == - PNG_OPTION_ON) /* optimize supported but disabled */ -# endif /* WRITE_OPTIMIZE_FILTER */ -# ifdef PNG_WRITE_HEURISTIC_FILTER_SUPPORTED - if (((png_ptr->options >> PNG_DISABLE_HEURISTIC_FILTER) & 3) == - PNG_OPTION_ON) /* heuristic supported but disabled */ -# endif /* WRITE_HEURISTIC_FILTER */ +# ifdef PNG_SELECT_FILTER_METHODICALLY_SUPPORTED + if (methodical_option == PNG_OPTION_ON || + (methodical_option != PNG_OPTION_OFF && + heuristic_option != PNG_OPTION_ON)) + select_filters_methodically_init(png_ptr); + + else /* don't do methodical selection */ +# endif /* SELECT_FILTER_METHODICALLY */ +# ifdef PNG_SELECT_FILTER_HEURISTICALLY_SUPPORTED + if (heuristic_option == PNG_OPTION_OFF) /* don't use heuristics */ +# endif /* SELECT_FILTER_HEURISTICALLY */ filters_to_try &= -filters_to_try; } /* start of row */ @@ -2579,25 +2614,24 @@ png_write_filter_row(png_structrp png_ptr, png_bytep prev_pixels, prev_pixels, unfiltered_row, row_bits, bpp, filters_to_try, x == 0, end_of_image); -# ifdef PNG_WRITE_OPTIMIZE_FILTER_SUPPORTED -#if 0 - else if (((png_ptr->options >> PNG_DISABLE_OPTIMIZE_FILTER) & 3) != - PNG_OPTION_ON) /* optimize supported and not disabled */ - impossible("optimize filters NYI"); -#endif -# endif /* WRITE_OPTIMIZE_FITLER */ -# ifdef PNG_WRITE_HEURISTIC_FILTER_SUPPORTED +# ifdef PNG_SELECT_FILTER_METHODICALLY_SUPPORTED + else if (png_ptr->zbuffer_select != NULL) + filters_to_try = select_filter_methodically(png_ptr, + first_row_in_pass ? NULL : prev_row, prev_pixels, unfiltered_row, + row_bits, bpp, filters_to_try, end_of_image); +# endif /* SELECT_FILTER_METHODICALLY */ +# ifdef PNG_SELECT_FILTER_HEURISTICALLY_SUPPORTED /* The heuristic must select a single filter based on the first block of * pixels: */ - else if (((png_ptr->options >> PNG_DISABLE_HEURISTIC_FILTER) & 3) != - PNG_OPTION_ON) + else filters_to_try = select_filter_heuristically(png_ptr, first_row_in_pass ? NULL : prev_row, prev_pixels, unfiltered_row, row_bits, bpp, filters_to_try, end_of_image); -# endif /* WRITE_HEURISTIC_FITLER */ - else - impossible("bad filter select logic"); +# else /* !SELECT_FILTER_HEURISTICALLY */ + else + impossible("bad filter select logic"); +# endif /* !SELECT_FILTER_HEURISTICALLY */ /* Copy the current row into the previous row buffer, if available, unless * this is the last row in the pass, when there is no point. Note that diff --git a/scripts/pnglibconf.dfa b/scripts/pnglibconf.dfa index bf903f3ea..0a014070c 100644 --- a/scripts/pnglibconf.dfa +++ b/scripts/pnglibconf.dfa @@ -925,24 +925,24 @@ option CONVERT_tIME requires WRITE_ANCILLARY_CHUNKS # # WRITE_FILTER # Enables code to do PNG row filtering on write. If not enabled rows will -# be written without filtering, the 'NONE' filter. This enables the +# be written without filtering; the 'NONE' filter. This enables the # png_set_filter interface allowing the application to select the filter # used for each row. # -# WRITE_HEURISTIC_FILTER +# SELECT_FILTER_HEURISTICALLY # Enables code to cause libpng to choose a filter from a set passed to # png_set_filter. Without this code libpng just chooses the first filter in # the list if multiple are given. # -# WRITE_OPTIMIZE_FILTER +# SELECT_FILTER_METHODICALLY # Enables code to try all the filters in the list passed to png_set_filter # and choose the one which results in the least number of compressed bytes # added by the current row. # # See png.h for more description of these options. option WRITE_FILTER requires WRITE -option WRITE_HEURISTIC_FILTER requires WRITE_FILTER enables SET_OPTION -option WRITE_OPTIMIZE_FILTER requires WRITE_FILTER enables SET_OPTION +option SELECT_FILTER_HEURISTICALLY requires WRITE_FILTER enables SET_OPTION +option SELECT_FILTER_METHODICALLY requires WRITE_FILTER enables SET_OPTION # added at libpng-1.5.4 diff --git a/scripts/pnglibconf.h.prebuilt b/scripts/pnglibconf.h.prebuilt index f0b784717..23a74f201 100644 --- a/scripts/pnglibconf.h.prebuilt +++ b/scripts/pnglibconf.h.prebuilt @@ -134,13 +134,13 @@ #define PNG_WRITE_FILTER_SUPPORTED #define PNG_WRITE_FLUSH_SUPPORTED #define PNG_WRITE_GET_PALETTE_MAX_SUPPORTED -#define PNG_WRITE_HEURISTIC_FILTER_SUPPORTED +#define PNG_SELECT_FILTER_HEURISTICALLY_SUPPORTED #define PNG_WRITE_INTERLACING_SUPPORTED #define PNG_WRITE_INT_FUNCTIONS_SUPPORTED #define PNG_WRITE_INVERT_ALPHA_SUPPORTED #define PNG_WRITE_INVERT_SUPPORTED #define PNG_WRITE_OPTIMIZE_CMF_SUPPORTED -#define PNG_WRITE_OPTIMIZE_FILTER_SUPPORTED +#define PNG_SELECT_FILTER_METHODICALLY_SUPPORTED #define PNG_WRITE_PACKSWAP_SUPPORTED #define PNG_WRITE_PACK_SUPPORTED #define PNG_WRITE_PNG_SUPPORTED