Re: Optimizing libpng encode time
Adam Richter <[email protected]> Sat, 29 Aug 2020 19:40:29 -0700
| Newsgroups | gmane.comp.graphics.png.devel |
|---|---|
| Message-ID | <CAGn-TgiC49LUtvr_LzW84b4WaQL4gV3=n1Z_m-gW_MhsXjntYw@mail.gmail.com> |
Hi, everyone.
I have cleaned up that optimization to libpng encoding that I
discussed about a month and a half ago, which I would like to propose
for inclusion along with a couple of trivial patches which I will also
discuss in this message. I have also attached a fourth patch, which
is also intended for optimizing encoding, which is not yet ready to be
merged, but which I would also be happy to discuss.
I have made these patches against the libpng-1.6 branch. submitted the
first as pull request at
https://sourceforge.net/p/libpng/code/merge-requests/ a couple of days
ago, and I am awaiting disposition of that request before submitting
the others. Because that first patch is a pending pull request from
the master branch on the sourceforge git system and I do not want to
mess up that pull request, I have put the other patches on a different
branch, "adam-tmp" at https://sourceforge.net/u/adamjrichter/libpng/ ,
in case anyone wants to take a look at it with git clone.
Here is a summary of the patches. I would welcome any review or
discussion of any of them.
0001 - Delete a two line in png_write_finish_row() that I think can
never be true and has had the property since its first appearance in
the git history in libpng-0.81 (and kudos to, I assume, Guy Schalnat
for creating that amazing history in the git archive from historical
source releases.
0002 - Fix a potential buffer overflow that gcc complains about, which
probably has never occurred, but it's only 6 bytes of stack space.
0003 - This is the main one. It is a clean up of some of the encoding
filter optimizations I discussed in my email to this list in the
middle of June, and I also regard it is a bit of a code clean up,
being a net deletion of 163 lines. Basically, the idea is to evaluate
all five of the available filter strategies in one memory read to the
CPU and then separately execute the winning strategy, instead of the
previous strategy of trying each filter and aborting if its cost
exceeds the cost of the cheapest (that is, best) filter that has been
processed. It's basically trading some extra computation for reducing
unnecessary writes. The libpng-1.7.x branch has a similar idea, where
it tries to do all of the filters in a single pass through the CPU,
but it tries to write output streams for each of them in that process.
It is not obvious which approach should win, but my changes seem to
make the code faster, in part, I think, because this approach is a bit
friendlier to compiler vectorization. Here are some very informal
benchmark results, which are similar to what I discussed in that
previous email. In cases where "-O3" is mentioned, the old and new
code were compiled with "-O3" (basically for the "-ftree-vectorize"
optimization). In most cases, the old code got essentially no speed
improvement from being compiled with "-O3". This patch does not
include a change to turn on "-O3" in the build process.
"make test":
-O2 : 0.51% slow down
-O3: 2.91% speed up
"gm convert [...]" pnm to png: 4488x3508 8 bit color image from a scanner:
-O2: 1.87% speed up
-O3: 8.71% speed up
"gm convert [...]" pnm to png: 2370x3018 8 bit color image from a scanner:
-O2: 11.11% speed up
-O3: 21.07% speed up
By the way, I expect that future versions of the GNU C compiler should
at least have better vectorization of the "average" filter, based on
my look at the gcc git tree and compiling a similar function.
I hope that the performance improvement will be a little helpful, but
i also see this patch as a code clean up that may simplify future
optimization efforts.
0004 - One of those future optimization efforts I have in mind is this
work-in-progress diff for eliminating making an extra copy in memory
of the input data being passed to png_write_rows and similar
functions, such as png_write_image and png_write_png. This
optimization only works when both of the following are true (a)
multiple rows are passed in for encoding and (b) when it is not
necessary to do any of many of the initial transformations of the
input, such as byte swapping, interlaced input, etc. This code seems
to work if filtering is configured (at least "make test" succeeds),
but I need to fix the no-filtering build. Unfortunately, I am seeing
almost know speed up, even though completely commenting out the memcpy
that all of this is trying to avoid, or making it copy only, say,
every even row, results in an unbelievable speed-up of over a factor
of 10 (for no memcpy) for the 4488x3508 image I used in the benchmark
above. if it turns out that that memcpy avoidance is not a real
optimization, these changes may make it easier to do some thread-level
parallelization with OpenMP ("#omp pragma parallel for"...).
Any discussion or review of code would be most welcome. Thanks in
advance for any thoughts on any of this.
Adam
On Tue, Jul 14, 2020 at 6:24 AM Adam Richter <[email protected]> wrote:
>
> Hi.
>
> I am new to libpng development, just looking to avoid duplication of
> effort in a what I expect to be just a brief exploration of ways to
> make PNG encoding run a bit faster.
>
> The current release occurred in the past few months, and I see mention
> of an "upcoming 1.7.x series" for libpng at
> http://www.libpng.org/pub/png/libpng.html , so I infer that some
> development is probably occurring.
>
> Assuming that this mailing list is the right place, I would like to
> pass along a few observations so far, from my initial look, using gcc
> 9.3.0-10ubuntu2 on Ubuntu 20.04 on an i7-4770k, trying just a few .png
> images, each of a different type (scan of a mostly white piece of
> paper with some writing on it, scan of a book page, scan of a magazine
> cover, and a screen snapshot).
>
> What worked:
>
> 1. Compiling pngwutil.c with "-ftree-vectorize" (which is also
> activated by "-O3") to get the compiler to generate single instruction
> multiple data (SIMD) loop vectorization gets about a 1% improvement in
> the GraphicsMagick command "gm convert ______.pnm ______.png", for my
> few test files, and it more import to the next item.
>
> 2. I have attached a patch changing the filter selection code in
> pngwutil.c to first score all filters in one pass (at least in cases
> when certain filters are selected). This change gets mixed results by
> itself, but, with "-ftree-vectorize" it makes that "gm convert"
> command run ~10-15% faster. Basically, the idea here is to reduce the
> number of transfers from memory cache to the CPU. Without this change,
> selecting every filter means the code will read the input row nearly
> five times and write it nearly five times. With the change, the code
> reads the input line twice and writes it once, but computing the
> winning output an extra time. So, with the vectorization, the
> computation is cheap enough so that the reduction in cache-CPU
> transfers outweighs the cost of the additional computation. I suspect
> that this trade-off will also be more beneficial for multiprocessing,
> where memory bandwidth is likely to be more of an area of contention.
> By the way, I hereby release my copyright interest in the attached
> patch to the public domain, so please feel free to incorporate code
> from it if you wish.
>
> 3. For the Paeth encoding, changing the differences from ints to
> shorts made the code slightly faster, especially with the
> vectorization enabled.
>
> What failed:
>
> 4. I briefly tried "#pragma omp parallel for simd (reduce +:"... with
> "-fopenmp" for my filter comparison function, and it resulted in
> slightly worse performance. Doing so without with "simd" keyword
> (using multiple CPU threads) resulted in worse performance, presumably
> because there is too little work in a single scanline to justify the
> threading overhead. I have not yet tried doing the same on the filter
> encoding functions.
>
> 5. Putting "__attribute__ ((target_clones("sse2", "avx", "avx2",
> "default") ))" did not improve my too small benchmark.
>
> I hope these data points are helpful. Any further pointers to how to
> contribute to libpng development would be welcome. Thanks in advance
> for any input.
>
> Adam
_______________________________________________
png-mng-implement mailing list
png-mng-implement-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org
https://lists.sourceforge.net/lists/listinfo/png-mng-implement
0001-pngwutil.c-png_write_finish_row-delete-test-that-can.patch
(text/x-patch, 1.1 KB)
From 45c0be274535415e709f91caf91239a21d4a4a77 Mon Sep 17 00:00:00 2001 From: Adam Richter <[email protected]> Date: Thu, 27 Aug 2020 06:04:12 -0700 Subject: [PATCH 1/3] pngwutil.c png_write_finish_row(): delete test that can never be true. Delete a test ( (png_ptr->transformations & PNG_INTERLACE) != 0 ) that cannot be true at that point, because it is deep in an "else" branch of the same test (line 2037), nothing in between in that thread of execution could have changed png_ptr->transformations, and I do not believe there is any valid use case where another thread should change it between those two tests. --- pngwutil.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/pngwutil.c b/pngwutil.c index 16345e4c0..f81e3d659 100644 --- a/pngwutil.c +++ b/pngwutil.c @@ -2059,9 +2059,6 @@ png_write_finish_row(png_structrp png_ptr) png_pass_ystart[png_ptr->pass]) / png_pass_yinc[png_ptr->pass]; - if ((png_ptr->transformations & PNG_INTERLACE) != 0) - break; - } while (png_ptr->usr_width == 0 || png_ptr->num_rows == 0); } -- 2.25.1
0002-contrib-libtests-pngstest.c-write_one_file-Fix-a-pot.patch
(text/x-patch, 906 B)
From 61745d979d4b9bdd199d3dd61b38f156579635ff Mon Sep 17 00:00:00 2001 From: Adam Richter <[email protected]> Date: Sat, 29 Aug 2020 05:13:03 -0700 Subject: [PATCH 2/3] contrib/libtests/pngstest.c write_one_file(): Fix a potential buffer overflow warned about by gcc 9.3.0, which I think could only start occurring at the 100,001st call to write_on_file(). --- contrib/libtests/pngstest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/libtests/pngstest.c b/contrib/libtests/pngstest.c index a368bf0f4..951d58821 100644 --- a/contrib/libtests/pngstest.c +++ b/contrib/libtests/pngstest.c @@ -3210,7 +3210,7 @@ write_one_file(Image *output, Image *image, int convert_to_8bit) { #ifdef PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED static int counter = 0; - char name[32]; + char name[38]; sprintf(name, "%s%d.png", tmpf, ++counter); -- 2.25.1
0003-Mostly-in-pngwutil.c-in-png_write_filtered_row-and-r.patch
(text/x-patch, 18.7 KB)
From 1b3c0be75e1fed75ce84d8389c1a8d2103b01664 Mon Sep 17 00:00:00 2001 From: Adam Richter <[email protected]> Date: Sat, 29 Aug 2020 10:16:54 -0700 Subject: [PATCH 3/3] Mostly in pngwutil.c, in png_write_filtered_row() and related functions, separate selecting the filter from executing the selected filter, eliminating png_ptr->tst_row the code that supported it, although this patch does leave a placeholder field png_png->tst_row_UNUSED, for binary compatability. --- pngstruct.h | 3 +- pngwrite.c | 24 --- pngwutil.c | 474 ++++++++++++++++++---------------------------------- 3 files changed, 169 insertions(+), 332 deletions(-) diff --git a/pngstruct.h b/pngstruct.h index 8bdc7ce46..26c2b6f5e 100644 --- a/pngstruct.h +++ b/pngstruct.h @@ -230,7 +230,8 @@ struct png_struct_def */ #ifdef PNG_WRITE_FILTER_SUPPORTED png_bytep try_row; /* buffer to save trial row when filtering */ - png_bytep tst_row; /* buffer to save best trial row when filtering */ + png_bytep tst_row_UNUSED; /* No longer used. Retained for binary + compatability. */ #endif size_t info_rowbytes; /* Added in 1.5.4: cache of updated row bytes */ diff --git a/pngwrite.c b/pngwrite.c index 59377a4dd..667e97500 100644 --- a/pngwrite.c +++ b/pngwrite.c @@ -951,10 +951,8 @@ png_write_destroy(png_structrp png_ptr) #ifdef PNG_WRITE_FILTER_SUPPORTED png_free(png_ptr, png_ptr->prev_row); png_free(png_ptr, png_ptr->try_row); - png_free(png_ptr, png_ptr->tst_row); png_ptr->prev_row = NULL; png_ptr->try_row = NULL; - png_ptr->tst_row = NULL; #endif #ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED @@ -1060,7 +1058,6 @@ png_set_filter(png_structrp png_ptr, int method, int filters) */ if (png_ptr->row_buf != NULL) { - int num_filters; png_alloc_size_t buf_size; /* Repeat the checks in png_write_start_row; 1 pixel high or wide @@ -1084,20 +1081,6 @@ png_set_filter(png_structrp png_ptr, int method, int filters) filters &= ~(PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH); } - num_filters = 0; - - if (filters & PNG_FILTER_SUB) - num_filters++; - - if (filters & PNG_FILTER_UP) - num_filters++; - - if (filters & PNG_FILTER_AVG) - num_filters++; - - if (filters & PNG_FILTER_PAETH) - num_filters++; - /* Allocate needed row buffers if they have not already been * allocated. */ @@ -1107,13 +1090,6 @@ png_set_filter(png_structrp png_ptr, int method, int filters) if (png_ptr->try_row == NULL) png_ptr->try_row = png_voidcast(png_bytep, png_malloc(png_ptr, buf_size)); - - if (num_filters > 1) - { - if (png_ptr->tst_row == NULL) - png_ptr->tst_row = png_voidcast(png_bytep, - png_malloc(png_ptr, buf_size)); - } } png_ptr->do_filter = (png_byte)filters; #endif diff --git a/pngwutil.c b/pngwutil.c index f81e3d659..27f89c420 100644 --- a/pngwutil.c +++ b/pngwutil.c @@ -1943,25 +1943,7 @@ png_write_start_row(png_structrp png_ptr) if (((filters & (PNG_FILTER_SUB | PNG_FILTER_UP | PNG_FILTER_AVG | PNG_FILTER_PAETH)) != 0) && png_ptr->try_row == NULL) { - int num_filters = 0; - png_ptr->try_row = png_voidcast(png_bytep, png_malloc(png_ptr, buf_size)); - - if (filters & PNG_FILTER_SUB) - num_filters++; - - if (filters & PNG_FILTER_UP) - num_filters++; - - if (filters & PNG_FILTER_AVG) - num_filters++; - - if (filters & PNG_FILTER_PAETH) - num_filters++; - - if (num_filters > 1) - png_ptr->tst_row = png_voidcast(png_bytep, png_malloc(png_ptr, - buf_size)); } /* We only need to keep the previous row if we are using one of the following @@ -2272,47 +2254,29 @@ png_write_filtered_row(png_structrp png_ptr, png_bytep filtered_row, size_t row_bytes); #ifdef PNG_WRITE_FILTER_SUPPORTED -static size_t /* PRIVATE */ -png_setup_sub_row(png_structrp png_ptr, png_uint_32 bpp, - size_t row_bytes, size_t lmins) -{ - png_bytep rp, dp, lp; - size_t i; - size_t sum = 0; - unsigned int v; - png_ptr->try_row[0] = PNG_FILTER_VALUE_SUB; - - for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1; i < bpp; - i++, rp++, dp++) - { - v = *dp = *rp; +static png_byte +png_byte_abs(png_byte v) +{ #ifdef PNG_USE_ABS - sum += 128 - abs((int)v - 128); + return 128 - abs((int)v - 128); #else - sum += (v < 128) ? v : 256 - v; + return (v < 128) ? v : 256 - v; #endif - } +} - for (lp = png_ptr->row_buf + 1; i < row_bytes; - i++, rp++, lp++, dp++) - { - v = *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff); +static short +png_short_abs(short v) +{ #ifdef PNG_USE_ABS - sum += 128 - abs((int)v - 128); + return abs(v); #else - sum += (v < 128) ? v : 256 - v; + return (v >= 0) ? v : -v; #endif - - if (sum > lmins) /* We are already worse, don't continue. */ - break; - } - - return (sum); } static void /* PRIVATE */ -png_setup_sub_row_only(png_structrp png_ptr, png_uint_32 bpp, +png_setup_sub_row(png_structrp png_ptr, png_uint_32 bpp, size_t row_bytes) { png_bytep rp, dp, lp; @@ -2333,35 +2297,8 @@ png_setup_sub_row_only(png_structrp png_ptr, png_uint_32 bpp, } } -static size_t /* PRIVATE */ -png_setup_up_row(png_structrp png_ptr, size_t row_bytes, size_t lmins) -{ - png_bytep rp, dp, pp; - size_t i; - size_t sum = 0; - unsigned int v; - - png_ptr->try_row[0] = PNG_FILTER_VALUE_UP; - - for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1, - pp = png_ptr->prev_row + 1; i < row_bytes; - i++, rp++, pp++, dp++) - { - v = *dp = (png_byte)(((int)*rp - (int)*pp) & 0xff); -#ifdef PNG_USE_ABS - sum += 128 - abs((int)v - 128); -#else - sum += (v < 128) ? v : 256 - v; -#endif - - if (sum > lmins) /* We are already worse, don't continue. */ - break; - } - - return (sum); -} static void /* PRIVATE */ -png_setup_up_row_only(png_structrp png_ptr, size_t row_bytes) +png_setup_up_row(png_structrp png_ptr, size_t row_bytes) { png_bytep rp, dp, pp; size_t i; @@ -2376,48 +2313,8 @@ png_setup_up_row_only(png_structrp png_ptr, size_t row_bytes) } } -static size_t /* PRIVATE */ -png_setup_avg_row(png_structrp png_ptr, png_uint_32 bpp, - size_t row_bytes, size_t lmins) -{ - png_bytep rp, dp, pp, lp; - png_uint_32 i; - size_t sum = 0; - unsigned int v; - - png_ptr->try_row[0] = PNG_FILTER_VALUE_AVG; - - for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1, - pp = png_ptr->prev_row + 1; i < bpp; i++) - { - v = *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff); - -#ifdef PNG_USE_ABS - sum += 128 - abs((int)v - 128); -#else - sum += (v < 128) ? v : 256 - v; -#endif - } - - for (lp = png_ptr->row_buf + 1; i < row_bytes; i++) - { - v = *dp++ = (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2)) - & 0xff); - -#ifdef PNG_USE_ABS - sum += 128 - abs((int)v - 128); -#else - sum += (v < 128) ? v : 256 - v; -#endif - - if (sum > lmins) /* We are already worse, don't continue. */ - break; - } - - return (sum); -} static void /* PRIVATE */ -png_setup_avg_row_only(png_structrp png_ptr, png_uint_32 bpp, +png_setup_avg_row(png_structrp png_ptr, png_uint_32 bpp, size_t row_bytes) { png_bytep rp, dp, pp, lp; @@ -2438,33 +2335,25 @@ png_setup_avg_row_only(png_structrp png_ptr, png_uint_32 bpp, } } -static size_t /* PRIVATE */ +static void /* PRIVATE */ png_setup_paeth_row(png_structrp png_ptr, png_uint_32 bpp, - size_t row_bytes, size_t lmins) + size_t row_bytes) { png_bytep rp, dp, pp, cp, lp; size_t i; - size_t sum = 0; - unsigned int v; png_ptr->try_row[0] = PNG_FILTER_VALUE_PAETH; for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1, pp = png_ptr->prev_row + 1; i < bpp; i++) { - v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); - -#ifdef PNG_USE_ABS - sum += 128 - abs((int)v - 128); -#else - sum += (v < 128) ? v : 256 - v; -#endif + *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); } for (lp = png_ptr->row_buf + 1, cp = png_ptr->prev_row + 1; i < row_bytes; i++) { - int a, b, c, pa, pb, pc, p; + short a, b, c, pa, pb, pc, p; b = *pp++; c = *cp++; @@ -2473,74 +2362,150 @@ png_setup_paeth_row(png_structrp png_ptr, png_uint_32 bpp, 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 + pa = png_short_abs(p); + pb = png_short_abs(pc); + pc = png_short_abs(p + pc); p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c; - v = *dp++ = (png_byte)(((int)*rp++ - p) & 0xff); + *dp++ = (png_byte)(((int)*rp++ - p) & 0xff); + } +} -#ifdef PNG_USE_ABS - sum += 128 - abs((int)v - 128); -#else - sum += (v < 128) ? v : 256 - v; -#endif +static unsigned int +png_choose_filter_none_vs_sub(png_structrp png_ptr, png_uint_32 bpp, + size_t row_bytes) +{ + png_bytep rp, lp; + size_t i; + size_t sum_none = 0; + size_t sum_sub; - if (sum > lmins) /* We are already worse, don't continue. */ - break; + for (i = 0, rp = png_ptr->row_buf + 1; i < bpp; i++, rp++) + { + sum_none += png_byte_abs(*rp); + } + + sum_sub = sum_none; + + for (lp = png_ptr->row_buf + 1; i < row_bytes; i++, lp++, rp++) + { + png_byte input = *rp; + png_byte left = *lp; + + sum_none += png_byte_abs(input); + sum_sub += png_byte_abs(input - left); } - return (sum); + return (sum_none < sum_sub) ? PNG_FILTER_NONE : PNG_FILTER_SUB; } -static void /* PRIVATE */ -png_setup_paeth_row_only(png_structrp png_ptr, png_uint_32 bpp, - size_t row_bytes) + +static unsigned int +png_choose_filter(png_structrp png_ptr, png_uint_32 bpp, + size_t row_bytes, unsigned int filters) { - png_bytep rp, dp, pp, cp, lp; + png_bytep rp, pp, cp, lp; size_t i; - png_ptr->try_row[0] = PNG_FILTER_VALUE_PAETH; + size_t sum_none = 0; + size_t sum_up = 0; + size_t sum_avg = 0; + size_t sum_sub, sum_paeth; + unsigned int best_filter, sum_least; - for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1, - pp = png_ptr->prev_row + 1; i < bpp; i++) + for (i = 0, rp = png_ptr->row_buf + 1, + pp = png_ptr->prev_row + 1; i < bpp; i++, rp++, pp++) { - *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); + png_byte input = *rp; + png_byte up; + + sum_none += png_byte_abs(input); + up = *pp; + sum_up += png_byte_abs(input - up); + sum_avg += png_byte_abs(input - up/2); } + sum_sub = sum_none; + sum_paeth = sum_up; + for (lp = png_ptr->row_buf + 1, cp = png_ptr->prev_row + 1; i < row_bytes; - i++) + i++, pp++, cp++, lp++, rp++) { - int a, b, c, pa, pb, pc, p; + png_byte input = *rp; + png_byte left, up, avg; + short up_plus_left, a, b, c, pa, pb, pc, p; + unsigned int v_paeth; - b = *pp++; - c = *cp++; - a = *lp++; + sum_none += png_byte_abs(input); + left = *lp; + sum_sub += png_byte_abs(input - left); + + up = *pp; + sum_up += png_byte_abs(input - up); + + up_plus_left = up + left; + avg = up_plus_left >> 1; + sum_avg += png_byte_abs(input - avg); + + b = up; + c = *cp; + a = left; 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 + pa = png_short_abs(p); + pb = png_short_abs(pc); + pc = png_short_abs(p + pc); p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c; + v_paeth = (input - p) & 0xFF; - *dp++ = (png_byte)(((int)*rp++ - p) & 0xff); + sum_paeth += png_byte_abs(v_paeth); } + + best_filter = 0; + sum_least = 0; + + if (filters & PNG_FILTER_NONE) { + best_filter = PNG_FILTER_NONE; + sum_least = sum_none; + } + + if ((filters & PNG_FILTER_SUB) && + (best_filter == 0 || sum_least > sum_sub)) { + + best_filter = PNG_FILTER_SUB; + sum_least = sum_sub; + } + + if ((filters & PNG_FILTER_UP) && + (best_filter == 0 || sum_least > sum_up)) { + + best_filter = PNG_FILTER_UP; + sum_least = sum_up; + } + + if ((filters & PNG_FILTER_AVG) && + (best_filter == 0 || sum_least > sum_avg)) { + + best_filter = PNG_FILTER_AVG; + sum_least = sum_avg; + } + + if ((filters & PNG_FILTER_PAETH) && + (best_filter == 0 || sum_least > sum_paeth)) { + + best_filter = PNG_FILTER_PAETH; + // sum_least = sum_paeth; + } + + if (best_filter == 0) { + png_error(png_ptr, "png_choose_filter: best_filter mask is 0."); + } + return best_filter; } + #endif /* WRITE_FILTER */ void /* PRIVATE */ @@ -2550,10 +2515,9 @@ png_write_find_filter(png_structrp png_ptr, png_row_infop row_info) png_write_filtered_row(png_ptr, png_ptr->row_buf, row_info->rowbytes+1); #else unsigned int filter_to_do = png_ptr->do_filter; - png_bytep row_buf; + unsigned int lowest_filter = png_ptr->do_filter; png_bytep best_row; png_uint_32 bpp; - size_t mins; size_t row_bytes = row_info->rowbytes; png_debug(1, "in png_write_find_filter"); @@ -2561,10 +2525,6 @@ png_write_find_filter(png_structrp png_ptr, png_row_infop row_info) /* Find out how many bytes offset each pixel is */ bpp = (row_info->pixel_depth + 7) >> 3; - row_buf = png_ptr->row_buf; - mins = PNG_SIZE_MAX - 256/* so we can detect potential overflow of the - running sum */; - /* The prediction method we use is to find which method provides the * smallest value when summing the absolute values of the distances * from zero, using anything >= 128 as negative numbers. This is known @@ -2593,143 +2553,43 @@ png_write_find_filter(png_structrp png_ptr, png_row_infop row_info) /* We don't need to test the 'no filter' case if this is the only filter * that has been chosen, as it doesn't actually do anything to the data. */ - best_row = png_ptr->row_buf; + lowest_filter = filter_to_do & (0U - filter_to_do); if (PNG_SIZE_MAX/128 <= row_bytes) { /* Overflow can occur in the calculation, just select the lowest set * filter. */ - filter_to_do &= 0U-filter_to_do; - } - else if ((filter_to_do & PNG_FILTER_NONE) != 0 && - filter_to_do != PNG_FILTER_NONE) - { - /* Overflow not possible and multiple filters in the list, including the - * 'none' filter. - */ - png_bytep rp; - size_t sum = 0; - size_t i; - unsigned int v; - - { - for (i = 0, rp = row_buf + 1; i < row_bytes; i++, rp++) - { - v = *rp; -#ifdef PNG_USE_ABS - sum += 128 - abs((int)v - 128); -#else - sum += (v < 128) ? v : 256 - v; -#endif - } - } - - mins = sum; - } - - /* Sub filter */ - if (filter_to_do == PNG_FILTER_SUB) - /* It's the only filter so no testing is needed */ - { - png_setup_sub_row_only(png_ptr, bpp, row_bytes); - best_row = png_ptr->try_row; - } - - else if ((filter_to_do & PNG_FILTER_SUB) != 0) - { - size_t sum; - size_t lmins = mins; - - sum = png_setup_sub_row(png_ptr, bpp, row_bytes, lmins); - - if (sum < mins) - { - mins = sum; - best_row = png_ptr->try_row; - if (png_ptr->tst_row != NULL) - { - png_ptr->try_row = png_ptr->tst_row; - png_ptr->tst_row = best_row; - } - } - } - - /* Up filter */ - if (filter_to_do == PNG_FILTER_UP) - { - png_setup_up_row_only(png_ptr, row_bytes); - best_row = png_ptr->try_row; - } - - else if ((filter_to_do & PNG_FILTER_UP) != 0) - { - size_t sum; - size_t lmins = mins; - - sum = png_setup_up_row(png_ptr, row_bytes, lmins); - - if (sum < mins) - { - mins = sum; - best_row = png_ptr->try_row; - if (png_ptr->tst_row != NULL) - { - png_ptr->try_row = png_ptr->tst_row; - png_ptr->tst_row = best_row; - } - } - } - - /* Avg filter */ - if (filter_to_do == PNG_FILTER_AVG) - { - png_setup_avg_row_only(png_ptr, bpp, row_bytes); - best_row = png_ptr->try_row; - } - - else if ((filter_to_do & PNG_FILTER_AVG) != 0) - { - size_t sum; - size_t lmins = mins; - - sum= png_setup_avg_row(png_ptr, bpp, row_bytes, lmins); - - if (sum < mins) - { - mins = sum; - best_row = png_ptr->try_row; - if (png_ptr->tst_row != NULL) - { - png_ptr->try_row = png_ptr->tst_row; - png_ptr->tst_row = best_row; - } - } - } - - /* Paeth filter */ - if (filter_to_do == PNG_FILTER_PAETH) - { - png_setup_paeth_row_only(png_ptr, bpp, row_bytes); - best_row = png_ptr->try_row; - } - - else if ((filter_to_do & PNG_FILTER_PAETH) != 0) - { - size_t sum; - size_t lmins = mins; - - sum = png_setup_paeth_row(png_ptr, bpp, row_bytes, lmins); - - if (sum < mins) - { - best_row = png_ptr->try_row; - if (png_ptr->tst_row != NULL) - { - png_ptr->try_row = png_ptr->tst_row; - png_ptr->tst_row = best_row; - } - } + filter_to_do = lowest_filter; + } + else if (filter_to_do == lowest_filter) + ; /* do nothing */ + else if ((filter_to_do & (PNG_FILTER_UP | PNG_FILTER_AVG | PNG_FILTER_PAETH)) != 0) { + filter_to_do = png_choose_filter(png_ptr, bpp, row_bytes, filter_to_do); + } else { + filter_to_do = png_choose_filter_none_vs_sub(png_ptr, bpp, row_bytes); + } + best_row = png_ptr->try_row; + switch (filter_to_do) { + case 0: + case PNG_FILTER_NONE: + best_row = png_ptr->row_buf; + break; + case PNG_FILTER_SUB: + png_setup_sub_row(png_ptr, bpp, row_bytes); + break; + case PNG_FILTER_UP: + png_setup_up_row(png_ptr, row_bytes); + break; + case PNG_FILTER_AVG: + png_setup_avg_row(png_ptr, bpp, row_bytes); + break; + case PNG_FILTER_PAETH: + png_setup_paeth_row(png_ptr, bpp, row_bytes); + break; + default: + png_error(png_ptr, "Invalid filter_to_do"); + break; } /* Do the actual writing of the filtered row data from the chosen filter. */ -- 2.25.1
0004-libpng-encode-copying-avoidance.work-in-progress.diff
(text/x-patch, 17 KB)
diff --git a/pngpriv.h b/pngpriv.h
index 583c26f9b..59e9cde60 100644
--- a/pngpriv.h
+++ b/pngpriv.h
@@ -1366,7 +1366,8 @@ PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_sse2,(png_row_infop
/* Choose the best filter to use and filter the row data */
PNG_INTERNAL_FUNCTION(void,png_write_find_filter,(png_structrp png_ptr,
- png_row_infop row_info),PNG_EMPTY);
+ png_row_infop row_info, png_const_bytep row_in,
+ png_const_bytep prev_row_in), PNG_EMPTY);
#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
PNG_INTERNAL_FUNCTION(void,png_read_IDAT_data,(png_structrp png_ptr,
@@ -1717,7 +1718,8 @@ PNG_INTERNAL_FUNCTION(void,png_check_IHDR,(png_const_structrp png_ptr,
#if defined(PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED) || \
defined(PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED)
PNG_INTERNAL_FUNCTION(void,png_do_check_palette_indexes,
- (png_structrp png_ptr, png_row_infop row_info),PNG_EMPTY);
+ (png_structrp png_ptr, png_row_infop row_info,
+ const png_const_bytep row_in),PNG_EMPTY);
#endif
#if defined(PNG_FLOATING_POINT_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED)
diff --git a/pngrtran.c b/pngrtran.c
index 238f5afe7..4ae5427f4 100644
--- a/pngrtran.c
+++ b/pngrtran.c
@@ -4980,7 +4980,7 @@ png_do_read_transformations(png_structrp png_ptr, png_row_infop row_info)
/* Added at libpng-1.5.10 */
if (row_info->color_type == PNG_COLOR_TYPE_PALETTE &&
png_ptr->num_palette_max >= 0)
- png_do_check_palette_indexes(png_ptr, row_info);
+ png_do_check_palette_indexes(png_ptr, row_info, png_ptr->row_buf + 1);
#endif
#ifdef PNG_READ_BGR_SUPPORTED
diff --git a/pngtrans.c b/pngtrans.c
index 1100f46eb..523f72b78 100644
--- a/pngtrans.c
+++ b/pngtrans.c
@@ -696,7 +696,8 @@ png_do_bgr(png_row_infop row_info, png_bytep row)
defined(PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED)
/* Added at libpng-1.5.10 */
void /* PRIVATE */
-png_do_check_palette_indexes(png_structrp png_ptr, png_row_infop row_info)
+png_do_check_palette_indexes(png_structrp png_ptr, png_row_infop row_info,
+ const png_const_bytep row_in)
{
if (png_ptr->num_palette < (1 << row_info->bit_depth) &&
png_ptr->num_palette > 0) /* num_palette can be 0 in MNG files */
@@ -708,7 +709,8 @@ png_do_check_palette_indexes(png_structrp png_ptr, png_row_infop row_info)
* forms produced on either GCC or MSVC.
*/
int padding = PNG_PADBITS(row_info->pixel_depth, row_info->width);
- png_bytep rp = png_ptr->row_buf + row_info->rowbytes - 1;
+ // png_bytep rp = png_ptr->row_buf + row_info->rowbytes - 1;
+ png_bytep rp = row_in + row_info->rowbytes - 1;
switch (row_info->bit_depth)
{
@@ -717,7 +719,7 @@ png_do_check_palette_indexes(png_structrp png_ptr, png_row_infop row_info)
/* in this case, all bytes must be 0 so we don't need
* to unpack the pixels except for the rightmost one.
*/
- for (; rp > png_ptr->row_buf; rp--)
+ for (; rp >= row_in; rp--)
{
if ((*rp >> padding) != 0)
png_ptr->num_palette_max = 1;
@@ -729,7 +731,7 @@ png_do_check_palette_indexes(png_structrp png_ptr, png_row_infop row_info)
case 2:
{
- for (; rp > png_ptr->row_buf; rp--)
+ for (; rp >= row_in; rp--)
{
int i = ((*rp >> padding) & 0x03);
@@ -759,7 +761,7 @@ png_do_check_palette_indexes(png_structrp png_ptr, png_row_infop row_info)
case 4:
{
- for (; rp > png_ptr->row_buf; rp--)
+ for (; rp >= row_in; rp--)
{
int i = ((*rp >> padding) & 0x0f);
@@ -779,7 +781,7 @@ png_do_check_palette_indexes(png_structrp png_ptr, png_row_infop row_info)
case 8:
{
- for (; rp > png_ptr->row_buf; rp--)
+ for (; rp >= row_in; rp--)
{
if (*rp > png_ptr->num_palette_max)
png_ptr->num_palette_max = (int) *rp;
diff --git a/pngwrite.c b/pngwrite.c
index 667e97500..4f12c668c 100644
--- a/pngwrite.c
+++ b/pngwrite.c
@@ -570,6 +570,10 @@ png_create_write_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr,
}
+static void
+png_write_row_given_prev(png_structrp png_ptr, png_const_bytep row,
+ png_const_bytep prev_row_in, int must_save_row);
+
/* Write a few rows of image data. If the image is interlaced,
* either you will have to write the 7 sub images, or, if you
* have called png_set_interlace_handling(), you will have to
@@ -581,16 +585,20 @@ png_write_rows(png_structrp png_ptr, png_bytepp row,
{
png_uint_32 i; /* row counter */
png_bytepp rp; /* row pointer */
+ png_const_bytep prev_row_in;
png_debug(1, "in png_write_rows");
if (png_ptr == NULL)
return;
+ prev_row_in = NULL;
/* Loop through the rows */
- for (i = 0, rp = row; i < num_rows; i++, rp++)
- {
- png_write_row(png_ptr, *rp);
+ for (i = 0, rp = row; i < num_rows; i++, rp++) {
+ png_const_bytep row_in = *rp;
+ int /* bool */ is_last_row = (i == (num_rows - 1) );
+ png_write_row_given_prev(png_ptr, row_in, prev_row_in, is_last_row);
+ prev_row_in = row_in;
}
}
@@ -600,9 +608,7 @@ png_write_rows(png_structrp png_ptr, png_bytepp row,
void PNGAPI
png_write_image(png_structrp png_ptr, png_bytepp image)
{
- png_uint_32 i; /* row index */
int pass, num_pass; /* pass variables */
- png_bytepp rp; /* points to current row */
if (png_ptr == NULL)
return;
@@ -621,10 +627,7 @@ png_write_image(png_structrp png_ptr, png_bytepp image)
for (pass = 0; pass < num_pass; pass++)
{
/* Loop through image */
- for (i = 0, rp = image; i < png_ptr->height; i++, rp++)
- {
- png_write_row(png_ptr, *rp);
- }
+ png_write_rows(png_ptr, image, png_ptr->height);
}
}
@@ -693,12 +696,31 @@ png_do_write_intrapixel(png_row_infop row_info, png_bytep row)
}
#endif /* MNG_FEATURES */
+static int /* bool */
+png_want_writable_row_buf(const png_const_structrp png_ptr)
+{
+ /* TODO/FIXME: Determine which flags in png_ptr->transformations
+ want to modify the contents of the input row buffer, and check
+ only those instead of traformations != 0. */
+
+ return (png_ptr->interlaced != 0)
+ || (png_ptr->transformations != 0)
+#if defined(PNG_MNG_FEATURES_SUPPORTED)
+ || ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) != 0 &&
+ (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING))
+#endif
+ ;
+}
+
/* Called by user to write a row of image data */
-void PNGAPI
-png_write_row(png_structrp png_ptr, png_const_bytep row)
+static void
+png_write_row_given_prev(png_structrp png_ptr, png_const_bytep row,
+ png_const_bytep prev_row_in, int must_save_row)
{
/* 1.5.6: moved from png_struct to be a local structure: */
png_row_info row_info;
+ png_const_bytep row_in;
+ int /* bool */ want_writable_row;
if (png_ptr == NULL)
return;
@@ -839,7 +861,13 @@ png_write_row(png_structrp png_ptr, png_const_bytep row)
png_debug1(3, "row_info->rowbytes = %lu", (unsigned long)row_info.rowbytes);
/* Copy user's row into buffer, leaving room for filter byte. */
- memcpy(png_ptr->row_buf + 1, row, row_info.rowbytes);
+ want_writable_row = png_want_writable_row_buf(png_ptr);
+ if (must_save_row || want_writable_row) {
+ png_bytep buf = png_ptr->row_buf + 1;
+ memcpy(buf, row, row_info.rowbytes);
+ row_in = buf;
+ } else
+ row_in = row;
#ifdef PNG_WRITE_INTERLACING_SUPPORTED
/* Handle interlacing */
@@ -892,16 +920,30 @@ png_write_row(png_structrp png_ptr, png_const_bytep row)
/* Check for out-of-range palette index */
if (row_info.color_type == PNG_COLOR_TYPE_PALETTE &&
png_ptr->num_palette_max >= 0)
- png_do_check_palette_indexes(png_ptr, &row_info);
+ png_do_check_palette_indexes(png_ptr, &row_info, row_in);
#endif
+ /* If want_writable_row is true, then that means that the bytes
+ submitted that prev_row_in points to may not be exactly the
+ previous line that the filters want to compare against, so
+ use png_ptr->prev_row, which saves the row after transformations. */
+ if (prev_row_in == NULL || want_writable_row)
+ prev_row_in = png_ptr->prev_row + 1;
+
/* Find a filter if necessary, filter the row and write it out. */
- png_write_find_filter(png_ptr, &row_info);
+ png_write_find_filter(png_ptr, &row_info, row_in, prev_row_in);
if (png_ptr->write_row_fn != NULL)
(*(png_ptr->write_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass);
}
+/* Called by user to write a row of image data */
+void PNGAPI
+png_write_row(png_structrp png_ptr, png_const_bytep row)
+{
+ png_write_row_given_prev(png_ptr, row, NULL, 1);
+}
+
#ifdef PNG_WRITE_FLUSH_SUPPORTED
/* Set the automatic flush interval or 0 to turn flushing off */
void PNGAPI
diff --git a/pngwutil.c b/pngwutil.c
index 27f89c420..f72cdca60 100644
--- a/pngwutil.c
+++ b/pngwutil.c
@@ -1940,8 +1940,11 @@ png_write_start_row(png_structrp png_ptr)
png_ptr->do_filter = filters;
- if (((filters & (PNG_FILTER_SUB | PNG_FILTER_UP | PNG_FILTER_AVG |
- PNG_FILTER_PAETH)) != 0) && png_ptr->try_row == NULL)
+ // TODO/FIXME. The following commented out test for filter values may
+ // soon return once the memcpy in png_write_find_filter for handling of
+ // PNG_FILTER_NONE can be removed.
+ if ( /* ((filters & (PNG_FILTER_SUB | PNG_FILTER_UP | PNG_FILTER_AVG |
+ PNG_FILTER_PAETH)) != 0) && */ png_ptr->try_row == NULL)
{
png_ptr->try_row = png_voidcast(png_bytep, png_malloc(png_ptr, buf_size));
}
@@ -2276,37 +2279,39 @@ png_short_abs(short v)
}
static void /* PRIVATE */
-png_setup_sub_row(png_structrp png_ptr, png_uint_32 bpp,
- size_t row_bytes)
+png_setup_sub_row(png_structrp png_ptr, const png_const_bytep row_in,
+ png_uint_32 bpp, size_t row_bytes)
{
- png_bytep rp, dp, lp;
+ png_const_bytep rp, lp;
+ png_bytep dp;
size_t i;
png_ptr->try_row[0] = PNG_FILTER_VALUE_SUB;
- for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1; i < bpp;
+ for (i = 0, rp = row_in, dp = png_ptr->try_row + 1; i < bpp;
i++, rp++, dp++)
{
*dp = *rp;
}
- for (lp = png_ptr->row_buf + 1; i < row_bytes;
- i++, rp++, lp++, dp++)
+ for (lp = row_in; 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, size_t row_bytes)
+png_setup_up_row(png_structrp png_ptr, const png_const_bytep row_in,
+ const png_const_bytep prev_row_in, size_t row_bytes)
{
- png_bytep rp, dp, pp;
+ png_const_bytep rp, pp;
+ png_bytep dp;
size_t i;
png_ptr->try_row[0] = PNG_FILTER_VALUE_UP;
- for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1,
- pp = png_ptr->prev_row + 1; i < row_bytes;
+ for (i = 0, rp = row_in, dp = png_ptr->try_row + 1,
+ pp = prev_row_in; i < row_bytes;
i++, rp++, pp++, dp++)
{
*dp = (png_byte)(((int)*rp - (int)*pp) & 0xff);
@@ -2314,21 +2319,22 @@ png_setup_up_row(png_structrp png_ptr, size_t row_bytes)
}
static void /* PRIVATE */
-png_setup_avg_row(png_structrp png_ptr, png_uint_32 bpp,
- size_t row_bytes)
+png_setup_avg_row(png_structrp png_ptr, const png_const_bytep row_in,
+ const png_const_bytep prev_row_in, png_uint_32 bpp, size_t row_bytes)
{
- png_bytep rp, dp, pp, lp;
+ png_const_bytep rp, pp, lp;
+ png_bytep dp;
png_uint_32 i;
png_ptr->try_row[0] = PNG_FILTER_VALUE_AVG;
- for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1,
- pp = png_ptr->prev_row + 1; i < bpp; i++)
+ for (i = 0, rp = row_in, dp = png_ptr->try_row + 1,
+ pp = prev_row_in; i < bpp; i++)
{
*dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff);
}
- for (lp = png_ptr->row_buf + 1; i < row_bytes; i++)
+ for (lp = row_in; i < row_bytes; i++)
{
*dp++ = (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2))
& 0xff);
@@ -2336,21 +2342,22 @@ png_setup_avg_row(png_structrp png_ptr, png_uint_32 bpp,
}
static void /* PRIVATE */
-png_setup_paeth_row(png_structrp png_ptr, png_uint_32 bpp,
- size_t row_bytes)
+png_setup_paeth_row(png_structrp png_ptr, const png_const_bytep row_in,
+ const png_const_bytep prev_row_in, png_uint_32 bpp, size_t row_bytes)
{
- png_bytep rp, dp, pp, cp, lp;
+ png_const_bytep rp, pp, cp, lp;
+ png_bytep dp;
size_t i;
png_ptr->try_row[0] = PNG_FILTER_VALUE_PAETH;
- for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1,
- pp = png_ptr->prev_row + 1; i < bpp; i++)
+ for (i = 0, rp = row_in, dp = png_ptr->try_row + 1,
+ pp = prev_row_in; i < bpp; i++)
{
*dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff);
}
- for (lp = png_ptr->row_buf + 1, cp = png_ptr->prev_row + 1; i < row_bytes;
+ for (lp = row_in, cp = prev_row_in; i < row_bytes;
i++)
{
short a, b, c, pa, pb, pc, p;
@@ -2373,22 +2380,22 @@ png_setup_paeth_row(png_structrp png_ptr, png_uint_32 bpp,
}
static unsigned int
-png_choose_filter_none_vs_sub(png_structrp png_ptr, png_uint_32 bpp,
- size_t row_bytes)
+png_choose_filter_none_vs_sub(png_structrp png_ptr,
+ const png_const_bytep row_in, png_uint_32 bpp, size_t row_bytes)
{
- png_bytep rp, lp;
+ png_const_bytep rp, lp;
size_t i;
size_t sum_none = 0;
size_t sum_sub;
- for (i = 0, rp = png_ptr->row_buf + 1; i < bpp; i++, rp++)
+ for (i = 0, rp = row_in; i < bpp; i++, rp++)
{
sum_none += png_byte_abs(*rp);
}
sum_sub = sum_none;
- for (lp = png_ptr->row_buf + 1; i < row_bytes; i++, lp++, rp++)
+ for (lp = row_in; i < row_bytes; i++, lp++, rp++)
{
png_byte input = *rp;
png_byte left = *lp;
@@ -2401,10 +2408,11 @@ png_choose_filter_none_vs_sub(png_structrp png_ptr, png_uint_32 bpp,
}
static unsigned int
-png_choose_filter(png_structrp png_ptr, png_uint_32 bpp,
+png_choose_filter(png_structrp png_ptr, const png_const_bytep row_in,
+ const png_const_bytep prev_row_in, png_uint_32 bpp,
size_t row_bytes, unsigned int filters)
{
- png_bytep rp, pp, cp, lp;
+ png_const_bytep rp, pp, cp, lp;
size_t i;
size_t sum_none = 0;
@@ -2413,8 +2421,7 @@ png_choose_filter(png_structrp png_ptr, png_uint_32 bpp,
size_t sum_sub, sum_paeth;
unsigned int best_filter, sum_least;
- for (i = 0, rp = png_ptr->row_buf + 1,
- pp = png_ptr->prev_row + 1; i < bpp; i++, rp++, pp++)
+ for (i = 0, rp = row_in, pp = prev_row_in; i < bpp; i++, rp++, pp++)
{
png_byte input = *rp;
png_byte up;
@@ -2428,7 +2435,7 @@ png_choose_filter(png_structrp png_ptr, png_uint_32 bpp,
sum_sub = sum_none;
sum_paeth = sum_up;
- for (lp = png_ptr->row_buf + 1, cp = png_ptr->prev_row + 1; i < row_bytes;
+ for (lp = row_in, cp = prev_row_in; i < row_bytes;
i++, pp++, cp++, lp++, rp++)
{
png_byte input = *rp;
@@ -2509,7 +2516,8 @@ png_choose_filter(png_structrp png_ptr, png_uint_32 bpp,
#endif /* WRITE_FILTER */
void /* PRIVATE */
-png_write_find_filter(png_structrp png_ptr, png_row_infop row_info)
+png_write_find_filter(png_structrp png_ptr, const png_row_infop row_info,
+ const png_const_bytep row_in, png_const_bytep prev_row_in)
{
#ifndef PNG_WRITE_FILTER_SUPPORTED
png_write_filtered_row(png_ptr, png_ptr->row_buf, row_info->rowbytes+1);
@@ -2565,27 +2573,34 @@ png_write_find_filter(png_structrp png_ptr, png_row_infop row_info)
else if (filter_to_do == lowest_filter)
; /* do nothing */
else if ((filter_to_do & (PNG_FILTER_UP | PNG_FILTER_AVG | PNG_FILTER_PAETH)) != 0) {
- filter_to_do = png_choose_filter(png_ptr, bpp, row_bytes, filter_to_do);
+ filter_to_do = png_choose_filter(png_ptr, row_in, prev_row_in, bpp,
+ row_bytes, filter_to_do);
} else {
- filter_to_do = png_choose_filter_none_vs_sub(png_ptr, bpp, row_bytes);
+ filter_to_do = png_choose_filter_none_vs_sub(png_ptr, row_in, bpp,
+ row_bytes);
}
best_row = png_ptr->try_row;
switch (filter_to_do) {
case 0:
case PNG_FILTER_NONE:
- best_row = png_ptr->row_buf;
+ if (row_in == png_ptr->row_buf + 1)
+ best_row = png_ptr->row_buf;
+ else {
+ png_ptr->try_row[0] = PNG_FILTER_VALUE_NONE;
+ memcpy(png_ptr->try_row + 1, row_in, row_bytes);
+ }
break;
case PNG_FILTER_SUB:
- png_setup_sub_row(png_ptr, bpp, row_bytes);
+ png_setup_sub_row(png_ptr, row_in, bpp, row_bytes);
break;
case PNG_FILTER_UP:
- png_setup_up_row(png_ptr, row_bytes);
+ png_setup_up_row(png_ptr, row_in, prev_row_in, row_bytes);
break;
case PNG_FILTER_AVG:
- png_setup_avg_row(png_ptr, bpp, row_bytes);
+ png_setup_avg_row(png_ptr, row_in, prev_row_in, bpp, row_bytes);
break;
case PNG_FILTER_PAETH:
- png_setup_paeth_row(png_ptr, bpp, row_bytes);
+ png_setup_paeth_row(png_ptr, row_in, prev_row_in, bpp, row_bytes);
break;
default:
png_error(png_ptr, "Invalid filter_to_do");