diff --git a/ANNOUNCE b/ANNOUNCE index 618d9c1ec..b1edea33b 100644 --- a/ANNOUNCE +++ b/ANNOUNCE @@ -1,5 +1,5 @@ -Libpng 1.7.0beta49 - February 7, 2015 +Libpng 1.7.0beta49 - February 10, 2015 This is not intended to be a public release. It will be replaced within a few weeks by a public version or by another test version. @@ -701,7 +701,9 @@ Version 1.7.0beta48 [February 7, 2015] Work around one more Coverity-scan dead-code warning. Do not build png_product2() when it is unused. -Version 1.7.0beta49 [February 7, 2015] +Version 1.7.0beta49 [February 10, 2015] + Combined sub_row, up_row, avg_row, and paeth_row buffers into a + single try_row buffer. Send comments/corrections/commendations to png-mng-implement at lists.sf.net (subscription required; visit diff --git a/CHANGES b/CHANGES index 547de38cc..3b22bdb89 100644 --- a/CHANGES +++ b/CHANGES @@ -4990,7 +4990,9 @@ Version 1.7.0beta48 [February 7, 2015] Work around one more Coverity-scan dead-code warning. Do not build png_product2() when it is unused. -Version 1.7.0beta49 [February 7, 2015] +Version 1.7.0beta49 [February 10, 2015] + Combined sub_row, up_row, avg_row, and paeth_row buffers into a + single try_row buffer. Send comments/corrections/commendations to png-mng-implement at lists.sf.net (subscription required; visit diff --git a/png.h b/png.h index 356c22d8f..875f34db6 100644 --- a/png.h +++ b/png.h @@ -1,7 +1,7 @@ /* png.h - header file for PNG reference library * - * libpng version 1.7.0beta49 - February 7, 2015 + * libpng version 1.7.0beta49 - February 10, 2015 * Copyright (c) 1998-2015 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) @@ -11,7 +11,7 @@ * Authors and maintainers: * libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat * libpng versions 0.89c, June 1996, through 0.96, May 1997: Andreas Dilger - * libpng versions 0.97, January 1998, through 1.7.0beta49 - February 7, 2015: Glenn + * libpng versions 0.97, January 1998, through 1.7.0beta49 - February 10, 2015: Glenn * See also "Contributing Authors", below. * * Note about libpng version numbers: @@ -200,7 +200,7 @@ * * This code is released under the libpng license. * - * libpng versions 1.2.6, August 15, 2004, through 1.7.0beta49, February 7, 2015, are + * libpng versions 1.2.6, August 15, 2004, through 1.7.0beta49, February 10, 2015, are * Copyright (c) 2004, 2006-2013 Glenn Randers-Pehrson, and are * distributed according to the same disclaimer and license as libpng-1.2.5 * with the following individual added to the list of Contributing Authors: @@ -312,7 +312,7 @@ * Y2K compliance in libpng: * ========================= * - * February 7, 2015 + * February 10, 2015 * * Since the PNG Development group is an ad-hoc body, we can't make * an official declaration. @@ -382,7 +382,7 @@ /* Version information for png.h - this should match the version in png.c */ #define PNG_LIBPNG_VER_STRING "1.7.0beta49" #define PNG_HEADER_VERSION_STRING \ - " libpng version 1.7.0beta49 - February 7, 2015\n" + " libpng version 1.7.0beta49 - February 10, 2015\n" #define PNG_LIBPNG_VER_SONUM 17 #define PNG_LIBPNG_VER_DLLNUM 17 @@ -1765,7 +1765,6 @@ PNG_EXPORT(72, void, png_set_compression_window_bits, (png_structrp png_ptr, PNG_EXPORT(73, void, png_set_compression_method, (png_structrp png_ptr, int method)); -#endif #ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED /* Also set zlib parameters for compressing non-IDAT chunks */ @@ -1787,6 +1786,7 @@ PNG_EXPORT(225, void, png_set_text_compression_window_bits, PNG_EXPORT(226, void, png_set_text_compression_method, (png_structrp png_ptr, int method)); #endif /* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */ +#endif /* WRITE */ /* These next functions are called for input/output, memory, and error * handling. They are in the file pngrio.c, pngwio.c, and pngerror.c, diff --git a/pngstruct.h b/pngstruct.h index fc272d0fb..062f131d7 100644 --- a/pngstruct.h +++ b/pngstruct.h @@ -345,13 +345,8 @@ struct png_struct_def /* This is somewhat excessive, there is no obvious reason on write to * allocate a buffer for each possible filtered row, only for the one being * tested and the current best. - * - * TODO: fix this */ - png_bytep sub_row; /* buffer to save "sub" row when filtering */ - png_bytep up_row; /* buffer to save "up" row when filtering */ - png_bytep avg_row; /* buffer to save "avg" row when filtering */ - png_bytep paeth_row; /* buffer to save "Paeth" row when filtering */ + png_bytep try_row; /* buffer to save trial row when filtering */ #endif /* UNKNOWN CHUNK HANDLING */ diff --git a/pngwrite.c b/pngwrite.c index ce78a48d8..248e638fe 100644 --- a/pngwrite.c +++ b/pngwrite.c @@ -942,15 +942,9 @@ png_write_destroy(png_structrp png_ptr) png_ptr->row_buf = NULL; #ifdef PNG_WRITE_FILTER_SUPPORTED png_free(png_ptr, png_ptr->prev_row); - png_free(png_ptr, png_ptr->sub_row); - png_free(png_ptr, png_ptr->up_row); - png_free(png_ptr, png_ptr->avg_row); - png_free(png_ptr, png_ptr->paeth_row); + png_free(png_ptr, png_ptr->try_row); png_ptr->prev_row = NULL; - png_ptr->sub_row = NULL; - png_ptr->up_row = NULL; - png_ptr->avg_row = NULL; - png_ptr->paeth_row = NULL; + png_ptr->try_row = NULL; #endif #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED @@ -1065,8 +1059,8 @@ png_set_filter(png_structrp png_ptr, int method, int filters) * it is too late to start using the filters that need it, since we * will be missing the data in the previous row. If an application * wants to start and stop using particular filters during compression, - * it should start out with all of the filters, and then add and - * remove them after the start of compression. + * it should start out with all of the filters, and then remove them + * or add them back after the start of compression. * * NOTE: this is a nasty constraint on the code, because it means that the * prev_row buffer must be maintained even if there are currently no diff --git a/pngwutil.c b/pngwutil.c index cfb65e407..14822aacb 100644 --- a/pngwutil.c +++ b/pngwutil.c @@ -1943,7 +1943,7 @@ png_write_tIME(png_structrp png_ptr, png_const_timep mod_time) #ifdef PNG_WRITE_FILTER_SUPPORTED void /* PRIVATE */ png_write_alloc_filter_row_buffers(png_structrp png_ptr, int filters) - /* Allocate row buffers for any filters that need them, this is also called + /* Allocate row buffers for any filters that need them. This is also called * from png_set_filter if the filters are changed during write to ensure that * the required buffers exist. png_set_filter ensures that up/avg/paeth are * only set if png_ptr->prev_row is allocated. @@ -1954,29 +1954,10 @@ png_write_alloc_filter_row_buffers(png_structrp png_ptr, int filters) */ png_alloc_size_t buf_size = png_ptr->rowbytes + 1; - if ((filters & PNG_FILTER_SUB) != 0 && png_ptr->sub_row == NULL) + if (((filters & (PNG_FILTER_SUB | PNG_FILTER_UP | PNG_FILTER_AVG | + PNG_FILTER_PAETH)) != 0) && png_ptr->try_row == NULL) { - png_ptr->sub_row = png_voidcast(png_bytep, png_malloc(png_ptr, buf_size)); - png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB; - } - - if ((filters & PNG_FILTER_UP) != 0 && png_ptr->up_row == NULL) - { - png_ptr->up_row = png_voidcast(png_bytep, png_malloc(png_ptr, buf_size)); - png_ptr->up_row[0] = PNG_FILTER_VALUE_UP; - } - - if ((filters & PNG_FILTER_AVG) != 0 && png_ptr->avg_row == NULL) - { - png_ptr->avg_row = png_voidcast(png_bytep, png_malloc(png_ptr, buf_size)); - png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG; - } - - if ((filters & PNG_FILTER_PAETH) != 0 && png_ptr->paeth_row == NULL) - { - png_ptr->paeth_row = png_voidcast(png_bytep, png_malloc(png_ptr, - buf_size)); - png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH; + png_ptr->try_row = png_voidcast(png_bytep, png_malloc(png_ptr, buf_size)); } } #endif /* WRITE_FILTER */ @@ -2352,17 +2333,117 @@ static void /* PRIVATE */ png_write_filtered_row(png_structrp png_ptr, png_bytep filtered_row, png_size_t row_bytes); +#ifdef PNG_WRITE_FILTER_SUPPORTED +static void /* PRIVATE */ +png_setup_sub_row(png_structrp png_ptr, png_uint_32 bpp, + png_bytep row_buf, png_size_t row_bytes) +{ + png_bytep rp, dp, lp; + png_size_t i; + + for (i = 0, rp = row_buf + 1, dp = png_ptr->try_row + 1; i < bpp; + i++, rp++, dp++) + { + *dp = *rp; + } + + for (lp = row_buf + 1; i < row_bytes; + i++, rp++, lp++, dp++) + { + *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff); + } +} + +static void /* PRIVATE */ +png_setup_up_row(png_structrp png_ptr, png_bytep row_buf, png_size_t row_bytes) +{ + png_bytep rp, dp, pp; + png_size_t i; + + for (i = 0, rp = row_buf + 1, dp = png_ptr->try_row + 1, + pp = png_ptr->prev_row + 1; i < row_bytes; + i++, rp++, pp++, dp++) + { + *dp = (png_byte)(((int)*rp - (int)*pp) & 0xff); + } +} + +static void /* PRIVATE */ +png_setup_avg_row(png_structrp png_ptr,png_uint_32 bpp, + png_bytep row_buf, png_size_t row_bytes) +{ + png_bytep rp, dp, pp, lp; + png_uint_32 i; + + for (i = 0, rp = row_buf + 1, dp = png_ptr->try_row + 1, + pp = png_ptr->prev_row + 1; i < bpp; i++) + { + *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff); + } + + for (lp = row_buf + 1; i < row_bytes; i++) + { + *dp++ = (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2)) + & 0xff); + } +} + +static void /* PRIVATE */ +png_setup_paeth_row(png_structrp png_ptr,png_uint_32 bpp, + png_bytep row_buf, png_size_t row_bytes) +{ + png_bytep rp, dp, pp, cp, lp; + png_size_t i; + + for (i = 0, rp = row_buf + 1, dp = png_ptr->try_row + 1, + pp = png_ptr->prev_row + 1; i < bpp; i++) + { + *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); + } + + for (lp = row_buf + 1, cp = png_ptr->prev_row + 1; i < row_bytes; i++) + { + int a, b, c, pa, pb, pc, p; + + b = *pp++; + c = *cp++; + a = *lp++; + + p = b - c; + pc = a - c; + +#ifdef PNG_USE_ABS + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); +#else + pa = p < 0 ? -p : p; + pb = pc < 0 ? -pc : pc; + pc = (p + pc) < 0 ? -(p + pc) : p + pc; +#endif + + p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c; + + *dp++ = (png_byte)(((int)*rp++ - p) & 0xff); + } +} + #define PNG_MAXSUM (((png_uint_32)(-1)) >> 1) #define PNG_HISHIFT 10 #define PNG_LOMASK ((png_uint_32)0xffffL) #define PNG_HIMASK ((png_uint_32)(~PNG_LOMASK >> PNG_HISHIFT)) +#endif /* WRITE_FILTER */ + void /* PRIVATE */ png_write_find_filter(png_structrp png_ptr, png_row_infop row_info) { - png_bytep best_row; -#ifdef PNG_WRITE_FILTER_SUPPORTED +#ifndef PNG_WRITE_FILTER_SUPPORTED + png_write_filtered_row(png_ptr, png_ptr->row_buf, row_info->rowbytes+1); +#else png_bytep prev_row, row_buf; png_uint_32 mins, bpp; + png_byte best_filter_value = PNG_FILTER_VALUE_NONE; + png_byte last_tested_row_value = PNG_FILTER_VALUE_NONE; png_byte filter_to_do = png_ptr->do_filter; png_size_t row_bytes = row_info->rowbytes; #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED @@ -2383,10 +2464,7 @@ png_write_find_filter(png_structrp png_ptr, png_row_infop row_info) bpp = (row_info->pixel_depth + 7) >> 3; prev_row = png_ptr->prev_row; -#endif - best_row = png_ptr->row_buf; -#ifdef PNG_WRITE_FILTER_SUPPORTED - row_buf = best_row; + row_buf = png_ptr->row_buf; mins = PNG_MAXSUM; /* The prediction method we use is to find which method provides the @@ -2475,22 +2553,10 @@ png_write_find_filter(png_structrp png_ptr, png_row_infop row_info) if (filter_to_do == PNG_FILTER_SUB) /* It's the only filter so no testing is needed */ { - png_bytep rp, lp, dp; - png_size_t i; - - for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp; - i++, rp++, dp++) - { - *dp = *rp; - } - - for (lp = row_buf + 1; i < row_bytes; - i++, rp++, lp++, dp++) - { - *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff); - } - - best_row = png_ptr->sub_row; + png_ptr->try_row[0] = PNG_FILTER_VALUE_SUB; + last_tested_row_value = PNG_FILTER_VALUE_SUB; + png_setup_sub_row(png_ptr, bpp, row_buf, row_bytes); + best_filter_value = PNG_FILTER_VALUE_SUB; } else if ((filter_to_do & PNG_FILTER_SUB) != 0) @@ -2538,7 +2604,7 @@ png_write_find_filter(png_structrp png_ptr, png_row_infop row_info) } #endif - for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp; + for (i = 0, rp = row_buf + 1, dp = png_ptr->try_row + 1; i < bpp; i++, rp++, dp++) { v = *dp = *rp; @@ -2594,24 +2660,17 @@ png_write_find_filter(png_structrp png_ptr, png_row_infop row_info) if (sum < mins) { mins = sum; - best_row = png_ptr->sub_row; + best_filter_value = PNG_FILTER_VALUE_SUB; } } /* Up filter */ if (filter_to_do == PNG_FILTER_UP) { - png_bytep rp, dp, pp; - png_size_t i; - - for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1, - pp = prev_row + 1; i < row_bytes; - i++, rp++, pp++, dp++) - { - *dp = (png_byte)(((int)*rp - (int)*pp) & 0xff); - } - - best_row = png_ptr->up_row; + last_tested_row_value = PNG_FILTER_VALUE_UP; + png_ptr->try_row[0] = PNG_FILTER_VALUE_UP; + png_setup_up_row(png_ptr, row_buf, row_bytes); + best_filter_value = PNG_FILTER_VALUE_UP; } else if ((filter_to_do & PNG_FILTER_UP) != 0) @@ -2656,7 +2715,7 @@ png_write_find_filter(png_structrp png_ptr, png_row_infop row_info) } #endif - for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1, + for (i = 0, rp = row_buf + 1, dp = png_ptr->try_row + 1, pp = prev_row + 1; i < row_bytes; i++) { v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); @@ -2704,28 +2763,17 @@ png_write_find_filter(png_structrp png_ptr, png_row_infop row_info) if (sum < mins) { mins = sum; - best_row = png_ptr->up_row; + best_filter_value = PNG_FILTER_VALUE_UP; } } /* Avg filter */ if (filter_to_do == PNG_FILTER_AVG) { - png_bytep rp, dp, pp, lp; - png_uint_32 i; - - for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1, - pp = prev_row + 1; i < bpp; i++) - { - *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff); - } - - for (lp = row_buf + 1; i < row_bytes; i++) - { - *dp++ = (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2)) - & 0xff); - } - best_row = png_ptr->avg_row; + last_tested_row_value = PNG_FILTER_VALUE_AVG; + png_ptr->try_row[0] = PNG_FILTER_VALUE_AVG; + png_setup_avg_row(png_ptr, bpp, row_buf, row_bytes); + best_filter_value = PNG_FILTER_VALUE_AVG; } else if ((filter_to_do & PNG_FILTER_AVG) != 0) @@ -2769,7 +2817,7 @@ png_write_find_filter(png_structrp png_ptr, png_row_infop row_info) } #endif - for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1, + for (i = 0, rp = row_buf + 1, dp = png_ptr->try_row + 1, pp = prev_row + 1; i < bpp; i++) { v = *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff); @@ -2825,48 +2873,17 @@ png_write_find_filter(png_structrp png_ptr, png_row_infop row_info) if (sum < mins) { mins = sum; - best_row = png_ptr->avg_row; + best_filter_value = PNG_FILTER_VALUE_AVG; } } /* Paeth filter */ if ((filter_to_do == PNG_FILTER_PAETH) != 0) { - png_bytep rp, dp, pp, cp, lp; - png_size_t i; - - for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1, - pp = prev_row + 1; i < bpp; i++) - { - *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); - } - - for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++) - { - int a, b, c, pa, pb, pc, p; - - b = *pp++; - c = *cp++; - a = *lp++; - - p = b - c; - pc = a - c; - -#ifdef PNG_USE_ABS - pa = abs(p); - pb = abs(pc); - pc = abs(p + pc); -#else - pa = p < 0 ? -p : p; - pb = pc < 0 ? -pc : pc; - pc = (p + pc) < 0 ? -(p + pc) : p + pc; -#endif - - p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c; - - *dp++ = (png_byte)(((int)*rp++ - p) & 0xff); - } - best_row = png_ptr->paeth_row; + last_tested_row_value = PNG_FILTER_VALUE_PAETH; + png_ptr->try_row[0] = PNG_FILTER_VALUE_PAETH; + png_setup_paeth_row(png_ptr, bpp, row_buf, row_bytes); + best_filter_value = PNG_FILTER_VALUE_PAETH; } else if ((filter_to_do & PNG_FILTER_PAETH) != 0) @@ -2910,7 +2927,7 @@ png_write_find_filter(png_structrp png_ptr, png_row_infop row_info) } #endif - for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1, + for (i = 0, rp = row_buf + 1, dp = png_ptr->try_row + 1, pp = prev_row + 1; i < bpp; i++) { v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); @@ -2999,15 +3016,38 @@ png_write_find_filter(png_structrp png_ptr, png_row_infop row_info) if (sum < mins) { - best_row = png_ptr->paeth_row; + best_filter_value = PNG_FILTER_VALUE_PAETH; } } -#endif /* WRITE_FILTER */ /* Do the actual writing of the filtered row data from the chosen filter. */ - png_write_filtered_row(png_ptr, best_row, row_info->rowbytes+1); + if (best_filter_value == PNG_FILTER_VALUE_NONE) + png_write_filtered_row(png_ptr, png_ptr->row_buf, row_info->rowbytes+1); + + else + { + png_ptr->try_row[0] = best_filter_value; + + if (best_filter_value != last_tested_row_value) + { + /* TO DO: make this redundant code into functions? */ + if (best_filter_value == PNG_FILTER_VALUE_SUB) + png_setup_sub_row(png_ptr, bpp, row_buf, row_bytes); + + if (best_filter_value == PNG_FILTER_VALUE_UP) + png_setup_up_row(png_ptr, row_buf, row_bytes); + + if (best_filter_value == PNG_FILTER_VALUE_AVG) + png_setup_avg_row(png_ptr, bpp, row_buf, row_bytes); + + if (best_filter_value == PNG_FILTER_VALUE_PAETH) + png_setup_paeth_row(png_ptr, bpp, row_buf, row_bytes); + + } + + png_write_filtered_row(png_ptr, png_ptr->try_row, row_info->rowbytes+1); + } -#ifdef PNG_WRITE_FILTER_SUPPORTED #ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* Save the type of filter we picked this time for future calculations */ if (png_ptr->num_prev_filters > 0) @@ -3019,9 +3059,9 @@ png_write_find_filter(png_structrp png_ptr, png_row_infop row_info) png_ptr->prev_filters[j] = png_ptr->prev_filters[j - 1]; } - png_ptr->prev_filters[j] = best_row[0]; + png_ptr->prev_filters[j] = best_filter_value; } -#endif +#endif /* WRITE_WEIGHTED_FILTER */ #endif /* WRITE_FILTER */ }