From 0f2554a5e628b23ca05bd028ffac9957ffcf99b3 Mon Sep 17 00:00:00 2001 From: John Bowler Date: Sun, 22 Nov 2015 19:41:41 -0800 Subject: [PATCH] Squashed commit of the following: commit fc4b42b1d56f95efeb1b9fe42dc35b7d98d246bb Merge: 74516c7 9eb1413 Author: John Bowler Date: Sun Nov 22 19:37:54 2015 -0800 Merge branch 'libpng17' into libpng17-filter-enhancements commit 74516c7257f1a28a69985684c5673caa390c700a Author: John Bowler Date: Sun Nov 22 19:32:43 2015 -0800 Make check full pass on gcc/g++ x86_64 Signed-off-by: John Bowler commit e891e34737fc0bc9ee873a5d56b83c1e777b990c Author: John Bowler Date: Sun Nov 22 12:01:37 2015 -0800 Checkpoint: write buffering changes This version fails in pngvalid --size because of an error handling very narrow images, otherwise a standard build passes make check. Signed-off-by: John Bowler commit 457a046ebdab737eefb477126cf855e49df6de50 Author: John Bowler Date: Sun Nov 22 06:39:36 2015 -0800 Fix previous bad merge commit b4f426c97267317637d43f41fe0b05d1659bc63d Merge: 07b9b90 a3458a6 Author: John Bowler Date: Sun Nov 22 06:32:34 2015 -0800 Merge branch 'libpng17' into libpng17-filter-enhancements commit 07b9b90dfd653b744dbc3710b096facf6b4605f6 Merge: ed43306 5592e0b Author: John Bowler Date: Sat Nov 21 17:07:23 2015 -0800 Merge branch 'libpng17' into libpng17-filter-enhancements commit ed43306599f7039a90187862db82273fca3a4c3d Merge: 772aed7 d3c0359 Author: John Bowler Date: Tue Nov 17 17:47:26 2015 -0800 Merge branch 'libpng17' into libpng17-filter-enhancements commit 772aed72378df9c8fccc5a4594b095d02d410a9c Merge: 73ae431 801608f Author: John Bowler Date: Mon Nov 16 14:26:38 2015 -0800 Merge branch 'libpng17' into libpng17-filter-enhancements commit 73ae4316cb6db7d7f6756583a1c213c35ca4e3f4 Merge: 687e6e3 c09b3ab Author: John Bowler Date: Sun Nov 15 09:31:30 2015 -0800 Merge branch 'libpng17' into libpng17-filter-enhancements commit 687e6e393e9d0220c2a12ec474aa01b83c5e9f25 Merge: fedd6da e916d9b Author: John Bowler Date: Thu Nov 5 08:45:14 2015 -0800 Merge branch 'libpng17' into libpng17-filter-enhancements commit fedd6da8798a14b2e002b0bc1379f5a09a03598a Merge: 2e2fc5f ea41fd2 Author: John Bowler Date: Tue Nov 3 21:05:01 2015 -0800 Merge branch 'libpng17' into libpng17-filter-enhancements commit 2e2fc5f6d7678b710c52b7ea081ac4add677d8d5 Merge: 990d5f8 5b05197 Author: John Bowler Date: Mon Oct 12 08:28:30 2015 -0700 Merge branch 'libpng17' into libpng17-filter-enhancements commit 990d5f88688635dc0888657b689e30ffe7e7a7b3 Author: John Bowler Date: Sun Oct 4 17:04:53 2015 -0700 Read row buffer changes The read code now allocates one row buffer of the size of the input PNG row and, only if required, one buffer of the size of the output. The output buffer is required for the progressive reader (always) and for the sequential reader if libpng is de-interlacing an image (because the output row is used multiple times if png_read_row is called with a display row parameter.) This should reduce memory utilization by libpng significantly, but it has no detectable effect on overall performance figures of the test programs, these are probably dominated by memory allocations for the whole image within the test programs. Signed-off-by: John Bowler commit 527bf989bf0e30440f9e07a5544a6ebb1d6fd039 Merge: 50ebbc2 9099254 Author: John Bowler Date: Sat Oct 3 13:39:17 2015 -0700 Merge branch 'libpng17' into libpng17-filter-enhancements commit 50ebbc2c9a24cf1a6b428db53d55fbd5af4d6be6 Merge: 21a7f40 2cd6d56 Author: John Bowler Date: Sat Oct 3 11:16:32 2015 -0700 Merge branch 'libpng17' into libpng17-filter-enhancements commit 21a7f401ab40c79ead9e35882a8066e2cf1d6902 Merge: b512e1c 15a143e Author: John Bowler Date: Wed Sep 30 19:01:23 2015 -0700 Merge branch 'libpng17' into libpng17-filter-enhancements commit b512e1c2c5bfe6df8b6dca32f862d325ec22115e Author: John Bowler Date: Wed Sep 30 17:33:34 2015 -0700 Transform rewrite: perform transforms in small chunks The intent of this change is to reduce the memory footprint during transform sequences by performing transforms in fixed (small) sized blocks of pixels. The change is incomplete; the filter code still works row-by-row, so the whole tranform also works row-by-row, the intent is to fix this so that everything works in small(ish) chunks. At present the change has no discernable effect on pngvalid --speed or pngstest with (e.g.) rgb-8-1.8.png; user time and (minor) page faults are the same in old and new versions. The same applies to real-world 15MP PNG images; even on these the presence of the filter code causes a cyclical progress through memory which will interfere with any caching otherwise possible (useful word, 'otherwise'.) Signed-off-by: John Bowler commit 781cb3699b92beb0e6bc5e03cef8fba820267082 Author: John Bowler Date: Wed Sep 30 17:12:53 2015 -0700 Fix NO_WRITE_INTERLACE in pngvalid.c The support for writing interlaced images directly from libpng 1.7 was unintentionally disabled (INTERLACE_LAST was defined incorrectly, excluding the interlaced images). This obscured the fact that the transform and error test case generators lacked the support for writing interlaced images from libpng. Signed-off-by: John Bowler commit 406ee2fd7946a384f1d7713712dc646080c5c52c Author: John Bowler Date: Wed Sep 30 17:11:40 2015 -0700 Add pngvalid --transform --interlace test This increases code coverage by generating test cases with smaller length rows as a result of the interlacing. Without this packswap handling was incompletely tested. Signed-off-by: John Bowler Signed-off-by: John Bowler --- Makefile.am | 2 +- contrib/libtests/makepng.c | 13 +- contrib/libtests/pngvalid.c | 190 +++-- png.c | 106 +++ png.h | 19 +- pngpread.c | 62 +- pngpriv.h | 145 +++- pngread.c | 71 +- pngrutil.c | 1160 +++++++++++++++------------- pngstruct.h | 61 +- pngtest.c | 15 +- pngtest.png | Bin 8695 -> 5699 bytes pngtrans.c | 203 ++--- pngwrite.c | 997 ++++++++++-------------- pngwtran.c | 230 ------ pngwutil.c | 1069 ++++++++++++++++--------- scripts/pnglibconf.dfa | 7 +- tests/pngvalid-interlace-transform | 2 + 18 files changed, 2299 insertions(+), 2053 deletions(-) create mode 100755 tests/pngvalid-interlace-transform diff --git a/Makefile.am b/Makefile.am index 13b8cc002..02546d0b9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -53,7 +53,7 @@ TESTS =\ tests/pngvalid-gamma-threshold tests/pngvalid-gamma-transform\ tests/pngvalid-progressive-size\ tests/pngvalid-progressive-interlace-standard\ - tests/pngvalid-transform\ + tests/pngvalid-transform tests/pngvalid-interlace-transform\ tests/pngvalid-progressive-standard tests/pngvalid-standard\ tests/pngstest-1.8 tests/pngstest-1.8-alpha tests/pngstest-linear\ tests/pngstest-linear-alpha tests/pngstest-none tests/pngstest-none-alpha\ diff --git a/contrib/libtests/makepng.c b/contrib/libtests/makepng.c index 1d3a0addd..164e0ad7d 100644 --- a/contrib/libtests/makepng.c +++ b/contrib/libtests/makepng.c @@ -148,6 +148,13 @@ #include +#if PNG_LIBPNG_VER < 10700 + /* WRITE_INTERLACING was used instead of WRITE_INTERLACE prior to 1.7 */ +# ifdef PNG_WRITE_INTERLACING_SUPPORTED +# define PNG_WRITE_INTERLACE_SUPPORTED +# endif +#endif /* WRITE_INTERLACE check */ + /* Work round for GCC complaints about casting a (double) function result to * an unsigned: */ @@ -944,11 +951,11 @@ write_png(const char **name, FILE *fp, int color_type, int bit_depth, png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, filters); { -# ifdef PNG_WRITE_INTERLACING_SUPPORTED +# ifdef PNG_WRITE_INTERLACE_SUPPORTED int passes = png_set_interlace_handling(png_ptr); -# else /* !WRITE_INTERLACING */ +# else /* !WRITE_INTERLACE */ int passes = 1; -# endif /* !WRITE_INTERLACING */ +# endif /* !WRITE_INTERLACE */ int pass; png_size_t rowbytes = png_get_rowbytes(png_ptr, info_ptr); diff --git a/contrib/libtests/pngvalid.c b/contrib/libtests/pngvalid.c index 47a24065c..26550086a 100644 --- a/contrib/libtests/pngvalid.c +++ b/contrib/libtests/pngvalid.c @@ -120,6 +120,10 @@ typedef png_byte *png_const_bytep; /* READ_INTERLACING was used instead of READ_DEINTERLACE. */ # ifdef PNG_READ_INTERLACING_SUPPORTED # define PNG_READ_DEINTERLACE_SUPPORTED +# endif + /* WRITE_INTERLACING was used instead of WRITE_INTERLACE. */ +# ifdef PNG_WRITE_INTERLACING_SUPPORTED +# define PNG_WRITE_INTERLACE_SUPPORTED # endif #endif @@ -3545,17 +3549,18 @@ transform_row(png_const_structp pp, png_byte buffer[TRANSFORM_ROWMAX], * interlacing support. If there is no write interlacing we can't generate test * cases with interlace: */ -#ifdef PNG_WRITE_INTERLACING_SUPPORTED +#ifdef PNG_WRITE_INTERLACE_SUPPORTED # define INTERLACE_LAST PNG_INTERLACE_LAST # define check_interlace_type(type) ((void)(type)) # define set_write_interlace_handling(pp,type) png_set_interlace_handling(pp) +# define do_own_interlace 0 #elif PNG_LIBPNG_VER < 10700 # define set_write_interlace_handling(pp,type) (1) static void check_interlace_type(int const interlace_type) { /* Prior to 1.7.0 libpng does not support the write of an interlaced image - * unless PNG_WRITE_INTERLACING_SUPPORTED, even with do_interlace so the + * unless PNG_WRITE_INTERLACE_SUPPORTED, even with do_interlace so the * code here does the pixel interlace itself, so: */ if (interlace_type != PNG_INTERLACE_NONE) @@ -3568,15 +3573,66 @@ check_interlace_type(int const interlace_type) } } # define INTERLACE_LAST (PNG_INTERLACE_NONE+1) +# define do_own_interlace 0 #else /* libpng 1.7+ */ # define set_write_interlace_handling(pp,type)\ npasses_from_interlace_type(pp,type) # define check_interlace_type(type) ((void)(type)) -# define INTERLACE_LAST (PNG_INTERLACE_NONE+1) -#endif /* WRITE_INTERLACING tests */ +# define INTERLACE_LAST PNG_INTERLACE_LAST +# define do_own_interlace 1 +#endif /* WRITE_INTERLACE tests */ #define CAN_WRITE_INTERLACE\ - PNG_LIBPNG_VER >= 10700 || defined PNG_WRITE_INTERLACING_SUPPORTED + PNG_LIBPNG_VER >= 10700 || defined PNG_WRITE_INTERLACE_SUPPORTED + +/* The following two routines use the PNG interlace support macros from + * png.h to interlace or deinterlace rows. + */ +static void +interlace_row(png_bytep buffer, png_const_bytep imageRow, + unsigned int pixel_size, png_uint_32 w, int pass) +{ + png_uint_32 xin, xout, xstep; + + /* Note that this can, trivially, be optimized to a memcpy on pass 7, the + * code is presented this way to make it easier to understand. In practice + * consult the code in the libpng source to see other ways of doing this. + * + * It is OK for buffer and imageRow to be identical, because 'xin' moves + * faster than 'xout' and we copy up. + */ + xin = PNG_PASS_START_COL(pass); + xstep = 1U< 0) + interlace_row(buffer, buffer, + bit_size(pp, colour_type, bit_depth), w, pass); + else + continue; + } +# endif /* do_own_interlace */ + png_write_row(pp, buffer); } } @@ -3745,59 +3826,13 @@ make_transform_images(png_modifier *pm) char name[FILE_NAME_SIZE]; standard_name(name, sizeof name, 0, colour_type, bit_depth, - palette_number, interlace_type, 0, 0, 0); + palette_number, interlace_type, 0, 0, do_own_interlace); make_transform_image(&pm->this, colour_type, bit_depth, palette_number, interlace_type, name); } } } -/* The following two routines use the PNG interlace support macros from - * png.h to interlace or deinterlace rows. - */ -static void -interlace_row(png_bytep buffer, png_const_bytep imageRow, - unsigned int pixel_size, png_uint_32 w, int pass) -{ - png_uint_32 xin, xout, xstep; - - /* Note that this can, trivially, be optimized to a memcpy on pass 7, the - * code is presented this way to make it easier to understand. In practice - * consult the code in the libpng source to see other ways of doing this. - */ - xin = PNG_PASS_START_COL(pass); - xstep = 1U< 0) + interlace_row(buffer, buffer, + bit_size(pp, colour_type, bit_depth), w, pass); + else + continue; + } +# endif /* do_own_interlace */ + png_write_row(pp, buffer); } } @@ -4254,7 +4314,7 @@ make_errors(png_modifier* const pm, png_byte const colour_type, char name[FILE_NAME_SIZE]; standard_name(name, sizeof name, 0, colour_type, 1<bit_width)) != 0) { char msg[64]; - sprintf(msg, "display row[%lu][%d] changed from %.2x to %.2x", + sprintf(msg, "display row[%lu][%d] changed from %.2x to %.2x", (unsigned long)y, where-1, std[where-1], store_image_row(dp->ps, pp, iDisplay, y)[where-1]); png_error(pp, msg); @@ -5396,7 +5456,7 @@ test_size(png_modifier* const pm, png_byte const colour_type, # endif # endif /* READ_DEINTERLACE */ -# ifdef PNG_WRITE_INTERLACING_SUPPORTED +# ifdef PNG_WRITE_INTERLACE_SUPPORTED /* Test the libpng write side against the pngvalid read side: */ standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/, PNG_INTERLACE_ADAM7, w, h, 0), 1/*do_interlace*/, @@ -5407,7 +5467,7 @@ test_size(png_modifier* const pm, png_byte const colour_type, # endif # ifdef PNG_READ_DEINTERLACE_SUPPORTED -# ifdef PNG_WRITE_INTERLACING_SUPPORTED +# ifdef PNG_WRITE_INTERLACE_SUPPORTED /* Test both together: */ standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo), 0/*palette*/, PNG_INTERLACE_ADAM7, w, h, 0), 0/*do_interlace*/, diff --git a/png.c b/png.c index 5fc1758b0..715488e53 100644 --- a/png.c +++ b/png.c @@ -237,6 +237,19 @@ png_create_png_struct,(png_const_charp user_png_ver, png_voidp error_ptr, jmp_buf create_jmp_buf; # endif + /* This is a compile-type only test to ensure that the build satisifies the + * contraints for the row buffer stack allocations. A 'duplicate case + * statements' style of error means that one of the tests below failed: + */ + switch (0) + { + case 0: + case PNG_ROW_BUFFER_SIZE >= PNG_MIN_ROW_BUFFER_SIZE: /*1*/ + case 2*(PNG_ROW_BUFFER_SIZE <= PNG_MAX_ROW_BUFFER_SIZE): /*2*/ + default: + break; + } + /* This temporary stack-allocated structure is used to provide a place to * build enough context to allow the user provided memory allocator (if any) * to be called. @@ -2362,6 +2375,99 @@ png_calc_rowbytes(png_const_structrp png_ptr, unsigned int pixel_depth, return rowbytes; } +unsigned int /*PRIVATE*/ +png_max_pixel_block(png_const_structrp png_ptr) +{ + /* Need the *smallest* pixel size that must occur in alignment units. On + * read this is the PNG pixel depth because the read transforms cannot reduce + * the pixel size below the input size or 8-bits, whichever is smaller. + * + * On write the 'pack' transform can pack 8-bit pixels back to a lower bit + * depth, but the lowest bit depth is still given by the PNG pixel size. + */ + const unsigned int pixel_depth = PNG_PIXEL_DEPTH(*png_ptr); + const unsigned int pixel_block = /* count of pixels in a block */ + pixel_depth < 8U ? + PNG_ROW_BUFFER_BYTE_ALIGN * (8U/pixel_depth) : + PNG_ROW_BUFFER_BYTE_ALIGN; /* pixels may be any whole byte size */ + /* The maximum block size in bits is MAX_PIXEL_DEPTH*pixel_block so work out + * the minimum number of pixel blocks that can fit in PNG_ROW_BUFFER_SIZE + * bytes and use this to calculate the maximum number of pixels: + */ + return pixel_block * + ((8U*PNG_ROW_BUFFER_SIZE) / (png_ptr->row_max_pixel_depth*pixel_block)); +} + +void /* PRIVATE */ +png_copy_row(png_const_structrp png_ptr, png_bytep dp, png_const_bytep sp, + png_uint_32 x/*in INPUT*/, png_uint_32 width/*of INPUT*/, + unsigned int pixel_depth, int clear/*clear the final byte*/) + /* Copy the row in row_buffer; this is the non-interlaced copy used in both + * the read and write code. + */ +{ + png_alloc_size_t cb; + unsigned int remaining; /* remaining bits in a partial byte */ + + /* Copy 'cb' pixels, but take care with the last byte because it may + * be partially written. 'x' must correspond to the start of a byte, check + * that too: + */ + switch (pixel_depth) + { + case 1U: remaining = width & 7U; + debug((x & 7U) == 0U); + cb = width >> 3; + dp += x >> 3; + break; + case 2U: remaining = (width << 1) & 6U; + debug((x & 3U) == 0U); + cb = width >> 2; + dp += x >> 2; + break; + case 4U: remaining = (width << 2) & 4U; + debug((x & 1U) == 0U); + cb = width >> 1; + dp += x >> 1; + break; + case 8U: remaining = 0U; + cb = width; + dp += x; + break; + default: remaining = 0U; + cb = png_calc_rowbytes(png_ptr, pixel_depth, width); + dp += png_calc_rowbytes(png_ptr, pixel_depth, x); + break; + } + + memcpy(dp, sp, cb); + + if (remaining > 0U) + { + /* 'remaining' is the number of bits still to be copied. Format may be + * little endian; bits to copy in the bottom of 's'. Make 'remaining' + * into a mask of the bits to *preserve* in dp. + */ + if ((png_ptr->row_format & PNG_FORMAT_FLAG_SWAPPED) == 0U) + remaining = 0xffU >> remaining; + + else + remaining = 0xffU << remaining; + + /* remaining is now the bits to *keep* from the destination byte, if + * 'clear' is true the source bytes aren't copied - this is for security + * reasons to avoid copying undefined bits at the end of a row. If + * 'clear' is set the destination bits are not preserved, they are just + * set to 0. + */ + if (clear) + dp[cb] = PNG_BYTE(sp[cb] & ~remaining); + + else + dp[cb] = PNG_BYTE((sp[cb] & ~remaining) | (dp[cb] & remaining)); + } +} + void /* PRIVATE */ png_check_IHDR(png_const_structrp png_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, diff --git a/png.h b/png.h index 7d166f5b3..fc25e3e34 100644 --- a/png.h +++ b/png.h @@ -1463,7 +1463,7 @@ PNG_EXPORT(44, void, png_set_shift, (png_structrp png_ptr, png_const_color_8p #endif #if defined(PNG_READ_DEINTERLACE_SUPPORTED) || \ - defined(PNG_WRITE_INTERLACING_SUPPORTED) + defined(PNG_WRITE_INTERLACE_SUPPORTED) /* Have the code handle the interlacing. Returns the number of passes. * MUST be called before png_read_update_info or png_start_read_image, * otherwise it will not have the desired effect. Note that it is still @@ -1676,11 +1676,12 @@ PNG_EXPORT(67, void, png_set_filter, (png_structrp png_ptr, int method, * * The resultant argument fits in a single byte. */ -#define PNG_FILTER_NONE (0x08 << PNG_FILTER_VALUE_NONE) -#define PNG_FILTER_SUB (0x08 << PNG_FILTER_VALUE_SUB) -#define PNG_FILTER_UP (0x08 << PNG_FILTER_VALUE_UP) -#define PNG_FILTER_AVG (0x08 << PNG_FILTER_VALUE_AVG) -#define PNG_FILTER_PAETH (0x08 << PNG_FILTER_VALUE_PAETH) +#define PNG_FILTER_MASK(value) (0x08 << (value)) +#define PNG_FILTER_NONE PNG_FILTER_MASK(PNG_FILTER_VALUE_NONE) +#define PNG_FILTER_SUB PNG_FILTER_MASK(PNG_FILTER_VALUE_SUB) +#define PNG_FILTER_UP PNG_FILTER_MASK(PNG_FILTER_VALUE_UP) +#define PNG_FILTER_AVG PNG_FILTER_MASK(PNG_FILTER_VALUE_AVG) +#define PNG_FILTER_PAETH PNG_FILTER_MASK(PNG_FILTER_VALUE_PAETH) /* Then two convenience values. PNG_NO_FILTERS is the same as * PNG_FILTER_VALUE_NONE, but this is harmless because they mean the same thing. @@ -2710,9 +2711,9 @@ PNG_EXPORT(216, png_uint_32, png_get_io_chunk_type, /* A macro to find the last pass (in the range 0 to 6) given an image width and * height. Then two macros two find whether a given image row or column which - * is prsent in the pass is the last row or column in the pass. Note that these - * macros return 'true' for earlier rows or columns of the image that are *not* - * in the pass. + * is present in the pass is the last row or column in the pass. Note that + * these macros return 'true' for earlier rows or columns of the image that are + * *not* in the pass. */ #define PNG_LAST_PASS(width, height) ((height) > 1 ? 6 : ((width) > 1 ? 5 : 0)) #define PNG_LAST_PASS_ROW(y, pass, height)\ diff --git a/pngpread.c b/pngpread.c index 154f78fab..8343968b1 100644 --- a/pngpread.c +++ b/pngpread.c @@ -335,13 +335,11 @@ png_push_read_unknown(png_structrp png_ptr, png_inforp info_ptr) /* Now check the CRC, before attempting the unknown handling. */ png_calculate_crc(png_ptr, buffer, chunk_length); png_crc_finish(png_ptr, 0); - # ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED - png_handle_unknown(png_ptr, info_ptr, buffer); + png_handle_unknown(png_ptr, info_ptr, buffer); # else /* !READ_UNKNOWN_CHUNKS */ - PNG_UNUSED(info_ptr) + PNG_UNUSED(info_ptr) # endif /* !READ_UNKNOWN_CHUNKS */ - png_ptr->process_mode = png_read_chunk_header; } @@ -362,7 +360,7 @@ png_push_have_row(png_structrp png_ptr, png_bytep row) * one: */ # ifdef PNG_READ_DEINTERLACE_SUPPORTED - if (!png_ptr->do_interlace) + if (!png_ptr->do_interlace) # endif { affirm(PNG_ROW_IN_INTERLACE_PASS(row_number, pass) && row != NULL); @@ -482,7 +480,8 @@ png_push_read_process_IDAT(png_structp png_ptr, png_bytep *bufferp, if (!png_ptr->zstream_eod) { png_bytep row_buffer = NULL; - png_row_op row_op = png_read_process_IDAT(png_ptr); + png_row_op row_op = + png_read_process_IDAT(png_ptr, NULL, NULL, 1/*save row*/); if (row_op != png_row_incomplete) { @@ -517,28 +516,34 @@ png_push_read_process_IDAT(png_structp png_ptr, png_bytep *bufferp, */ affirm(buffer_lengthp != NULL || png_ptr->zstream_error); - /* png_struct::row_buffer contains a complete, transformed, row; - * this is processed in both 'sparkle' and 'block' mode. + /* png_struct::transformed_row contains a complete, transformed, + * row; this is processed in both 'sparkle' and 'block' mode. */ - row_buffer = png_ptr->row_buffer; +# ifdef PNG_TRANSFORM_MECH_SUPPORTED + row_buffer = png_ptr->transformed_row; + if (row_buffer == NULL) +# endif /* TRANSFORM_MECH */ + row_buffer = png_ptr->row_buffer; break; case png_row_repeat: /* row not in this pass, but the existing row in - * png_struct::row_buffer may be used, this is only required if - * the 'block' or 'rectangle' mode of display is done and libpng - * is handling the de-interlace; when the app does it it only - * see the real rows. + * png_struct::transformed_row may be used, this is only required + * if the 'block' or 'rectangle' mode of display is done and + * libpng is handling the de-interlace; when the app does it it + * only see the real rows. */ - # ifdef PNG_READ_DEINTERLACE_SUPPORTED - if (png_ptr->do_interlace) - { - row_buffer = png_ptr->row_buffer; - break; - } + if (png_ptr->do_interlace) + { +# ifdef PNG_TRANSFORM_MECH_SUPPORTED + row_buffer = png_ptr->transformed_row; + if (row_buffer == NULL) +# endif + row_buffer = png_ptr->row_buffer; + break; + } # endif - continue; case png_row_skip: @@ -833,23 +838,6 @@ png_process_data(png_structrp png_ptr, png_inforp info_ptr, png_process_some_data(png_ptr, info_ptr); } -#ifdef PNG_READ_DEINTERLACE_SUPPORTED -void PNGAPI -png_progressive_combine_row(png_const_structrp png_ptr, png_bytep old_row, - png_const_bytep new_row) -{ - if (png_ptr == NULL) - return; - - /* new_row is a flag here - if it is NULL then the app callback was called - * from an empty row (see the calls to png_struct::row_fn above), otherwise - * it must be png_ptr->row_buffer - */ - if (new_row != NULL) - png_combine_row(png_ptr, old_row, 1/*blocky display*/); -} -#endif /* READ_DEINTERLACE */ - void PNGAPI png_set_progressive_read_fn(png_structrp png_ptr, png_voidp progressive_ptr, png_progressive_info_ptr info_fn, png_progressive_row_ptr row_fn, diff --git a/pngpriv.h b/pngpriv.h index 91fad2a8a..f7adfa6fe 100644 --- a/pngpriv.h +++ b/pngpriv.h @@ -556,6 +556,47 @@ # define png_isaligned(ptr, type) 0 #endif +/* Buffer alignment control. These #defines control how the buffers used during + * read are aligned and how big they are. + */ +#ifndef PNG_ROW_BUFFER_ALIGN_TYPE + /* The absolute minimum alignment for a row buffer is that required for + * png_uint_32 direct access. The #define is of a legal C type that can be + * used as the type in the definition of the first member of a C union; give + * a typedef name if in doubt. + */ +# define PNG_ROW_BUFFER_ALIGN_TYPE png_uint_32 +#endif /* !ROW_BUFFER_ALIGN_TYPE */ +#ifndef PNG_ROW_BUFFER_BYTE_ALIGN + /* This is the minimum size in bytes of the buffer used while processing + * parts of row. Except at the end of the row pixels will always be + * processed in blocks such that the block size is a multiple of this number + */ +# define PNG_ROW_BUFFER_BYTE_ALIGN\ + ((unsigned int)/*SAFE*/(sizeof (PNG_ROW_BUFFER_ALIGN_TYPE))) +#endif /* !ROW_BUFFER_BYTE_ALIGN */ +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED +# define PNG_MAX_PIXEL_BYTES 16U /* 4x32-bit channels */ +#else /* !READ_USER_TRANSFORM */ +# define PNG_MAX_PIXEL_BYTES 8U /* 4x16-bit channels */ +#endif /* !READ_USER_TRANSFORM */ +/* PNG_ROW_BUFFER_SIZE is a compile time constant for the size of the row + * buffer. The minimum size of 2048 bytes is intended to allow the buffer to + * hold a complete 256 entry color map of 64-bit (8-byte) pixels. This is a + * requirement at some points of the colormap handling code. + * + * The maximum size is intended to allow (unsigned int) indexing of the buffer, + * it only affects systems with a 16-bit unsigned value where it limits the + * maximum to 4096 bytes. + */ +#define PNG_MIN_ROW_BUFFER_SIZE\ + (PNG_MAX_PIXEL_BYTES * PNG_ROW_BUFFER_BYTE_ALIGN * 8U) +#define PNG_MAX_ROW_BUFFER_SIZE ((UINT_MAX / 16U) + 1U) +#ifndef PNG_ROW_BUFFER_SIZE +# define PNG_ROW_BUFFER_SIZE\ + (PNG_MIN_ROW_BUFFER_SIZE < 2048U ? 2048U : PNG_MIN_ROW_BUFFER_SIZE) +#endif /* ROW_BUFFER_SIZE */ + /* End of memory model/platform independent support */ /* End of 1.5.0beta36 move from pngconf.h */ @@ -892,9 +933,23 @@ PNG_INTERNAL_FUNCTION(png_uint_16, png_u16_affirm,(png_const_structrp png_ptr, /* Safe calculation of a rowbytes value; does a png_error if the system limits * are exceeded. */ -png_alloc_size_t /* PRIVATE */ -png_calc_rowbytes(png_const_structrp png_ptr, unsigned int pixel_depth, - png_uint_32 row_width); +PNG_INTERNAL_FUNCTION(png_alloc_size_t,png_calc_rowbytes, + (png_const_structrp png_ptr, unsigned int pixel_depth, + png_uint_32 row_width),PNG_EMPTY); + +/* Common code to calculate the maximum number of pixels to transform or filter + * at one time; controlled by PNG_ROW_BUFFER_SIZE above: + */ +PNG_INTERNAL_FUNCTION(unsigned int,png_max_pixel_block, + (png_const_structrp png_ptr),PNG_EMPTY); + +/* Copy the row in row_buffer; this is the non-interlaced copy used in both + * the read and write code. + */ +PNG_INTERNAL_FUNCTION(void, png_copy_row,(png_const_structrp png_ptr, + png_bytep dp, png_const_bytep sp, png_uint_32 x/*in INPUT*/, + png_uint_32 width/*of INPUT*/, unsigned int pixel_depth, + int clear/*clear the final byte*/),PNG_EMPTY); /* Zlib support */ #define PNG_UNEXPECTED_ZLIB_RETURN (-7) @@ -1149,33 +1204,21 @@ PNG_INTERNAL_FUNCTION(void,png_write_sCAL_s,(png_structrp png_ptr, int unit, png_const_charp width, png_const_charp height),PNG_EMPTY); #endif -PNG_INTERNAL_FUNCTION(void,png_copy_row,(png_const_structrp png_ptr, - png_bytep dp),PNG_EMPTY); - /* Copy the row in row_buffer; this is the 'simple' case of png_combine_row - * where no adjustment to the pixel spacing is required. - */ - -#ifdef PNG_READ_DEINTERLACE_SUPPORTED -PNG_INTERNAL_FUNCTION(void,png_combine_row,(png_const_structrp png_ptr, - png_bytep row, int display),PNG_EMPTY); -#endif /* READ_DEINTERLACE */ - -/* GRR TO DO (2.0 or whenever): simplify other internal calling interfaces */ - -#ifdef PNG_WRITE_INTERLACING_SUPPORTED -/* Turn on write interlacing */ -PNG_INTERNAL_FUNCTION(void,png_set_write_interlace,(png_structrp),PNG_EMPTY); -#endif - -#ifdef PNG_WRITE_FILTER_SUPPORTED -/* Choose the best filter to use and filter the row data returning a buffer to - * the result and filling in 'filter_byte' appropriately. +/* Choose the best filter to use and filter the row data then write it out. If + * WRITE_FILTERING is not supported this just writes the data out with a zero + * (NONE) filter byte. + * + * This may be called multiple times per row, but calls must be in 'x' order; + * first a call with x 0 to mark the start of the row and, at the end, one with + * 'end_of_row' set (this can be done in the same function call if the whole row + * is passed.) */ -PNG_INTERNAL_FUNCTION(png_const_bytep,png_write_filter_row, - (png_structrp png_ptr, png_const_bytep unfiltered_row, int first_pass_row, - png_const_bytep previous_row, png_alloc_size_t rowbytes, unsigned int bpp, - png_bytep filter_byte),PNG_EMPTY); -#endif +PNG_INTERNAL_FUNCTION(unsigned int, png_write_filter_row, + (png_structrp png_ptr, png_bytep prev_pixels, png_const_bytep unfiltered_row, + png_uint_32 x, png_uint_32 width/*pixels*/, int first_row_in_pass, + int last_pass_row, unsigned int filters_to_try/*from previous call*/, + int end_of_image), + PNG_EMPTY); #ifdef PNG_TRANSFORM_MECH_SUPPORTED PNG_INTERNAL_FUNCTION(void,png_transform_free,(png_const_structrp png_ptr, @@ -1595,17 +1638,52 @@ typedef enum png_row_incomplete, /* more IDAT data needed for row */ png_row_process, - /* png_struct::row_buffer contains a complete, transformed, row */ + /* png_struct::row_buffer contains a complete row */ png_row_repeat, /* row not in this pass, but the existing row may be used */ png_row_skip /* row not in pass and no appropriate data; skip this row */ } png_row_op; -PNG_INTERNAL_FUNCTION(png_row_op,png_read_process_IDAT,(png_structrp png_ptr), - PNG_EMPTY); +PNG_INTERNAL_FUNCTION(png_row_op,png_read_process_IDAT,(png_structrp png_ptr, + png_bytep transformed_row, png_bytep display_row, int save_row), + PNG_EMPTY); /* Process a block of IDAT data; the routine returns early if it has * obtained a row. It is valid to call this routine with no input data; - * it will return PNG_ROW_INCOMPLETE if it needs input. + * it will return png_row_incomplete if it needs input. + * + * transformed_row: The transformed pixels of the input are written here. + * For interlaced images only the pixels in the pass will + * be written, the other pixels will not be touched. + * + * display_row: The transformed pixels but replicated to that the entire + * buffer will have been initialized. For passes after the + * first the pixels written are determined by the 'block' + * algorithm; only those *following* pixels which are + * written by *later* passes are written (with a copy of the + * pixel from the pass.) + * + * save_row: A boolean which indicates that the row (unexpanded) + * should be saved in png_struct::transformed_row. This can + * be used in a later call to png_combine_row. + * + * During reading the row is built up until png_row_process is returned. At + * this point png_struct::row_buffer contains the original PNG row from the + * file and, if save_row was set, png_struct::transformed_row contains the + * row after the selected row transforms have been performed. For interlaced + * images both are the width of the interlace pass. + * + * When png_row_repeat is returned the same is true, except that the buffers + * still contain the contents of the preceding row (the one where this + * funciton returned png_row_pricess). + * + * The row buffers should not be accessed if png_row_skip is returned; this + * row is not modified in the current pass. + */ + +PNG_INTERNAL_FUNCTION(void,png_read_free_row_buffers,(png_structrp png_ptr), + PNG_EMPTY); + /* Free allocated row buffers; done as soon as possible to avoid carrying + * around all the memory for longer than necessary. */ PNG_INTERNAL_FUNCTION(int,png_read_finish_IDAT,(png_structrp png_ptr), @@ -1671,7 +1749,6 @@ PNG_INTERNAL_FUNCTION(void,png_handle_chunk,(png_structrp png_ptr, * png_handle_chunk (via the libpng read callback.) */ -/* Handle the transformations for reading and writing */ PNG_INTERNAL_FUNCTION(void,png_init_row_info,(png_structrp png_ptr),PNG_EMPTY); /* Set the png_struct::row_ members from the PNG file information, running * transforms if required. diff --git a/pngread.c b/pngread.c index 86d51f412..4e39be95b 100644 --- a/pngread.c +++ b/pngread.c @@ -353,11 +353,6 @@ png_read_IDAT(png_structrp png_ptr) png_ptr->zstream.avail_in = IDAT_size; } -#ifndef PNG_READ_DEINTERLACE_SUPPORTED - /* No png_combine_row; just copy the row bytes: */ -# define png_combine_row(pp, pb, display) png_copy_row((pp), (pb)) -#endif - void PNGAPI png_read_row(png_structrp png_ptr, png_bytep row, png_bytep dsp_row) /* It is valid to call this API with both 'row' and 'dsp_row' NULL, all @@ -406,60 +401,42 @@ png_read_row(png_structrp png_ptr, png_bytep row, png_bytep dsp_row) * has PNG_AFTER_IDAT set, by either doing png_error or using 0 bytes for * the data (after issuing a warning.) */ - switch (png_read_process_IDAT(png_ptr)) + switch (png_read_process_IDAT(png_ptr, row, dsp_row, 0/*no save*/)) { case png_row_incomplete: /* more IDAT data needed for row */ debug(png_ptr->zstream.avail_in == 0); continue; - case png_row_process: - /* png_struct::row_buffer contains a complete, transformed, row; - * this is processed in both 'sparkle' and 'block' mode. The final - * parameter to png_combine_row is false, meaning only overwrite - * the pixels corresponding to this pass: - */ - if (row != NULL) - png_combine_row(png_ptr, row, 0/*'sparkle'*/); - - goto display_row; /* Skip the 'DEINT' check */ - case png_row_repeat: - /* row not in this pass, but the existing row in - * png_struct::row_buffer may be used, this is only done if the - * 'block' or 'rectangle' mode of display is required. The final - * parameter to png_combine_row is true, meaning overwrite all the - * pixels belong to this and *later* passes. + /* row not in this pass, but the existing row in row_buffer or (if + * transforms are happening) png_struct::transformed_row is + * available from a previous row. */ -#ifdef PNG_READ_DEINTERLACE_SUPPORTED - if (!png_ptr->do_interlace) - continue; -#else - continue; -#endif - - display_row: - if (dsp_row != NULL) - png_combine_row(png_ptr, dsp_row, 1/*'rectangle/block'*/); - - goto row_fn; /* Skip the 'DEINT' check */ - + /* FALL THROUGH */ case png_row_skip: /* row not in pass and no appropriate data; skip this row, nothing - * more need be done, except the read_row_fn: + * more need be done, except the read_row_fn and then only if libpng + * is doing the interlace handling (this is the historical + * behavior!) */ -#ifdef PNG_READ_DEINTERLACE_SUPPORTED - if (!png_ptr->do_interlace) +# ifdef PNG_READ_DEINTERLACE_SUPPORTED + if (!png_ptr->do_interlace) continue; +# else /* !do_interlace */ continue; -#else - continue; -#endif - - row_fn: +# endif /* !do_interlace */ + /* FALL THROUGH */ + case png_row_process: + /* png_read_process_IDAT has done everything we need, the only extra + * required is to call the application row callback. + */ if (png_ptr->read_row_fn != NULL) png_ptr->read_row_fn(png_ptr, png_ptr->row_number, png_ptr->pass); - + /* And return now because the next row has been processed; so there + * is exactly one read_row_fn callback for each call to + * png_read_process_IDAT. + */ return; default: @@ -732,10 +709,8 @@ png_read_destroy(png_structrp png_ptr) { png_debug(1, "in png_read_destroy"); - png_free(png_ptr, png_ptr->row_buffer); - png_ptr->row_buffer = NULL; - png_free(png_ptr, png_ptr->alt_buffer); - png_ptr->alt_buffer = NULL; + png_read_free_row_buffers(png_ptr); + png_free(png_ptr, png_ptr->read_buffer); png_ptr->read_buffer = NULL; diff --git a/pngrutil.c b/pngrutil.c index 4ecff2659..85b6d73df 100644 --- a/pngrutil.c +++ b/pngrutil.c @@ -3102,62 +3102,26 @@ png_handle_chunk(png_structrp png_ptr, png_inforp info_ptr) png_known_chunks[index].handle(png_ptr, info_ptr); } -void /* PRIVATE */ -png_copy_row(png_const_structrp png_ptr, png_bytep dp) - /* Copy the row in row_buffer; this is the 'simple' case of png_combine_row +static void +copy_row(png_const_structrp png_ptr, png_bytep dp, png_const_bytep sp, + png_uint_32 x/*in INPUT*/, png_uint_32 width/*of INPUT*/, int clear) + /* Copy the row in row_buffer; this is the 'simple' case of combine_row * where no adjustment to the pixel spacing is required. */ { - unsigned int pixel_depth = -# ifdef PNG_TRANSFORM_MECH_SUPPORTED - png_ptr->row_bit_depth * PNG_FORMAT_CHANNELS(png_ptr->row_format); -# else - PNG_PIXEL_DEPTH(*png_ptr); -# endif - png_alloc_size_t cb = png_ptr->width; - unsigned int remaining; /* remaining bits in a partial byte */ - - /* 1.7.0: png_combine_row used to copy data equal to the whole row even if - * the deinterlace transform had not been performed. This must be an error - * and possibly a security issue: - */ - if (png_ptr->interlaced) - cb = PNG_PASS_COLS(cb, png_ptr->pass); - - /* Copy 'cb' pixels, but take care with the last byte because it may - * be partially written. - */ - switch (pixel_depth) - { - case 1: remaining = cb & 7U; cb >>= 3; break; - case 2: remaining = (cb << 1) & 6U; cb >>= 2; break; - case 4: remaining = (cb << 2) & 4U; cb >>= 1; break; - default: remaining = 0U; cb *= pixel_depth >> 3; break; - } - - memcpy(dp, png_ptr->row_buffer, cb); - - if (remaining > 0) - { - /* 'remaining' is the number of bits still to be copied. */ -# ifdef PNG_READ_PACKSWAP_SUPPORTED - /* Format may be little endian; bits to copy in the bottom of 's' */ - if ((png_ptr->row_format & PNG_FORMAT_FLAG_SWAPPED) != 0) - remaining = 0xffU << remaining; - - else -# endif /* READ_PACKSWAP */ - remaining = 0xffU >> remaining; - - /* remaining is now the bits to *keep* from the destination byte */ - dp[cb] = png_check_byte(png_ptr, - (dp[cb] & remaining) | (png_ptr->row_buffer[cb] & ~remaining)); - } + png_copy_row(png_ptr, dp, sp, x, width, +# ifdef PNG_TRANSFORM_MECH_SUPPORTED + png_ptr->row_bit_depth * PNG_FORMAT_CHANNELS(png_ptr->row_format), +# else + PNG_PIXEL_DEPTH(*png_ptr), +# endif + clear/*clear partial byte at end of row*/); } #ifdef PNG_READ_DEINTERLACE_SUPPORTED -void /* PRIVATE */ -png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display) +static void +combine_row(png_const_structrp png_ptr, png_bytep dp, png_const_bytep sp, + png_uint_32 x/*in INPUT*/, png_uint_32 width/*of INPUT*/, int display) /* 1.7.0: API CHANGE: prior to 1.7.0 read de-interlace was done in two steps, * the first would expand a narrow pass by replicating pixels according to * the inter-pixel spacing of the pixels from the pass in the image. It did @@ -3172,16 +3136,16 @@ png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display) * the weird non-offseting of the start in the original * 'png_do_read_interlace'; the behavior was completely undocumented. * - * In 1.7.0 png_combine_row does all the work. It expects a raw - * uncompressed, de-filtered, transformed row and it either copies it if: + * In 1.7.0 combine_row does all the work. It expects a raw uncompressed, + * de-filtered, transformed row and it either copies it if: * * 1) It is not interlaced. * 2) libpng isn't handling the de-interlace. * 3) This is pass 7 (i.e. '6' using the libpng 0-based numbering). * - * The input data comes from png_struct: + * The input data comes from png_struct and sp: * - * png_struct::row_buffer; the row data + * sp[width(pixels)]; the row data from input[x(pixels)...] * png_struct::pass; the pass * png_struct::row_number; the row number in the *image* * png_struct::row_bit_depth, @@ -3190,7 +3154,9 @@ png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display) * png_struct::color_type; the pixel format otherwise * * The destination pointer (but not size) and how to handle intermediate - * passes are arguments to the API. 'display' is interpreted as: + * passes are arguments to the API. The destination is the pointer to the + * entire row buffer, not just the part from output[x] on. 'display' is + * interpreted as: * * 0: only overwrite destination pixels that will correspond to the source * pixel in the final image. 'sparkle' mode. @@ -3199,6 +3165,8 @@ png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display) * from *later* passes. 'block' mode. */ { + const unsigned int pass = png_ptr->pass; + png_debug(1, "in png_combine_row"); /* Factor out the copy case first, the 'display' argument is irrelevant in @@ -3206,37 +3174,47 @@ png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display) */ if (!png_ptr->do_interlace || png_ptr->pass == 6) { - png_copy_row(png_ptr, dp); + copy_row(png_ptr, dp, sp, x, width, 0/*do not clear*/); return; } else /* not a simple copy */ { - unsigned int pixel_depth = + const unsigned int pixel_depth = # ifdef PNG_TRANSFORM_MECH_SUPPORTED png_ptr->row_bit_depth * PNG_IMAGE_PIXEL_CHANNELS(png_ptr->row_format); # else PNG_PIXEL_DEPTH(*png_ptr); # endif - const unsigned int pass = png_ptr->pass; - png_const_bytep sp = png_ptr->row_buffer; png_uint_32 row_width = png_ptr->width; /* output width */ - /* The first source pixel is written to PNG_PASS_START_COL of the + /* The first source pixel is written to PNG_COL_FROM_PASS of the * destination: */ - unsigned int dstart = PNG_PASS_START_COL(pass); /* in pixels */ + png_uint_32 dx = PNG_COL_FROM_PASS_COL(x, pass); + /* The corresponding offset within the 8x8 block: */ + const unsigned int dstart = dx & 0x7U; + /* Find the first pixel written in any 8x8 block IN THIS PASS: */ + const unsigned int pass_start = PNG_PASS_START_COL(pass); /* Subsequent pixels are written PNG_PASS_COL_OFFSET further on: */ - unsigned int doffset = PNG_PASS_COL_OFFSET(pass); /* in pixels */ - /* In 'block' mode when dstart is 0 (PNG passes 1,3,5,7) the same pixel is - * replicated doffset times, when dstart is non-zero (PNG passes 2,4,6) it - * is replicated dstart times. For 'sparkle' mode only one copy of the - * pixel is written: + const unsigned int doffset = PNG_PASS_COL_OFFSET(pass); + /* In 'block' mode when PNG_PASS_START_COL(pass) is 0 (PNG passes 1,3,5,7) + * the same pixel is replicated doffset times, when PNG_PASS_START_COL is + * non-zero (PNG passes 2,4,6) it is replicated PNG_PASS_START_COL times. + * For 'sparkle' mode only one copy of the pixel is written: */ - unsigned int drep = display ? (dstart ? dstart : doffset) : 1; + unsigned int drep = display ? (pass_start ? pass_start : doffset) : 1; + + /* Standard check for byte alignment */ + debug(((x * pixel_depth/*OVERFLOW OK*/) & 0x7U) == 0U); /* The caller should have excluded the narrow cases: */ - affirm(row_width > dstart); - row_width -= dstart; + affirm(row_width > dx); + row_width -= dx; + /* Advance dp to the start of the 8x8 block containing the first pixel to + * write, adjust dx to be an offset within the block: + */ + dp += png_calc_rowbytes(png_ptr, pixel_depth, dx & ~0x7U); + dx &= 0x7U; /* So each source pixel sp[i] is written to: * @@ -3250,12 +3228,12 @@ png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display) * * Cherry pick the easy cases: */ - if (pixel_depth > 8) + if (pixel_depth > 8U) { - affirm((pixel_depth & 7) == 0); /* Convert to bytes: */ - pixel_depth >>= 3; - dp += dstart * pixel_depth; + const unsigned int pixel_bytes = pixel_depth >> 3; + + dp += dstart * pixel_bytes; for (;;) { @@ -3264,19 +3242,19 @@ png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display) if (drep > row_width) drep = row_width; - for (c=0; c= row_width) break; row_width -= doffset; - dp += (doffset-drep) * pixel_depth; - sp += pixel_depth; + dp += (doffset-drep) * pixel_bytes; + sp += pixel_bytes; } } - else if (pixel_depth == 8) + else if (pixel_depth == 8U) { /* Optimize the common 1-byte per pixel case (typical case for palette * mapped images): @@ -3303,13 +3281,13 @@ png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display) /* Pixels are 1, 2 or 4 bits in size. */ unsigned int spixel = *sp++; unsigned int dbrep = pixel_depth * drep; - unsigned int spos = 0; + unsigned int spos = 0U; # ifdef PNG_READ_PACKSWAP_SUPPORTED const int lsb = (png_ptr->row_format & PNG_FORMAT_FLAG_SWAPPED) != 0; # endif /* READ_PACKSWAP */ - if (dbrep >= 8) + if (dbrep >= 8U) { /* brep must be greater than 1, the destination does not require * sub-byte addressing except, maybe, at the end. @@ -3317,9 +3295,9 @@ png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display) * db is the count of bytes required to replicate the source pixel * drep times. */ - affirm((dbrep & 7) == 0); + debug((dbrep & 7U) == 0U); dbrep >>= 3; - affirm((dstart * pixel_depth & 7) == 0); + debug((dstart * pixel_depth & 7U) == 0U); dp += (dstart * pixel_depth) >> 3; for (;;) @@ -3332,18 +3310,17 @@ png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display) spixel_rep >>= spos; else # endif /* READ_PACKSWAP */ - spixel_rep >>= (8-pixel_depth)-spos; + spixel_rep >>= (8U-pixel_depth)-spos; switch (pixel_depth) { - case 1: spixel_rep &= 1; spixel_rep |= spixel_rep << 1; - /*FALL THROUGH*/ - case 2: spixel_rep &= 3; spixel_rep |= spixel_rep << 2; - /*FALL THROUGH*/ - case 4: spixel_rep &= 15; spixel_rep |= spixel_rep << 4; - /*FALL THROUGH*/ - default: - break; + case 1U: spixel_rep &= 1U; spixel_rep |= spixel_rep << 1; + /*FALL THROUGH*/ + case 2U: spixel_rep &= 3U; spixel_rep |= spixel_rep << 2; + /*FALL THROUGH*/ + case 4U: spixel_rep &= 15U; spixel_rep |= spixel_rep << 4; + /*FALL THROUGH*/ + default: break; } /* This may leave some pixels unwritten when there is a partial @@ -3364,7 +3341,7 @@ png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display) { unsigned int mask; - affirm(drep < 8); + debug(drep < 8U); dp += dbrep; /* Set 'mask' to have 0's where *dp must be overwritten @@ -3377,8 +3354,7 @@ png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display) # endif /* READ_PACKSWAP */ mask = 0xff >> drep; - *dp = png_check_byte(png_ptr, - (*dp & mask) | (spixel_rep & ~mask)); + *dp = PNG_BYTE((*dp & mask) | (spixel_rep & ~mask)); } break; @@ -3387,8 +3363,8 @@ png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display) row_width -= doffset; dp += (doffset * pixel_depth) >> 3; spos += pixel_depth; - if (spos == 8) - spixel = *sp++, spos = 0; + if (spos == 8U) + spixel = *sp++, spos = 0U; } /* for (;;) */ } /* pixel_depth * drep >= 8 */ @@ -3397,15 +3373,15 @@ png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display) /* brep may be 1, pixel_depth may be 1, 2 or 4, dbrep is the number * of bits to set. */ + unsigned int bstart = dstart * pixel_depth; /* in bits */ unsigned int dpixel; - dstart *= pixel_depth; - dp += dstart >> 3; - dstart &= 7; + dp += bstart >> 3; + bstart &= 7U; dpixel = *dp; /* dpixel: current *dp, being modified - * dstart: bit offset within dpixel + * bstart: bit offset within dpixel * drep: pixel size to write (used as a check against row_width) * doffset: pixel step to next written destination * @@ -3415,7 +3391,7 @@ png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display) * * Set dbrep to a mask for the bits to set: */ - dbrep = (1<>= spos; else # endif /* READ_PACKSWAP */ - spixel_rep >>= (8-pixel_depth)-spos; + spixel_rep >>= (8U-pixel_depth)-spos; switch (pixel_depth) { - case 1: spixel_rep &= 1; spixel_rep |= spixel_rep << 1; - /*FALL THROUGH*/ - case 2: spixel_rep &= 3; spixel_rep |= spixel_rep << 2; - /*FALL THROUGH*/ - case 4: spixel_rep &= 15; spixel_rep |= spixel_rep << 4; - /*FALL THROUGH*/ - default: - break; + case 1U: spixel_rep &= 1U; spixel_rep |= spixel_rep << 1; + /*FALL THROUGH*/ + case 2U: spixel_rep &= 3U; spixel_rep |= spixel_rep << 2; + /*FALL THROUGH*/ + case 4U: spixel_rep &= 15U; spixel_rep |= spixel_rep << 4; + /*FALL THROUGH*/ + default: break; } /* This may leave some pixels unwritten when there is a partial * byte write required at the end: */ if (drep > row_width) - drep = row_width, dbrep = (1<<(pixel_depth*drep))-1; + drep = row_width, dbrep = (1U<<(pixel_depth*drep))-1U; { unsigned int mask; - /* Mask dbrep bits at dstart: */ + /* Mask dbrep bits at bstart: */ # ifdef PNG_READ_PACKSWAP_SUPPORTED if (lsb) - mask = dstart; + mask = bstart; else # endif /* READ_PACKSWAP */ - mask = (8-pixel_depth)-dstart; + mask = (8U-pixel_depth)-bstart; mask = dbrep << mask; dpixel &= ~mask; @@ -3464,105 +3439,140 @@ png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display) if (doffset >= row_width) { - *dp = png_check_byte(png_ptr, dpixel); + *dp = PNG_BYTE(dpixel); break; } row_width -= doffset; - dstart += doffset * pixel_depth; + bstart += doffset * pixel_depth; - if (dstart >= 8) + if (bstart >= 8U) { - *dp = png_check_byte(png_ptr, dpixel); - dp += dstart >> 3; - dstart &= 7; + *dp = PNG_BYTE(dpixel); + dp += bstart >> 3; + bstart &= 7U; dpixel = *dp; } spos += pixel_depth; - if (spos == 8) - spixel = *sp++, spos = 0; + if (spos == 8U) + spixel = *sp++, spos = 0U; } /* for (;;) */ } /* pixel_depth * drep < 8 */ } /* pixel_depth < 8 */ } /* not a simple copy */ } -#endif /* READ_DEINTERLACE */ + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +void PNGAPI +png_progressive_combine_row(png_const_structrp png_ptr, png_bytep old_row, + png_const_bytep new_row) +{ + /* new_row is a flag here - if it is NULL then the app callback was called + * from an empty row (see the calls to png_struct::row_fn above), otherwise + * it must be png_struct::transformed_row + */ + if (png_ptr != NULL && new_row != NULL) + { + if (new_row != png_ptr->row_buffer +# ifdef PNG_TRANSFORM_MECH_SUPPORTED + && new_row != png_ptr->transformed_row +# endif /* TRANSFORM_MECH */ + ) + png_app_error(png_ptr, "invalid call to png_progressive_combine_row"); + else + { + png_uint_32 width = png_ptr->width; + + if (png_ptr->interlaced == PNG_INTERLACE_ADAM7) + { + const unsigned int pass = png_ptr->pass; + width = PNG_PASS_COLS(width, pass); + } + + combine_row(png_ptr, old_row, new_row, 0U, width, 1/*blocky display*/); + } + } +} +#endif /* PROGRESSIVE_READ */ +#else /* !READ_DEINTERLACE */ + /* No read deinterlace support, so 'combine' always reduces to 'copy', there + * is no 'display' argument: + */ +# define combine_row(pp, dp, sp, x, w, display)\ + copy_row(pp, dp, sp, x, w, 0/*!clear*/) +#endif /* !READ_DEINTERLACE */ static void -png_read_filter_row_sub(png_alloc_size_t istop, unsigned int bpp, - png_bytep row, png_const_bytep prev_row) +png_read_filter_row_sub(png_alloc_size_t row_bytes, unsigned int bpp, + png_bytep row, png_const_bytep prev_row, png_const_bytep prev_pixels) { - png_alloc_size_t i; - png_bytep rp = row + bpp; + while (row_bytes >= bpp) + { + unsigned int i; + + for (i=0; i 0) { - *rp = PNG_BYTE(*rp + *pp++); - rp++; + *row = PNG_BYTE(*row + *prev_row); + ++row; + ++prev_row; + --row_bytes; } PNG_UNUSED(bpp) + PNG_UNUSED(prev_pixels) } static void -png_read_filter_row_avg(png_alloc_size_t istop, unsigned int bpp, - png_bytep row, png_const_bytep prev_row) +png_read_filter_row_avg(png_alloc_size_t row_bytes, unsigned int bpp, + png_bytep row, png_const_bytep prev_row, png_const_bytep prev_pixels) { - png_alloc_size_t i; - png_bytep rp = row; - png_const_bytep pp = prev_row; - - istop -= bpp; - for (i = 0; i < bpp; i++) + while (row_bytes >= bpp) { - *rp = PNG_BYTE(*rp + (*pp++ / 2)); - rp++; - } + unsigned int i; - for (i = 0; i < istop; i++) - { - *rp = PNG_BYTE(*rp + (*pp++ + *(rp-bpp)) / 2); + for (i=0; irow_buffer = png_voidcast(png_bytep, - png_malloc(png_ptr, png_ptr->row_allocated_bytes)); + png_ptr->row_buffer = png_voidcast(png_bytep, png_malloc(png_ptr, + png_calc_rowbytes(png_ptr, PNG_PIXEL_DEPTH(*png_ptr), png_ptr->width))); if (png_inflate_claim(png_ptr, png_IDAT) != Z_OK) png_error(png_ptr, png_ptr->zstream.msg); @@ -3864,28 +3898,78 @@ png_read_start_IDAT(png_structrp png_ptr) * preserved and preset at the next call to the function. * * The function may also call png_error if an unrecoverable error occurs. + * + * The caller passes in a callback function and parameter to be called when row + * data is available. The callback is called repeatedly for each row to handle + * all the transformed row data. */ png_row_op /*PRIVATE*/ -png_read_process_IDAT(png_structrp png_ptr) +png_read_process_IDAT(png_structrp png_ptr, png_bytep transformed_row, + png_bytep display_row, int save_row) { - png_uint_32 width = png_ptr->width; + /* Common sub-expressions. These are all constant across the whole PNG, but + * are recalculated here each time because this is fast and it only happens + * once per row + once per block of input data. + */ + const unsigned int max_pixels = png_max_pixel_block(png_ptr); + const unsigned int pixel_depth = png_ptr->row_input_pixel_depth; + /* The number of input bytes read each time (cannot overflow because it is + * limited by PNG_ROW_BUFFER_SIZE): + */ + const unsigned int input_byte_count = (max_pixels * pixel_depth) / 8U; + const unsigned int bpp = (pixel_depth+0x7U)>>3; + const png_uint_32 width = png_ptr->width; + const unsigned int interlaced = png_ptr->interlaced != PNG_INTERLACE_NONE; + png_uint_32 row_number = png_ptr->row_number; unsigned int pass = png_ptr->pass; - const unsigned int interlaced = png_ptr->interlaced != PNG_INTERLACE_NONE; enum anonymous { - start_of_pass = 0U, /* at start of pass, no filter byte */ - need_filter_byte = 1U, /* need the filter byte *after* this row */ + start_of_row = 0U, /* at the start of the row; read a filter byte */ need_row_bytes = 2U, /* reading the row */ - processing_row = 3U, /* control returned to caller to process the row */ - start_read_row_bytes, /* start read a row (internal) */ - transform_row /* transform an unfiltered row (internal) */ + processing_row = 3U /* control returned to caller to process the row */ } state = png_upcast(enum anonymous, png_ptr->row_state); /* The caller is responsible for calling png_read_start_IDAT: */ affirm(png_ptr->zowner == png_IDAT); + /* Basic sanity checks: */ + affirm(pixel_depth > 0U && pixel_depth <= 64U && + input_byte_count <= PNG_ROW_BUFFER_SIZE && + pixel_depth <= 8U*PNG_MAX_PIXEL_BYTES); + for (;;) switch (state) { + png_alloc_size_t row_bytes_processed; + png_alloc_size_t bytes_read; /* bytes in pixel_buffer */ + png_uint_32 pass_width; + png_byte row_filter; + union + { + PNG_ROW_BUFFER_ALIGN_TYPE force_buffer_alignment; + png_byte buffer[16U]; + } previous_pixels; + union + { + PNG_ROW_BUFFER_ALIGN_TYPE force_buffer_alignment; + png_byte buffer[PNG_ROW_BUFFER_SIZE]; + } pixel_buffer; + + case need_row_bytes: + /* The above variables need to be restored: */ + row_bytes_processed = png_ptr->row_bytes_read; + bytes_read = row_bytes_processed % input_byte_count; + row_bytes_processed -= bytes_read; + + pass_width = width; + if (interlaced) + pass_width = PNG_PASS_COLS(pass_width, pass); + + memcpy(pixel_buffer.buffer, png_ptr->scratch, bytes_read); + memcpy(previous_pixels.buffer, png_ptr->scratch+bytes_read, 2*bpp); + row_filter = png_ptr->scratch[bytes_read+2*bpp]; + + goto pixel_loop; + case processing_row: /* When there was a previous row (not at the start of the image) the * row number needs to be updated and, possibly, the pass number. @@ -3898,8 +3982,8 @@ png_read_process_IDAT(png_structrp png_ptr) * is always necessary to read the filter byte of the next row. */ png_ptr->pass = ++pass & 0x7; - row_number = 0; - } + row_number = 0U; + } /* end of pass */ png_ptr->row_number = row_number; @@ -3909,15 +3993,13 @@ png_read_process_IDAT(png_structrp png_ptr) */ if (interlaced) { - png_uint_32 pass_width = width; - debug(pass <= 6); /* This macro cannot overflow because the PNG width (and height) * have already been checked to ensure that they are less than * 2^31 (i.e. they are 31-bit values, not 32-bit values.) */ - pass_width = PNG_PASS_COLS(pass_width, pass); + pass_width = PNG_PASS_COLS(width, pass); /* On average most rows are skipped, so do this first: */ if (pass_width == 0 || @@ -3978,369 +4060,337 @@ png_read_process_IDAT(png_structrp png_ptr) (~((row) >> (3U-((pass) >> 1))) & ~(pass) & 0x1U) /* Hence: */ - png_ptr->row_state = processing_row; + debug(png_ptr->row_state == processing_row); return pass_width == 0 || PNG_PASS_BLOCK_SKIP(pass, row_number) ? png_row_skip : png_row_repeat; } /* skipped row */ - /* processed; fall through to start_read_row_bytes unless this is - * the first row in this pass, in which case the filter byte has - * not been read. - */ - if (row_number == PNG_PASS_START_ROW(pass)) - { - state = start_of_pass; - continue; - } + /* processed; fall through to start_of_row */ } /* interlaced */ - else /* not interlaced */ if (row_number == 0) - { - /* On the first row it is necessary to read a filter byte: */ - state = start_of_pass; - continue; /* get the filter byte */ - } - /* FALL THROUGH */ - - case start_read_row_bytes: - /* The row is always read into png_struct::row_buffer, however if the - * row filter (png_struct::next_filter) requires the previous row it - * is necessary to make sure that the read does not overwrite it (the - * previous row:) - */ - if (png_ptr->next_filter > PNG_FILTER_VALUE_SUB && - !png_ptr->prev_in_alt) + case start_of_row: { - /* Swap the buffers: */ - png_bytep pb = png_ptr->alt_buffer; - - /* Check this first before assignment, otherwise the same buffer - * will be stored in two members of png_struct and we will do a - * double free on an OOM in png_malloc: - */ - if (pb == NULL) - { - pb = png_voidcast(png_bytep, - png_malloc(png_ptr, png_ptr->row_allocated_bytes)); - /* SECURITY: hide the heap contents: */ - memset(pb, 0, png_ptr->row_allocated_bytes); - } - - png_ptr->alt_buffer = png_ptr->row_buffer; - png_ptr->row_buffer = pb; - png_ptr->prev_in_alt = 1; /* until the filter has been undone */ - } - - /* Now png_ptr::zstream can be set, the code below sets avail_out each - * time, but next_out is used as a progress pointer so must be reset - * once at the start: - */ - png_ptr->zstream.next_out = png_ptr->row_buffer; - /* state = need_row_bytes; [not used below] */ - /* FALL THROUGH */ - - case need_row_bytes: - { - png_alloc_size_t row_bytes; - png_uint_32 pass_width = width; - int last_pass_row; - png_byte row_filter; - - if (interlaced) - pass_width = PNG_PASS_COLS(pass_width, pass); - - /* Find out how many bytes are expected for this row, this relies on - * color_type, bit_depth and png_ptr->width having been validated - * for potential overflow in png_read_start_IDAT: - */ - row_bytes = PNG_ROWBYTES(PNG_PIXEL_DEPTH(*png_ptr), pass_width); - - /* Check this every time, it's fast and safe: */ - affirm(row_bytes <= png_ptr->row_allocated_bytes); - - { /* get expanded row bytes until the row is full */ - png_alloc_size_t avail_out; - png_bytep next_out = png_ptr->zstream.next_out; - - /* The affirm will fire if something tampers with next_out and - * sets it to somewhere other than row_buffer, or if it is not - * reset between passes or at the end of a row. - */ - avail_out = next_out - png_ptr->row_buffer; /*unsigned*/ - affirm(avail_out < row_bytes); - avail_out = row_bytes - avail_out; /* 1..row_bytes */ - - { /* expand (deflate) the available IDAT input */ - int finish; - png_alloc_size_t cb; - - { /* calculate 'finish' and 'last_pass_row' */ - png_uint_32 height = png_ptr->height; - - /* last_pass_row indicates that this is the last row in - * this pass and, therefore, that the filter byte for the - * next row is irrelevant (or, indeed, may not be there if - * this is the last pass.) This is trival for - * non-interlaced images and more complex for interlaced - * ones. - * - * The test is optimized for the non-interlaced case. - */ - last_pass_row = row_number+1 >= height || (interlaced && - PNG_LAST_PASS_ROW(row_number, pass, height)); - - /* Set 'finish' if this is the last row in the last pass - * of the image. - */ - finish = last_pass_row && (!interlaced || pass >= - PNG_LAST_PASS(width, height)); - } /* calculate 'finish' and 'last_pass_row' */ - - cb = png_inflate_IDAT(png_ptr, finish, next_out, avail_out); - - if (cb < avail_out) - { - png_ptr->row_state = need_row_bytes; - return png_row_incomplete; - } - } /* expand (inflate) the availble IDAT input */ - } /* get expanded row bytes until the row is full */ - - /* The row is now complete; the return immediately above ended this - * function call if insufficient IDAT data was available. - * - * At this point all the required information to process the row has - * been read from the input stream and the original, filtered, row - * data is held in png_struct::row_buffer. - * - * png_struct::next_filter must contain the filter for *this* row, - * use this to reverse the filter: - */ - row_filter = png_ptr->next_filter; - - if (row_filter > PNG_FILTER_VALUE_NONE) - { - const unsigned int bpp = (PNG_PIXEL_DEPTH(*png_ptr)+0x7)>>3; - - /* This is checked in the read code below: */ - debug(row_filter < PNG_FILTER_VALUE_LAST); - - if (png_ptr->read_filter[0] == NULL) - png_init_filter_functions(png_ptr, bpp); - - /* If the filter code needs the previous row, it must have been - * saved previously: - */ - affirm(row_filter <= PNG_FILTER_SUB || - (png_ptr->prev_in_alt && png_ptr->alt_buffer != NULL)); - - png_ptr->read_filter[row_filter-1](row_bytes, bpp, - png_ptr->row_buffer, png_ptr->alt_buffer); - } - - /* The row has been read and is now the 'previous' row for the - * next line, we need the next filter byte to determine whether - * this needs to be saved or can be overwritten if there are row - * transformations. - */ - png_ptr->prev_in_alt = 0; - - if (last_pass_row) - { - /* No next line, so no need to store this row: */ - png_ptr->next_filter = PNG_FILTER_VALUE_NONE; - state = transform_row; - continue; - } - } /* need_row_bytes */ - - state = need_filter_byte; /* as opposed to 'start_of_pass' */ - /* FALL THROUGH */ - - case need_filter_byte: /* for the next row */ - case start_of_pass: /* so the first byte is for the upcoming row */ - { /* read filter byte */ - png_byte row_filter; - - /* This is the filter byte that precedes the *next* row, or, at - * start_of_pass, the first row: + /* Read the filter byte for the next row, previous_pixels is just + * used as a temporary buffer; it is reset below. */ png_alloc_size_t cb = png_inflate_IDAT(png_ptr, 0/*finish*/, - &png_ptr->next_filter, 1); + previous_pixels.buffer, 1U); /* This can be temporary; it verifies the invariants on how * png_inflate_IDAT updates the {next,avail}_out fields: */ #ifndef __COVERITY__ /* Suppress bogus Coverity complaint */ debug(png_ptr->zstream.avail_out == 1-cb && - png_ptr->zstream.next_out == cb + &png_ptr->next_filter); + png_ptr->zstream.next_out == cb + previous_pixels.buffer); #endif - /* next_out points into png_struct, for security do this: */ + /* next_out points to previous_pixels, for security do this: */ png_ptr->zstream.next_out = NULL; - png_ptr->zstream.avail_out = 0; + png_ptr->zstream.avail_out = 0U; /* One byte, so we either got it or have to get more input data: */ - if (cb != 1) + if (cb != 1U) { - affirm(cb == 0 && png_ptr->zstream.avail_in == 0); - png_ptr->row_state = state & 3U; + affirm(cb == 0U && png_ptr->zstream.avail_in == 0U); + png_ptr->row_state = start_of_row; return png_row_incomplete; } + } - /* Check the filter byte. */ - row_filter = png_ptr->next_filter; + /* Check the filter byte. */ + row_filter = previous_pixels.buffer[0]; + if (row_filter >= PNG_FILTER_VALUE_LAST) + png_chunk_error(png_ptr, "invalid PNG filter"); - if (row_filter >= PNG_FILTER_VALUE_LAST) - png_chunk_error(png_ptr, "invalid PNG filter"); + /* These are needed for the filter check below: */ + pass_width = width; + if (interlaced) + pass_width = PNG_PASS_COLS(pass_width, pass); - if (state == start_of_pass) - { - /* The filter is followed by the row data, but first check the - * filter byte; the spec requires that we invent an empty row - * if the first row of a pass requires it. - */ - if (row_filter >= PNG_FILTER_VALUE_UP) - { - /* x-0 == x, so do this optimization: */ - if (row_filter == PNG_FILTER_VALUE_UP) - png_ptr->next_filter = PNG_FILTER_VALUE_NONE; - - /* The Paeth predictor is always the preceding (leftwards) - * value, so this is the same as sub: - */ - else if (row_filter == PNG_FILTER_VALUE_PAETH) - png_ptr->next_filter = PNG_FILTER_VALUE_SUB; - - else /* PNG_FILTER_VALUE_AVG */ - { - /* It would be possible to 'invent' a new filter that did - * AVG using only the previous byte; it's 'SUB' of half the - * preceding value, but this seems pointless. - */ - png_bytep pb = png_ptr->alt_buffer; - - if (pb == NULL) - { - png_ptr->alt_buffer = pb = png_voidcast(png_bytep, - png_malloc(png_ptr, png_ptr->row_allocated_bytes)); - /* SECURITY: hide the heap contents: */ - memset(pb, 0, png_ptr->row_allocated_bytes); - } - - else - { - png_uint_32 pass_width = width; - png_alloc_size_t row_bytes; - - if (interlaced) - pass_width = PNG_PASS_COLS(pass_width, pass); - - /* Be safe here: this avoids a memory overwrite in a - * place where we are relying on previously validated - * values (NOTE: the row_bytes value may be truncated, - * that's a safe bug!) - */ - row_bytes = PNG_ROWBYTES(PNG_PIXEL_DEPTH(*png_ptr), - pass_width); - affirm(row_bytes <= png_ptr->row_allocated_bytes); - - /* Just zero the bytes that are needed; */ - memset(pb, 0, row_bytes); - } - - png_ptr->prev_in_alt = 1; - } - } /* silly first line filter */ - - /* Proceed to read the row bytes: */ - state = start_read_row_bytes; - continue; - } /* start_of_pass */ - - /* else state == need_filter_byte: - * png_struct::next_filter is the filter byte for the next row. - */ - } /* read filter byte */ - - case transform_row: - /* The entire row has been read and png_struct::next_filter is the - * filter for the next line or PNG_FILTER_VALUE_NONE if there is no - * next line. Do we have read transforms to perform? + /* The filter is followed by the row data, but first check the + * filter byte; the spec requires that we invent an empty row + * if the first row of a pass requires it. */ -# ifdef PNG_TRANSFORM_MECH_SUPPORTED - if (png_ptr->transform_list != NULL) + if (row_number == 0) switch (row_filter) + { + case PNG_FILTER_VALUE_UP: + /* x-0 == x, so do this optimization: */ + row_filter = PNG_FILTER_VALUE_NONE; + break; + + case PNG_FILTER_VALUE_PAETH: + /* The Paeth predictor is always the preceding (leftwards) + * value, so this is the same as sub: + */ + row_filter = PNG_FILTER_VALUE_SUB; + break; + + case PNG_FILTER_VALUE_AVG: + /* It would be possible to 'invent' a new filter that did + * AVG using only the previous byte; it's 'SUB' of half the + * preceding value, but this seems pointless. Zero out the + * row buffer to make AVG work. + * + * This is only required if 'pass' is >0, because on the first + * pass the code that allocated the row buffer zeroed it (for + * security reasons). + */ + if (pass > 0) + memset(png_ptr->row_buffer, 0U, + PNG_ROWBYTES(pixel_depth, pass_width)); + break; + + default: + break; + } /* switch row_filter */ + + /* Always zero the 'previous pixel' out at the start of a row; this + * allows the filter code to ignore the row start. + */ + memset(previous_pixels.buffer, 0U, sizeof previous_pixels.buffer); + + row_bytes_processed = 0U; + bytes_read = 0U; + + pixel_loop: + /* At this point the following must be set correctly: + * + * row_bytes_processed: bytes processed so far + * pass_width: width of a row in this pass in pixels + * pixel_depth: depth in bits of a pixel + * bytes_read: count of filtered bytes in pixel_buffer + * row_filter: filter byte for this row + * previous_pixels[0]: pixel 'a' for the filter + * previous_pixels[1]: pixel 'c' for the filter + * pixel_buffer[]: bytes_read filtered bytes + * + * The code in the loop decompresses PNG_ROW_BUFFER_SIZE filterd pixels + * from the input, unfilters and transforms them, then saves them to + * png_struct::row_buffer[row_bytes_processed...]. + */ + { /* pixel loop */ + const png_alloc_size_t row_bytes = + PNG_ROWBYTES(pixel_depth, pass_width); + png_bytep row_buffer = png_ptr->row_buffer + row_bytes_processed; + unsigned int pixels; + png_uint_32 x; + + /* Sanity check for potential buffer overwrite: */ + affirm(row_bytes > row_bytes_processed); + + /* Work out the current pixel index of the pixel at the start of the + * row buffer: + */ + switch (pixel_depth) { - unsigned int max_depth; - png_transform_control tc; - - png_init_transform_control(&tc, png_ptr); - - if (interlaced) - tc.width = PNG_PASS_COLS(width, pass); - else - tc.width = width; - - tc.sp = tc.dp = png_ptr->row_buffer; /* assume overwrite ok */ - - /* Look at the filter for the *next* row, if it uses the previous - * row (this row) then row_buffer must be preserved. - */ - if (png_ptr->next_filter > PNG_FILTER_VALUE_SUB) - { - /* 'row_buffer' is the location of what will become the - * *previous* row. Depending on the transforms it may or may - * not also be the transformed row. - */ - tc.dp = png_ptr->alt_buffer; /* Transformed row */ - - if (tc.dp == NULL) - { - /* Lazy allocation; this is where alt_buffer is - * allocated if there *are* transforms to perform. - */ - png_ptr->alt_buffer = png_voidcast(png_bytep, tc.dp = - png_malloc(png_ptr, png_ptr->row_allocated_bytes)); - } - } - - /* Run the list. It is ok if it doesn't end up doing anything; - * this can happen with a lazy init, but that may mean that - * alt_buffer is allocated when it doesn't need to be. - */ - max_depth = png_run_transform_list_forwards(png_ptr, &tc); - - /* This is too late, a memory overwrite has already happened, but - * it may still prevent exploits: - */ - affirm(max_depth <= png_ptr->row_max_pixel); - - /* This check used to be performed in png_combine_row, above; - * do it here to detect the bug earlier on (this is quite common - * while making changes to the transform code!) - */ - affirm(png_ptr->row_format == tc.format && - png_ptr->row_range == tc.range && - png_ptr->row_bit_depth == tc.bit_depth); -# ifdef PNG_READ_GAMMA_SUPPORTED - affirm(png_ptr->row_gamma == tc.gamma); -# endif /* READ_GAMMA */ - - /* If the transformed data ended up in alt_buffer then swap it - * back to row_buffer; this allows the caller to always look in - * row_buffer for the output data. - */ - if (tc.sp == png_ptr->alt_buffer) - { - png_bytep pb = png_ptr->alt_buffer; - - png_ptr->alt_buffer = png_ptr->row_buffer; - png_ptr->row_buffer = pb; - png_ptr->prev_in_alt = 1; /* else it is in row_buffer */ - } + case 1U: x = (png_uint_32)/*SAFE*/(row_bytes_processed << 3); + break; + case 2U: x = (png_uint_32)/*SAFE*/(row_bytes_processed << 2); + break; + case 4U: x = (png_uint_32)/*SAFE*/(row_bytes_processed << 1); + break; + case 8U: x = (png_uint_32)/*SAFE*/row_bytes_processed; + break; + default: x = (png_uint_32)/*SAFE*/(row_bytes_processed / bpp); + debug(row_bytes_processed % bpp == 0U); + break; } -# endif + + for (pixels = max_pixels; x < pass_width; x += pixels) + { + if (pixels > pass_width - x) + pixels = (unsigned int)/*SAFE*/(pass_width - x); + + /* At the end of the image pass Z_FINISH to zlib to optimize the + * final read (very slightly, is this worth doing?) To do this + * work out if we are at the end. + */ + { + const png_uint_32 height = png_ptr->height; + + /* last_pass_row indicates that this is the last row in this + * pass (the test is optimized for the non-interlaced case): + */ + const int last_pass_row = row_number+1 >= height || + (interlaced && PNG_LAST_PASS_ROW(row_number,pass,height)); + + /* Set 'finish' if this is the last row in the last pass of + * the image: + */ + const int finish = last_pass_row && (!interlaced || + pass >= PNG_LAST_PASS(width, height)); + + const png_alloc_size_t bytes_to_read = + PNG_ROWBYTES(pixel_depth, pixels); + + png_alloc_size_t cb; + + affirm(bytes_to_read > bytes_read); + cb = png_inflate_IDAT(png_ptr, finish, + pixel_buffer.buffer + bytes_read, + bytes_to_read - bytes_read); + bytes_read += cb; + + if (bytes_read < bytes_to_read) + { + /* Fewer bytes were read than needed: we need to stash all + * the information required at pixel_loop in png_struct so + * that the need_row_bytes case can restore it when more + * input is available. + */ + debug(png_ptr->zstream.avail_in == 0U); + png_ptr->zstream.next_out = NULL; + png_ptr->zstream.avail_out = 0U; + + png_ptr->row_bytes_read = row_bytes_processed + bytes_read; + memcpy(png_ptr->scratch, pixel_buffer.buffer, bytes_read); + memcpy(png_ptr->scratch+bytes_read, previous_pixels.buffer, + 2*bpp); + png_ptr->scratch[bytes_read+2*bpp] = row_filter; + png_ptr->row_state = need_row_bytes; + return png_row_incomplete; + } + + debug(bytes_read == bytes_to_read); + } /* fill pixel_buffer */ + + /* The buffer is full or the row is complete but the calculation + * was done using the pixel count, so double check against the + * byte count here: + */ + implies(bytes_read != input_byte_count, + bytes_read == row_bytes - row_bytes_processed); + + /* At this point all the required information to process the next + * block of pixels in the row has been read from the input stream + * and the original, filtered, row data is held in pixel_buffer. + * + * Because the buffer will be transformed after the unfilter + * operation we require whole pixels: + */ + debug(bytes_read >= bpp && bytes_read % bpp == 0); + + if (row_filter > PNG_FILTER_VALUE_NONE) + { + /* This is checked in the read code above: */ + debug(row_filter < PNG_FILTER_VALUE_LAST); + + /* Lazy init of the read functions, which allows hand crafted + * optimizations for 'bpp' (which does not change.) + */ + if (png_ptr->read_filter[0] == NULL) + png_init_filter_functions(png_ptr, bpp); + + /* Pixels 'a' then 'c' are in previous_pixels, pixel 'b' is in + * row_buffer and pixel 'x' (filtered) is in pixel_buffer. + */ + png_ptr->read_filter[row_filter-1](bytes_read, bpp, + pixel_buffer.buffer, row_buffer, previous_pixels.buffer); + } /* do the filter */ + + /* Now pixel_buffer.buffer contains the *un*filtered bytes of the + * current row and row_buffer needs updating with these. First + * preserve pixels 'a' and 'c' for the next time round the loop + * (if necessary). + */ + if (bytes_read < row_bytes - row_bytes_processed) + { + debug(bytes_read == input_byte_count); + memcpy(previous_pixels.buffer/* pixel 'a' */, + pixel_buffer.buffer + bytes_read - bpp, bpp); + memcpy(previous_pixels.buffer+bpp/* pixel 'c' */, + row_buffer + bytes_read - bpp, bpp); + } + + /* Now overwrite the previous row pixels in row_buffer with the + * current row pixels: + */ + memcpy(row_buffer, pixel_buffer.buffer, bytes_read); + row_buffer += bytes_read; + row_bytes_processed += bytes_read; + bytes_read = 0U; /* for next buffer */ + + /* Any transforms can now be performed along with any output + * handling (copy or interlace handling). + */ +# ifdef PNG_TRANSFORM_MECH_SUPPORTED + if (png_ptr->transform_list != NULL) + { + unsigned int max_depth; + png_transform_control tc; + + png_init_transform_control(&tc, png_ptr); + + tc.width = pixels; + tc.sp = tc.dp = pixel_buffer.buffer; + + /* Run the list. It is ok if it doesn't end up doing + * anything; this can happen with a lazy init. + * + * TODO: I don't think lazy inits happen any more, hence + * the 'debug' below. + */ + max_depth = png_run_transform_list_forwards(png_ptr, &tc); + debug(png_ptr->transform_list != NULL); + + /* This is too late, a stack overwrite has already + * happened, but it may still prevent exploits: + */ + affirm(max_depth <= png_ptr->row_max_pixel_depth); + + /* It is very important that the transform produces the + * same pixel format as the TC_INIT steps: + */ + affirm(png_ptr->row_format == tc.format && + png_ptr->row_range == tc.range && + png_ptr->row_bit_depth == tc.bit_depth); +# ifdef PNG_READ_GAMMA_SUPPORTED + /* This sometimes fails at present when gamma + * transforms are eliminated in PNG_TC_INIT_FINAL: + */ + debug(png_ptr->row_gamma == tc.gamma); +# endif /* READ_GAMMA */ + + /* If the caller needs the row saved (for the progressive + * read API) or if this PNG is interlaced and this row may + * be required in a subsequent pass (any pass before the + * last one) then it is stored in + * png_struct::transformed_row, and that may need to be + * allocated here. + */ +# if defined(PNG_PROGRESSIVE_READ_SUPPORTED) ||\ + defined(PNG_READ_DEINTERLACE_SUPPORTED) + if (png_ptr->transform_list != NULL && + (save_row || (png_ptr->do_interlace && pass < 6U))) + { + if (png_ptr->transformed_row == NULL) + png_ptr->transformed_row = png_voidcast(png_bytep, + png_malloc(png_ptr, png_calc_rowbytes(png_ptr, + png_ptr->row_bit_depth * + PNG_FORMAT_CHANNELS(png_ptr->row_format), + save_row ? width : (width+1U)>>1))); + + copy_row(png_ptr, png_ptr->transformed_row, + pixel_buffer.buffer, x, pixels, 1/*clear*/); + } +# endif /* PROGRESSIVE_READ || READ_DEINTERLACE */ + } /* transform_list != NULL */ +# endif /* TRANSFORM_MECH */ + + /* There are now 'pixels' possibly transformed pixels starting at + * row pixel x, where 'x' is an index in the interlaced row if + * interlacing is happening. Handle this row. + */ + if (transformed_row != NULL) + combine_row(png_ptr, transformed_row, pixel_buffer.buffer, + x, pixels, 0/*!display*/); + + if (display_row != NULL) + combine_row(png_ptr, display_row, pixel_buffer.buffer, x, + pixels, 1/*display*/); + } /* for x < pass_width */ + } /* pixel loop */ png_ptr->row_state = processing_row; return png_row_process; @@ -4348,6 +4398,29 @@ png_read_process_IDAT(png_structrp png_ptr) default: impossible("bad row state"); } /* forever switch */ + + PNG_UNUSED(save_row); /* May not be used above */ +} + +void /* PRIVATE */ +png_read_free_row_buffers(png_structrp png_ptr) +{ + /* The transformed row only gets saved if needed: */ +# if (defined(PNG_PROGRESSIVE_READ_SUPPORTED) ||\ + defined(PNG_READ_DEINTERLACE_SUPPORTED)) &&\ + defined(PNG_TRANSFORM_MECH_SUPPORTED) + if (png_ptr->transformed_row != NULL) + { + png_free(png_ptr, png_ptr->transformed_row); + png_ptr->transformed_row = NULL; + } +# endif /* PROGRESSIVE_READ || READ_DEINTERLACE */ + + if (png_ptr->row_buffer != NULL) + { + png_free(png_ptr, png_ptr->row_buffer); + png_ptr->row_buffer = NULL; + } } /* Complete reading of the IDAT chunks. This returns 0 if more data is to @@ -4367,21 +4440,10 @@ png_read_finish_IDAT(png_structrp png_ptr) IDAT_truncated } error = no_error; - /* Release row_buffer and alt_buffer first; they can use considerable - * amounts of memory. + /* Release the rowd buffers first; they can use considerable amounts of + * memory. */ - if (png_ptr->row_buffer != NULL) - { - if (png_ptr->alt_buffer != NULL) - { - png_free(png_ptr, png_ptr->alt_buffer); - png_ptr->alt_buffer = NULL; - } - - png_free(png_ptr, png_ptr->row_buffer); - png_ptr->row_buffer = NULL; - png_ptr->row_allocated_bytes = 0; - } + png_read_free_row_buffers(png_ptr); affirm(png_ptr->zowner == png_IDAT); /* else this should not be called */ diff --git a/pngstruct.h b/pngstruct.h index d85ad2c3c..6ca50c91f 100644 --- a/pngstruct.h +++ b/pngstruct.h @@ -446,15 +446,20 @@ struct png_struct_def * filter byte (which is in next_filter.) All fields are only used during * IDAT processing and start of 0. */ - png_bytep row_buffer; /* primary row buffer */ #if defined(PNG_WRITE_FILTER_SUPPORTED) || defined(PNG_READ_SUPPORTED) - png_bytep alt_buffer; /* if two row buffers needed */ -#endif -#ifdef PNG_WRITE_FILTER_SUPPORTED - png_bytep write_row[2]; /* Two rows to test filers */ -#endif + png_bytep row_buffer; /* primary row buffer */ +#endif /* WRITE_FILTER || READ */ +#if (defined(PNG_PROGRESSIVE_READ_SUPPORTED) ||\ + defined(PNG_READ_DEINTERLACE_SUPPORTED)) &&\ + defined(PNG_TRANSFORM_MECH_SUPPORTED) + png_bytep transformed_row; /* pointer to the transformed row, if + * required. May point to row_buffer. + */ +#endif /* (PROGRESSIVE_READ || READ_DEINTERLACE) && TRANSFORM_MECH */ - png_alloc_size_t row_allocated_bytes; /* Total amount allocated */ +#ifdef PNG_READ_SUPPORTED + png_alloc_size_t row_bytes_read; /* Total read in row */ +#endif /* READ */ png_uint_32 row_number; /* current row in pass */ #ifdef PNG_READ_GAMMA_SUPPORTED @@ -484,16 +489,34 @@ struct png_struct_def #ifdef PNG_SEQUENTIAL_READ_SUPPORTED unsigned int read_started :1; /* at least one call to png_read_row */ #endif +#if defined (PNG_READ_DEINTERLACE_SUPPORTED) ||\ + defined (PNG_WRITE_INTERLACE_SUPPORTED) unsigned int do_interlace :1; /* libpng handles the interlace */ +# endif /* READ_DEINTERLACE, WRITE_INTERLACE */ unsigned int pass :3; /* current (interlace) pass (0 - 6) */ /* The next two fields are just used by the IDAT process functions to store * the state of IDAT processing; they should not be altered or used by other * functions. */ - unsigned int prev_in_alt :1; /* previous row is stored in alt_buffer */ unsigned int row_state :2; /* state of row parsing (internal) */ + /* The following fields are set by png_row_init to the pixel depths of the + * pixels at various states. If transforms are not supported they will + * always be the same value: + * + * READ WRITE + * input: PNG From application + * output: To application PNG + * max: Largest in transform + */ + unsigned int row_input_pixel_depth :8; + unsigned int row_output_pixel_depth :8; + unsigned int row_max_pixel_depth :8; +#ifdef PNG_WRITE_FILTER_SUPPORTED + unsigned int filter_mask :8; /* mask of filters to consider on write */ +#endif /* WRITE_FILTER */ + # define PNG_RF_BITS 9 /* Number of bits required for the row format (below) */ #ifdef PNG_TRANSFORM_MECH_SUPPORTED /* The following fields describe the format of the user row; the output on @@ -528,9 +551,8 @@ struct png_struct_def * alpha or gray) have been inverted. * PNG_FORMAT_FLAG_INVALID NOT STORED HERE */ - unsigned int row_max_pixel :8; /* maximum pixel depth used */ #ifdef PNG_WRITE_TRANSFORMS_SUPPORTED - unsigned int info_format:PNG_RF_BITS; + unsigned int info_format:PNG_RF_BITS; /* This field is used to validate the png_info used to write the * IHDR. This is a new check in 1.7.0; previously it was possible to pass * a png_info from a png_read with the read tranform information in the @@ -539,27 +561,20 @@ struct png_struct_def */ #endif /* WRITE_TRANSFORMS */ #ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED - unsigned int write_invert_alpha :1; + unsigned int write_invert_alpha :1; /* This indicates the png_set_invert_alpha was called, it is used by the * write code to implement the transform without needing to run the whole * transform mechanism on the PNG palette data. */ #endif /* WRITE_INVERT_ALPHA */ #ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED - unsigned int rgb_to_gray_status :1; + unsigned int rgb_to_gray_status :1; /* If set an RGB pixel was encountered by the RGB to gray transform * wherein !(r==g==b). */ #endif /* RGB_TO_GRAY */ #endif /* TRANFORM_MECH */ -#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_FILTER_SUPPORTED) - png_byte next_filter; /* Filter byte for upcoming row (read or - * filters+masks to try (write, if WRITE_FILTER is - * supported). - */ -#endif - #ifdef PNG_READ_SUPPORTED /* These, and IDAT_read_size below, control how much input and output (at * most) is available to zlib. @@ -629,7 +644,8 @@ struct png_struct_def * un-filter function, this allows per-image and per-processor optimization. */ void (*read_filter[PNG_FILTER_VALUE_LAST-1])(png_alloc_size_t row_bytes, - unsigned int bpp, png_bytep row, png_const_bytep prev_row); + unsigned int bpp, png_bytep row, png_const_bytep prev_row, + png_const_bytep prev_pixels); #endif /* READ */ #ifdef PNG_WRITE_SUPPORTED @@ -749,5 +765,10 @@ struct png_struct_def #ifdef PNG_MNG_FEATURES_SUPPORTED unsigned int mng_features_permitted :3; #endif + + /* SCRATCH buffers, used when control returns to the application or a read + * loop. + */ + png_byte scratch[PNG_ROW_BUFFER_SIZE+16U]; }; #endif /* PNGSTRUCT_H */ diff --git a/pngtest.c b/pngtest.c index 126b847ff..59b13ac31 100644 --- a/pngtest.c +++ b/pngtest.c @@ -65,7 +65,7 @@ defined PNG_READ_tEXt_SUPPORTED &&\ defined PNG_READ_tIME_SUPPORTED &&\ defined PNG_READ_zTXt_SUPPORTED &&\ - defined PNG_WRITE_INTERLACING_SUPPORTED + defined PNG_WRITE_INTERLACE_SUPPORTED #ifdef PNG_ZLIB_HEADER # include PNG_ZLIB_HEADER /* defined by pnglibconf.h from 1.7 */ @@ -844,12 +844,14 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) png_structp write_ptr; png_infop write_info_ptr; png_infop write_end_info_ptr; +#ifdef PNG_WRITE_FILTER_SUPPORTED int interlace_preserved = 1; -#else +#endif /* WRITE_FILTER */ +#else /* !WRITE */ png_structp write_ptr = NULL; png_infop write_info_ptr = NULL; png_infop write_end_info_ptr = NULL; -#endif +#endif /* !WRITE */ png_bytep row_buf; png_uint_32 y; png_uint_32 width, height; @@ -1380,7 +1382,7 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) pngtest_debug("Writing row data"); #if defined(PNG_READ_DEINTERLACE_SUPPORTED) &&\ - defined(PNG_WRITE_INTERLACING_SUPPORTED) + defined(PNG_WRITE_INTERLACE_SUPPORTED) /* Both must be defined for libpng to be able to handle the interlace, * otherwise it gets handled below by simply reading and writing the passes * directly. @@ -1622,7 +1624,8 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) return (1); } -#ifdef PNG_WRITE_SUPPORTED /* else nothing was written */ +#if defined (PNG_WRITE_SUPPORTED) /* else nothing was written */ &&\ + defined (PNG_WRITE_FILTER_SUPPORTED) if (interlace_preserved != 0) /* else the files will be changed */ { for (;;) @@ -1699,7 +1702,7 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) } } } -#endif /* WRITE */ +#endif /* WRITE && WRITE_FILTER */ FCLOSE(fpin); FCLOSE(fpout); diff --git a/pngtest.png b/pngtest.png index cb3fef4cf844324953cc45f21b479a338cb9c288..2f0da831085bbdc8be3af2a651fed3b77556fc92 100644 GIT binary patch delta 5185 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 delta 8205 zcmV+oAoAbCEcZjOSOI?@w@E}nRCt{2oKI+E$(`pvIg^6l0|_F)f)|D|#6S-`!5UHr zL*{}Hx!?g6%pnD9sM?$6Fi?F{&8hpa1Ld3Q)^!-L=B7Frl!t-V!OP@f44JS7B}{|f zp_dIqH^hS~cmbX8!jMhwAzr4WDphq=+wQS<{DViODl;SF{UU$j_vicj{vP}xWLVhM z&yR*HfWLV4&Gj1xJ-Y(<6cl`x(<0J;DVhQ)d1>(Q`h7+<=lBIE)*=oc%agy*xe z1CT4}{{_JFNqLwnO<@d5W7| zb&MD-D3R+a0r-E|Fm)9GJ*)WebkYH+K8?qteR~4&_RT&T7OMxtVg>NgsJBXA%tvoj z-C8@}t^Dgpqt&p;R{(!`^NlB?{`sW*@)rPS#|Nu<`NhuXFJBCn%Zz+SKI`0^^UQgQ z-0=AMq%%D^TAfcWnUroecVPgn8mszRg}!$D-%n3gv&nxHV+7-M-}LlqSmf6_2QXT= z@-;tudOB_zMxkAxrh%R{X_MtTqg^oejOKJw9uD^m7jxI?7lue=?!xsQkJ=r0ethso zo&NH-}Z&OKFOS`^a?e`1GSu`hiG=2sT$MYZaLYlesehO2MOp-s)f5&)yo`wCnHd zhyWQdcY#Y^b2gcCc05|mUDcb;OZrw*RRJZ!LBD_SN;E|9KHRMqfPpm_EnHkWtkGB_ z7?qJ_!qL;QIX^z|#t6@6uG2ISi4;cA0)@>nS}3dV`rw7pOkA>n(%!}Cr0fj$a?(Il z_n%&LgbaY^v$Ave^rG{#@x0Ts2BR}BF5O^lk#5N+N5j>qZ�oy;aYu6M%!BJwJKS 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 + affirm(max_depth <= png_ptr->row_max_pixel_depth); } } #endif /* WRITE */ @@ -832,52 +832,42 @@ png_set_check_for_invalid_index(png_structrp png_ptr, int enabled) void /* PRIVATE */ png_init_row_info(png_structrp png_ptr) { - unsigned int max_depth = PNG_PIXEL_DEPTH(*png_ptr); + /* PNG pixels never exceed 64 bits in depth: */ + const png_byte png_depth = + png_check_bits(png_ptr, PNG_PIXEL_DEPTH(*png_ptr), 7U); -#ifdef PNG_TRANSFORM_MECH_SUPPORTED -# ifdef PNG_PALETTE_MAX_SUPPORTED - /* The palette index check stuff is *on* automatically. To handle this - * add it here, if it is supported. - * - * The logic here is a little complex because of the plethora of - * #defines controlling this stuff. - */ -# undef PNG_READ_CHECK_PALETTE -# undef PNG_WRITE_CHECK_PALETTE - -# if defined(PNG_READ_GET_PALETTE_MAX_SUPPORTED) ||\ - defined(PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED) -# ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED -# define PNG_READ_CHECK_PALETTE \ - (png_ptr->read_struct && !png_ptr->palette_index_check_disabled) -# else -# define PNG_READ_CHECK_PALETTE (png_ptr->read_struct) +# ifdef PNG_TRANSFORM_MECH_SUPPORTED + /* The palette index check stuff is *on* automatically. To handle this + * add it here, if it is supported. + */ +# ifdef PNG_PALETTE_MAX_SUPPORTED + /* The logic here is a little complex because of the plethora of + * #defines controlling this stuff. + */ + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE/* fast escape */ && ( +# if defined (PNG_READ_GET_PALETTE_MAX_SUPPORTED) ||\ + defined (PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED) + (png_ptr->read_struct +# ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED + && !png_ptr->palette_index_check_disabled) +# endif /* READ_CHECK_FOR_INVALID_INDEX */ +# else /* no READ support */ + 0 +# endif /* READ checks */ + || +# if defined (PNG_WRITE_GET_PALETTE_MAX_SUPPORTED) ||\ + defined (PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED) + (!png_ptr->read_struct +# ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED + && !png_ptr->palette_index_check_disabled) +# endif /* WRITE_CHECK_FOR_INVALID_INDEX */ +# else /* no WRITE support */ + 0 +# endif /* WRITE checks */ + )) + png_add_transform(png_ptr, 0/*size*/, palette_max_init, + PNG_TR_CHECK_PALETTE); # endif -# else /* no READ support */ -# define PNG_READ_CHECK_PALETTE 0 -# endif - -# if defined(PNG_WRITE_GET_PALETTE_MAX_SUPPORTED) ||\ - defined(PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED) -# ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED -# define PNG_WRITE_CHECK_PALETTE \ - (!png_ptr->read_struct && !png_ptr->palette_index_check_disabled) -# else -# define PNG_WRITE_CHECK_PALETTE (!png_ptr->read_struct) -# endif -# else /* no WRITE support */ -# define PNG_WRITE_CHECK_PALETTE 0 -# endif - - if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE/* fast escape */ && - (PNG_READ_CHECK_PALETTE || PNG_WRITE_CHECK_PALETTE)) - { - png_add_transform(png_ptr, 0/*size*/, palette_max_init, - PNG_TR_CHECK_PALETTE); - } -# undef PNG_READ_CHECK_PALETTE -# undef PNG_WRITE_CHECK_PALETTE -# endif /* PALETTE_MAX */ /* Application transforms may change the format of the data or, when * producing interlaced images, the number of pixels in a line. This code @@ -894,9 +884,9 @@ png_init_row_info(png_structrp png_ptr) affirm(tc.bit_depth <= 32); png_ptr->row_bit_depth = png_check_bits(png_ptr, tc.bit_depth, 6); png_ptr->row_range = png_check_bits(png_ptr, tc.range, 3); -# ifdef PNG_READ_GAMMA_SUPPORTED - png_ptr->row_gamma = tc.gamma; -# endif /* READ_GAMMA */ +# ifdef PNG_READ_GAMMA_SUPPORTED + png_ptr->row_gamma = tc.gamma; +# endif /* READ_GAMMA */ /* The above may have cancelled all the transforms in the list. */ if (png_ptr->transform_list != NULL) @@ -905,25 +895,60 @@ png_init_row_info(png_structrp png_ptr) * maximum pixel depth. At this point the transforms can swap * out their initialization code. */ - unsigned int depth = init_transform_mech(png_ptr, &tc, 0/*final*/); + unsigned int max_depth = + init_transform_mech(png_ptr, &tc, 0/*final*/); - if (depth > max_depth) - max_depth = depth; - -# ifdef PNG_READ_TRANSFORMS_SUPPORTED - /* Set this now because it only gets resolved finally at this - * point. + /* init_transform_mech is expected to take the input depth into + * account: */ - png_ptr->invalid_info = tc.invalid_info; -# endif /* READ_TRANSFORMS */ + debug(max_depth >= png_depth); + if (max_depth < png_depth) + max_depth = png_depth; + affirm(max_depth <= (png_ptr->read_struct ? 128U : 64U)); + +# ifdef PNG_READ_TRANSFORMS_SUPPORTED + /* Set this now because it only gets resolved finally at this + * point. + */ + png_ptr->invalid_info = tc.invalid_info; +# endif /* READ_TRANSFORMS */ /* And check the transform fields: */ affirm(png_ptr->row_format == tc.format && png_ptr->row_range == tc.range && png_ptr->row_bit_depth == tc.bit_depth); -# ifdef PNG_READ_GAMMA_SUPPORTED - affirm(png_ptr->row_gamma == tc.gamma); -# endif /* READ_GAMMA */ +# ifdef PNG_READ_GAMMA_SUPPORTED + affirm(png_ptr->row_gamma == tc.gamma); +# endif /* READ_GAMMA */ + + png_ptr->row_max_pixel_depth = + png_check_bits(png_ptr, max_depth, 8U); + + /* On 'read' input_depth is the PNG pixel depth and output_depth is + * the depth of the pixels passed to the application, but on 'write' + * the transform list is reversed so output_depth is the PNG depth + * and input_depth the application depth. + */ + { + const png_byte app_depth = + png_check_bits(png_ptr, PNG_TC_PIXEL_DEPTH(tc), 8U); + + affirm(app_depth <= max_depth); + + if (png_ptr->read_struct) + { + png_ptr->row_input_pixel_depth = png_depth; + png_ptr->row_output_pixel_depth = app_depth; + } + + else + { + png_ptr->row_input_pixel_depth = app_depth; + png_ptr->row_output_pixel_depth = png_depth; + } + + return; /* to skip the default settings below */ + } } } @@ -934,46 +959,27 @@ png_init_row_info(png_structrp png_ptr) png_ptr->row_bit_depth = png_check_bits(png_ptr, png_ptr->bit_depth, 6); png_ptr->row_range = 0; -# ifdef PNG_READ_GAMMA_SUPPORTED - if ((png_ptr->colorspace.flags & - (PNG_COLORSPACE_INVALID|PNG_COLORSPACE_HAVE_GAMMA)) == - PNG_COLORSPACE_HAVE_GAMMA) - png_ptr->row_gamma = png_ptr->colorspace.gamma; -# endif /* READ_GAMMA */ -# ifdef PNG_READ_TRANSFORMS_SUPPORTED - png_ptr->invalid_info = 0U; -# endif /* READ_TRANSFORMS */ +# ifdef PNG_READ_GAMMA_SUPPORTED + if ((png_ptr->colorspace.flags & + (PNG_COLORSPACE_INVALID|PNG_COLORSPACE_HAVE_GAMMA)) == + PNG_COLORSPACE_HAVE_GAMMA) + png_ptr->row_gamma = png_ptr->colorspace.gamma; +# endif /* READ_GAMMA */ +# ifdef PNG_READ_TRANSFORMS_SUPPORTED + png_ptr->invalid_info = 0U; +# endif /* READ_TRANSFORMS */ } +# endif /* TRANSFORM_MECH */ - /* 'max_depth' is now the maximum size of a pixel, including intermediate - * results during the transforms. The current limit is 4 32-bit channels: - */ - affirm(max_depth <= 128); - png_ptr->row_max_pixel = png_check_bits(png_ptr, max_depth, 8); -#endif /* TRANSFORM_MECH */ - - /* png_calc_rowbytes does a png_error on overflow. This is how the libpng - * code validates that there won't be overflows on future PNG_ROWBYTES - * calls. - * - * The largest integer we can guarantee with ANSI-C is a 32-bit one (unsigned - * long). To allow the row to be accessed as png_uint_32[] this code sets - * the allocation to a multiple of 4: + /* We get here if there are no transforms therefore no change to the pixel + * bit depths. */ - { - png_alloc_size_t rowbytes = png_calc_rowbytes(png_ptr, max_depth, - png_ptr->width); - png_alloc_size_t alloc = (rowbytes + 3U) & ~3U; - - if (alloc < rowbytes) - png_error(png_ptr, "PNG row exceeds system limits"); - - png_ptr->row_allocated_bytes = alloc; - } + png_ptr->row_output_pixel_depth = png_ptr->row_max_pixel_depth = + png_ptr->row_input_pixel_depth = png_depth; } #if defined(PNG_READ_DEINTERLACE_SUPPORTED) || \ - defined(PNG_WRITE_INTERLACING_SUPPORTED) + defined(PNG_WRITE_INTERLACE_SUPPORTED) int PNGAPI png_set_interlace_handling(png_structrp png_ptr) { @@ -999,26 +1005,25 @@ png_set_interlace_handling(png_structrp png_ptr) else /* write */ { -# ifdef PNG_WRITE_INTERLACING_SUPPORTED +# ifdef PNG_WRITE_INTERLACE_SUPPORTED if (png_ptr->interlaced) { png_ptr->do_interlace = 1; - png_set_write_interlace(png_ptr); return PNG_INTERLACE_ADAM7_PASSES; } return 1; -# else /* !WRITE_INTERLACING */ +# else /* !WRITE_INTERLACE */ png_app_error(png_ptr, "no interlace support"); /* return 0 below */ -# endif /* !WRITE_INTERLACING */ +# endif /* !WRITE_INTERLACE */ } } /* API CHANGE: 1.7.0: returns 0 if called with a NULL png_ptr */ return 0; } -#endif /* READ_DEINTERLACE || WRITE_INTERLACING */ +#endif /* READ_DEINTERLACE || WRITE_INTERLACE */ #ifdef PNG_MNG_READ_FEATURES_SUPPORTED /* Undoes intrapixel differencing, this is called immediately after the PNG diff --git a/pngwrite.c b/pngwrite.c index 4a751003e..5e3781b1f 100644 --- a/pngwrite.c +++ b/pngwrite.c @@ -425,12 +425,14 @@ png_write_end(png_structrp png_ptr, png_inforp info_ptr) * * Don't do this if the IDAT came from unknowns (TBD) or the app, above. * - * The check depends on the precise logic in png_write_finish_row. + * The check depends on the precise logic in png_write_row. */ - else if (png_ptr->interlaced ? png_ptr->pass != PNG_INTERLACE_ADAM7_PASSES : - png_ptr->row_number != png_ptr->height) + else if (png_ptr->pass != 7U) png_app_error(png_ptr, "png_write_row not called to last row"); + else + debug(png_ptr->row_number == 0U); + /* See if user wants us to write information chunks */ if (info_ptr != NULL) { @@ -597,7 +599,7 @@ png_write_image(png_structrp png_ptr, png_bytepp image) png_debug(1, "in png_write_image"); -#ifdef PNG_WRITE_INTERLACING_SUPPORTED +#ifdef PNG_WRITE_INTERLACE_SUPPORTED /* Initialize interlace handling. If image is not interlaced, * this will set pass to 1 */ @@ -624,280 +626,71 @@ png_write_image(png_structrp png_ptr, png_bytepp image) } } -/* Called to advance to the next row. A row may not have been output when - * libpng handles the interlace passes because the app hands libpng every image - * row for every pass. - * - * This function also writes the current row, if the pointer to the bytes is - * non-NULL, and does so with the appropriate 'flush' argument to zlib. - */ +#if defined(PNG_WRITE_INTERLACE_SUPPORTED) ||\ + defined(PNG_WRITE_TRANSFORMS_SUPPORTED) static void -png_write_finish_row(png_structrp png_ptr, png_byte filter_byte, - png_const_bytep row, png_alloc_size_t row_bytes) +write_row_buffered(png_structrp png_ptr, png_const_bytep row, + int first_row_in_pass, int last_pass_row, int end_of_image, + void (*copy_fn)(png_const_structrp png_ptr, png_bytep row_buffer, + png_const_bytep row, png_uint_32 x, unsigned int count, unsigned int p), + unsigned int copy_parameter) { - const png_uint_32 height = png_ptr->height; - png_uint_32 row_number = png_ptr->row_number; - int flush = Z_NO_FLUSH; + unsigned int max_pixels = png_max_pixel_block(png_ptr); + const unsigned int pass = png_ptr->pass; + const png_uint_32 width = png_ptr->interlaced == PNG_INTERLACE_NONE ? + png_ptr->width : PNG_PASS_COLS(png_ptr->width, pass); + png_uint_32 x; + unsigned int filters; + png_byte prev_pixels[4*2*2]; /* 2 pixels up to 4 2-byte channels each */ - png_debug(1, "in png_write_finish_row"); + memset(prev_pixels, 0U, sizeof prev_pixels); - if (png_ptr->interlaced) + for (x = 0U, filters = 0U; x < width; x += max_pixels) { - unsigned int pass = png_ptr->pass; + int finish = 0; -# ifdef PNG_WRITE_INTERLACING_SUPPORTED - if (png_ptr->do_interlace) - { - /* libpng is doing the de-interlace. */ - /* Z_FINISH must be set on the last row present in the image, not - * the actual last row. - * - * NOTE: this means that the application need not call libpng all - * the way to the end of the image, but this is double checked - * below in png_write_end where png_ptr->pass is checked. - */ - if (pass == PNG_LAST_PASS(png_ptr->width, height) && - PNG_LAST_PASS_ROW(row_number, pass, height) && - PNG_ROW_IN_INTERLACE_PASS(row_number, pass)) - flush = Z_FINISH; /* end of image */ - - if (++row_number == height) - { - ++pass; - row_number = 0; - png_ptr->pass = pass & 0x7; - } - } /* libpng doing interlace */ - - else /* app is doing the interlacing */ -# endif /* WRITE_INTERLACING */ - /* The application has to hand us interlaced rows. In this case - * row_number is the row number in the pass (this is not the - * behavior in the read code, where it is always the row number in - * the image.) - * - * Note that for any image row 0 of pass 0 is always present, so the - * check after the 'if' is not required at the start. - */ + union { - affirm(row != NULL); + PNG_ROW_BUFFER_ALIGN_TYPE force_buffer_alignment; + png_byte buffer[PNG_ROW_BUFFER_SIZE]; + } pixel_buffer; - if (++row_number == PNG_PASS_ROWS(height, pass)) - { - const png_uint_32 width = png_ptr->width; + if (max_pixels > width - x) + max_pixels = (unsigned int)/*SAFE*/(width - x); - /* Next pass, but it may not be present because of the width. */ - row_number = 0; + if (end_of_image && x + max_pixels >= width) + finish = 1; - for (;;) - { - if (++pass == PNG_INTERLACE_ADAM7_PASSES) - { - flush = Z_FINISH; - break; /* end of image */ - } + /* Copy a block of input pixels into the buffer, effecting the interlace + * on the way if required. The argument is the number of pixels in the + * buffer, not the number handled from the input which will be larger in + * the interlaced case. + */ + copy_fn(png_ptr, pixel_buffer.buffer, row, x, max_pixels, copy_parameter); - if (PNG_PASS_IN_IMAGE(width, height, pass)) - break; - } - - png_ptr->pass = pass & 0x7; - } /* end of pass */ - } /* app doing interlace */ - } /* writing an interlaced PNG */ - - else /* PNG not interlaced */ if (++row_number == height) - flush = Z_FINISH; - - png_ptr->row_number = row_number; - - if (row != NULL) - { - png_compress_IDAT(png_ptr, &filter_byte, 1, Z_NO_FLUSH); - png_compress_IDAT(png_ptr, row, row_bytes, flush); - -# ifdef PNG_WRITE_FLUSH_SUPPORTED - if (flush == Z_NO_FLUSH && - ++png_ptr->flush_rows >= png_ptr->flush_dist && - png_ptr->flush_dist > 0) - png_write_flush(png_ptr); -# endif /* WRITE_FLUSH */ - } - - /* The calculations above should ensure that Z_FINISH is set on the last row - * written, the following rows are empty pass rows, so the deflate stream - * should already have been closed: - */ - else - affirm(flush == Z_NO_FLUSH || png_ptr->zowner == 0); -} - -/* Called by user to write a row of image data */ -void PNGAPI -png_write_row(png_structrp png_ptr, png_const_bytep row) -{ - png_uint_32 row_number, row_width; - unsigned int pass; -# ifdef PNG_WRITE_FILTER_SUPPORTED - int last_pass_row, first_pass_row; -# endif - - if (png_ptr == NULL) - return; - - png_debug2(1, "in png_write_row (row %u, pass %d)", - png_ptr->row_number, png_ptr->pass); - - row_number = png_ptr->row_number; - row_width = png_ptr->width; - pass = png_ptr->pass; - - /* Unlike the read code initialization happens automatically: */ - if (row_number == 0 && pass == 0) - { - png_init_row_info(png_ptr); /* doesn't change row/pass/width */ - - /* If the app takes a png_info from a read operation and if the app has - * performed transforms on the data the png_info can contain IHDR - * information that cannot be represented in PNG. The code that writes - * the IHDR takes the color type from the png_info::format. The app adds - * transforms, before or after writing the IHDR, then the IHDR color_type - * stored in png_struct::color_type is used in png_init_row_info above to - * work out the actual row format. - * - * Prior to 1.7.0 this was not verified (there was no easy way to do so). - * Now we can check it here, however this is an: - * - * API CHANGE: in 1.7.0 an error may be flagged against bogus info_struct - * formats even though the app had removed them itself. It's just a - * warning at present. + /* Now pixel_buffer[0..max_pixels-1] contains max_pixels pixels which may + * need to be transformed (the interlace has already been handled). */ # ifdef PNG_WRITE_TRANSFORMS_SUPPORTED - /* The test is that either the row_format produced by the write - * transforms exactly matches that in the original info_struct::format - * or that the info_struct::format was a simple mapping of the - * color_type that ended up in the IHDR: - */ - if (png_ptr->row_format != png_ptr->info_format && - PNG_FORMAT_FROM_COLOR_TYPE(png_ptr->color_type) != - png_ptr->info_format) - png_app_warning(png_ptr, "info_struct format does not match IHDR"); -# endif /* WRITE_TRANSFORMS */ - } - - else if (row_number >= png_ptr->height || pass >= PNG_INTERLACE_ADAM7_PASSES) - png_error(png_ptr, "Too many calls to png_write_row"); - - /* If interlaced and not interested in row, return. Note that the - * assumption here, as in the read code, is that if the app wants to write an - * interlaced image when libpng does not support WRITE_INTERLACING the app - * will only provide the rows actually in the pass. - */ - if (png_ptr->interlaced) - { -# ifdef PNG_WRITE_INTERLACING_SUPPORTED - if (png_ptr->do_interlace) - { - /* libpng is doing the de-interlace. */ - if (!PNG_ROW_IN_INTERLACE_PASS(row_number, pass) || - PNG_PASS_COLS(row_width, pass) == 0) - { - /* Not in the pass; advance to the next row. Notice that because - * the app is expected to call us once for every image row in - * every pass it is sufficient to just add one to row_number - * here. - */ - png_write_finish_row(png_ptr, 0, NULL, 0); - return; - } - - /* Else: this row must be output, row_number is the row in the - * image. - */ - last_pass_row = - PNG_LAST_PASS_ROW(row_number, pass, png_ptr->height); - first_pass_row = row_number == PNG_PASS_START_ROW(pass); - debug(row_number >= PNG_PASS_START_ROW(pass)); - } - - else /* app is doing the interlacing */ -# endif /* WRITE_INTERLACING */ - { - /* The interlaced rows come from the application and they have the - * correct width, row_number is the row number in the pass, not the - * number of the corresponding (expanded) image row. - */ - row_width = PNG_PASS_COLS(row_width, pass); -# ifdef PNG_WRITE_FILTER_SUPPORTED - last_pass_row = - row_number+1 >= PNG_PASS_ROWS(png_ptr->height, pass); - first_pass_row = row_number == 0; -# endif /* WRITE_INTERLACING */ - } - } /* writing an interlaced PNG */ - -# ifdef PNG_WRITE_FILTER_SUPPORTED - else /* not an interlaced PNG */ - { - last_pass_row = row_number+1 >= png_ptr->height; - first_pass_row = row_number == 0; - } -# endif /* WRITE_INTERLACING */ - - /* 1.7.0: pretty much everything except the PNG row filter happens under the - * control of the transform code. - * - * png_struct::row_format is the *input* row format. During the transforms - * the png_transform_control::{sp,dp} pointers are used in the normal fashion - * with dp initially set to png_struct::row_buffer. - * - * After the transforms if there is no filtering to be done (WRITE_FILTER is - * not supported) 'sp' is written directly; it may be png_struct::row_buffer - * or it may be the original 'row' parameter to this routine. There is no - * need to save the transformed (PNG format) row. - * - * If there is filtering to be done then either the original row or - * png_transform_control::sp is filtered against the previous row, which is - * in png_struct::alt_buffer, the result is written with the appropriate - * filter byte and then the original row or png_transform_control::sp is - * saved to png_struct::alt_buffer. - * - * Thus there are four control flow possibilities depending on the pair of - * compile time flags [WRITE_TRANSFORM_MECH,WRITE_FILTER]. The simplest - * write code, which requires no internal buffer, arises when both are - * compiled out. alt_buffer is only required if WRITE_FILTER is supported - * and row_buffer is required when either are supported. - */ - { - png_byte filter_byte; - unsigned int output_bpp; /* bits per pixel */ - png_const_bytep output_row; - png_alloc_size_t output_bytes; - -# ifdef PNG_TRANSFORM_MECH_SUPPORTED - if (png_ptr->transform_list != NULL) /* else no transforms */ + if (png_ptr->transform_list != NULL) { png_transform_control tc; /* The initial values are the memory format, this was worked out in - * png_init_row_info above. + * png_init_row_info below. */ memset(&tc, 0, sizeof tc); tc.png_ptr = png_ptr; - tc.sp = row; - tc.dp = png_ptr->row_buffer; + tc.sp = tc.dp = pixel_buffer.buffer; - if (tc.dp == NULL) - png_ptr->row_buffer = png_voidcast(png_bytep, tc.dp = - png_malloc(png_ptr, png_ptr->row_allocated_bytes)); - - tc.width = row_width; /* width of interlaced row */ + tc.width = max_pixels; /* width of block that we have */ tc.format = png_ptr->row_format; tc.range = png_ptr->row_range; tc.bit_depth = png_ptr->row_bit_depth; /* tc.init == 0 */ /* tc.caching: not used */ /* tc.palette: not used */ + debug(PNG_TC_PIXEL_DEPTH(tc) == png_ptr->row_input_pixel_depth); /* Run the list. */ png_run_transform_list_backwards(png_ptr, &tc); @@ -910,88 +703,394 @@ png_write_row(png_structrp png_ptr, png_const_bytep row) /* Now we must have the PNG format from the IHDR: */ affirm(png_ptr->bit_depth == tc.bit_depth && png_ptr->color_type == PNG_COLOR_TYPE_FROM_FORMAT(tc.format)); - - /* If libpng is handling the interlacing the row width must now - * match the width required for this pass. - */ - affirm(tc.width == (!png_ptr->do_interlace ? - row_width : PNG_PASS_COLS(row_width, pass))); - - row_width = tc.width; - output_row = png_voidcast(png_const_bytep, tc.sp); } +# endif /* WRITE_TRANSFORMS */ - else /* no transforms */ -# endif /* TRANSFORM_MECH */ - output_row = row; + /* Call png_write_filter_row to write this block of data, the test on + * maxpixels says if this is the final block in the row, 'filters' is + * initialized when 0 is 0 then preserved here for later blocks: + */ + filters = png_write_filter_row(png_ptr, prev_pixels, pixel_buffer.buffer, + x, max_pixels, first_row_in_pass, last_pass_row, filters, finish); + } +} +#endif /* WRITE { INTERLACING || TRANSFORMS } */ - output_bpp = PNG_PIXEL_DEPTH(*png_ptr); - output_bytes = PNG_ROWBYTES(output_bpp, row_width); +#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED +static void +copy_row(png_const_structrp png_ptr, png_bytep row_buffer, + png_const_bytep row, png_uint_32 x, unsigned int count, + unsigned int pixel_depth) +{ + /* Copy row[x..x+count] pixels to row_buffer. */ + png_copy_row(png_ptr, row_buffer, row, x, count, pixel_depth, 1/*clear*/); +} +#endif /* WRITE_TRANSFORMS */ -# ifdef PNG_WRITE_FILTER_SUPPORTED +#ifdef PNG_WRITE_INTERLACE_SUPPORTED +static void +interlace_row_lbd(png_const_structrp png_ptr, png_bytep dp, png_const_bytep sp, + png_uint_32 x, unsigned int count, const unsigned int B) +{ + /* Pick out the correct pixels for the interlace pass. The basic idea here + * is to go through the row with a source pointer and a destination pointer + * (sp and dp), and copy the correct pixels for the pass. As the row gets + * compacted, sp will always be >= dp, so we should never overwrite anything. + * See the default: case for the easiest code to understand. + */ + const unsigned int pass = png_ptr->pass; + png_uint_32 i = PNG_COL_FROM_PASS_COL(x, pass); + const unsigned int inc = PNG_PASS_COL_OFFSET(pass); + + /* For pixels less than one byte wide the correct pixels have to be + * extracted from the input bytes. Because we are reading data in + * the application memory format we cannot rely on the PNG big + * endian order. Notice that this was apparently broken before + * 1.7.0. + * + * In libpng 1.7.0 libpng uses a classic bit-pump to optimize the + * extraction. In all passes before the last (6/7) no two pixels + * are adjacent in the input, so we are always extracting 1 bit. + * At present the code uses an 8-bit buffer to avoid coding for + * different byte sexes, but this could easily be changed. + * + * 'i' is the bit-index of bit in the input (sp[]), so, + * considering the 1-bit per pixel case, sp[i>>3] is the byte + * and the bit is bit (i&7) (0 lowest) on swapped (little endian) + * data or 7-(i&7) on PNG default (big-endian) data. + * + * Define these macros, where: + * + * B: the log2 bit depth (0, 1, 2 for 1bpp, 2bpp or 4bpp) of + * the data; this should be a constant. + * sp: the source pointer (sp) (a png_const_bytep) + * i: the pixel index in the input (png_uint_32) + * j: the bit index in the output (unsigned int) + * + * Unlike 'i', 'j' is interpreted directly; for LSB bytes it counts + * up, for MSB it counts down. + * + * NOTE: this could all be expanded to eliminate the code below by + * the time honoured copy'n'paste into three separate functions. This + * might be worth doing in the future. + */ +# define PIXEL_MASK ((1U << (1<>(3-(B))]) /* byte to use */ +# define SP_OFFSET_LSB ((BIT_MASK & i) << (B)) +# define SP_OFFSET_MSB ((BIT_MASK & ~i) << (B)) +# define SP_PIXEL(sex) ((SP_BYTE >> SP_OFFSET_ ## sex) & PIXEL_MASK) + { + unsigned int j; + unsigned int d; + + /* The data is always in the PNG, big-endian, format: */ + for (j = 8U, d = 0U; count > 0U; --count, i += inc) + { /* big-endian */ + j -= 1U<pass; + const unsigned int inc = PNG_PASS_COL_OFFSET(pass); + + /* Loop through the input copying each pixel to the correct place + * in the output. Note that the loop may be executed 0 times if + * this is called on a narrow image that does not contain this + * pass. + */ + for (sp += PNG_COL_FROM_PASS_COL(x, pass) * cbytes; count > 0; + --count, sp += inc * cbytes, dp += cbytes) + memcpy(dp, sp, cbytes); +} +#endif /* WRITE_INTERLACE */ + +static void +write_row_unbuffered(png_structrp png_ptr, png_const_bytep row, + int first_row_in_pass, int last_pass_row, int end_of_image) +{ + /* Split the row into blocks of the appropriate size: */ + const unsigned int input_depth = png_ptr->row_input_pixel_depth; + unsigned int max_pixels = png_max_pixel_block(png_ptr); + png_uint_32 max_bytes = max_pixels; + const unsigned int pass = png_ptr->pass; + const png_uint_32 width = png_ptr->interlaced == PNG_INTERLACE_NONE ? + png_ptr->width : PNG_PASS_COLS(png_ptr->width, pass); + png_uint_32 x; + unsigned int filters; + png_byte prev_pixels[4*2*2]; /* 2 pixels up to 4 2-byte channels each */ + + /* max_pixels is at most 16 bits, input_depth is at most 64, so the product + * always fits in 32 bits: + */ + max_bytes *= input_depth; /* bits */ + debug(input_depth <= 64 && (max_bytes & 7U) == 0U); + max_bytes >>= 3; + + memset(prev_pixels, 0U, sizeof prev_pixels); + + for (x = 0U, filters = 0U; x < width; x += max_pixels, row += max_bytes) + { + int finish = 0; + + if (max_pixels > width - x) + { + max_bytes = width - x; + max_pixels = (unsigned int)/*SAFE*/max_bytes; + max_bytes = (max_bytes * input_depth + 7U) >> 3; + } + + if (end_of_image && x + max_pixels >= width) + finish = 1; + + filters = png_write_filter_row(png_ptr, prev_pixels, row, x, max_pixels, + first_row_in_pass, last_pass_row, filters, finish); + } +} + +static void +write_row_core(png_structrp png_ptr, png_const_bytep row, + int first_row_in_pass, int last_pass_row, int end_of_image) +{ +# ifdef PNG_WRITE_TRANSFORMS_SUPPORTED + if (png_ptr->transform_list != NULL) + write_row_buffered(png_ptr, row, first_row_in_pass, last_pass_row, + end_of_image, copy_row, png_ptr->row_input_pixel_depth); + + else +# endif /* WRITE_TRANSFORMS */ + + /* If control reaches this point the intermediate buffer is not required and + * the input data can be used unmodified. + */ + write_row_unbuffered(png_ptr, row, first_row_in_pass, last_pass_row, + end_of_image); +} + +/* Write a single non-interlaced row. */ +static void +write_row_non_interlaced(png_structrp png_ptr, png_const_bytep row) +{ + const png_uint_32 row_number = png_ptr->row_number+1U; + const int last_pass_row = row_number == png_ptr->height; + + write_row_core(png_ptr, row, row_number == 1U, last_pass_row, + last_pass_row); + + if (!last_pass_row) + png_ptr->row_number = row_number; + + else + { + png_ptr->row_number = 0U; + png_ptr->pass = 7U; + } +} + +/* Write a single interlaced row. */ +static void +write_row_interlaced(png_structrp png_ptr, png_const_bytep row) +{ + /* row_number is the row in the pass. The app must only call png_write_row + * the correct number of times. 'pass' is set to 7U at the end. + */ + const png_uint_32 row_number = png_ptr->row_number+1U; + unsigned int pass = png_ptr->pass; + const int last_pass_row = row_number == PNG_PASS_ROWS(png_ptr->height, pass); + + write_row_core(png_ptr, row, row_number == 1U, last_pass_row, + last_pass_row && pass == PNG_LAST_PASS(png_ptr->width, png_ptr->height)); + + if (!last_pass_row) + png_ptr->row_number = row_number; + + else + { + png_ptr->row_number = 0U; + + do + ++pass; + while (pass < 7U && + !PNG_PASS_IN_IMAGE(png_ptr->width, png_ptr->height, pass)); + + png_ptr->pass = 0x7U & pass; + } +} + +#ifdef PNG_WRITE_INTERLACE_SUPPORTED +/* Interlace a row then write it out. */ +static int +interlace_row(png_structrp png_ptr, png_const_bytep row) +{ + /* row_number is in the image, it may not be in the pass and, likewise, the + * pass may not exist. + */ + const png_uint_32 row_number = png_ptr->row_number; /* in image */ + const unsigned int pass = png_ptr->pass; + const int write_row = png_ptr->width > PNG_PASS_START_COL(pass) && + PNG_ROW_IN_INTERLACE_PASS(row_number, pass); + + if (write_row) + { + const int last_pass_row = + PNG_LAST_PASS_ROW(row_number, pass, png_ptr->height); + + if (pass < 6) + { + /* Libpng is doing the interlacing and pixels need to be selected + * from the input row for this pass. + */ + /* row interlacing uses either the log bit depth for low bit + * depth input or the byte count for 8bpp or bigger pixels. + */ + const unsigned int input_depth = png_ptr->row_input_pixel_depth; + unsigned int B = 0; /* log2(input_depth) */ + const int end_of_image = last_pass_row && + pass == PNG_LAST_PASS(png_ptr->width, png_ptr->height); + + switch (input_depth) { - png_const_bytep unfiltered_row = output_row; - png_bytep alt_buffer = png_ptr->alt_buffer; + case 4U: /* B will be 2 */ + ++B; + /*FALL THROUGH*/ + case 2U: /* B will be 1 */ + ++B; + /*FALL THROUGH*/ + case 1U: /* B will be 0 */ + write_row_buffered(png_ptr, row, + row_number == PNG_PASS_START_ROW(pass), last_pass_row, + end_of_image, interlace_row_lbd, B); + break; - /* If necessary find an appropriate filter and apply it to get the - * filtered row. The function may return the original argument, it - * fills in 'filter_byte' appropriately. - */ - if (png_ptr->next_filter != PNG_FILTER_NONE) - output_row = png_write_filter_row(png_ptr, output_row, - first_pass_row, alt_buffer, output_bytes, - (output_bpp+7)>>3 /* bytes per pixel */, &filter_byte); - - else - filter_byte = PNG_FILTER_VALUE_NONE; - - /* If the available filters require it, or ever did (as evidenced by - * the presence of 'alt_buffer', store the unfiltered row in - * alt_buffer. Note that this does not happen on the last row of a - * pass, or the image. - */ - if (!last_pass_row && (alt_buffer != NULL || (png_ptr->next_filter & - (PNG_FILTER_UP+PNG_FILTER_AVG+PNG_FILTER_PAETH)) != 0)) - { - if (unfiltered_row == row) /* Must be copied */ - { - if (alt_buffer == NULL) - png_ptr->alt_buffer = alt_buffer = png_voidcast(png_bytep, - png_malloc(png_ptr, png_ptr->row_allocated_bytes)); - - memcpy(alt_buffer, unfiltered_row, output_bytes); - } - - else /* Can be swapped */ - { - png_bytep tmp = png_ptr->row_buffer; - - affirm(unfiltered_row == tmp); - png_ptr->row_buffer = alt_buffer; /* may be NULL */ - png_ptr->alt_buffer = tmp; - } - } + default: /* Parameter is the pixel size in bytes */ + write_row_buffered(png_ptr, row, + row_number == PNG_PASS_START_ROW(pass), last_pass_row, + end_of_image, interlace_row_byte, input_depth >> 3); + break; } -# else /* !WRITE_FILTER: no previous row to store */ - filter_byte = PNG_FILTER_VALUE_NONE; -# endif /* !WRITE_FILTER */ + } - png_write_finish_row(png_ptr, filter_byte, output_row, output_bytes); + else /* pass 6; no interlacing required */ + write_row_core(png_ptr, row, row_number == 1U, last_pass_row, + last_pass_row); } - /* API CHANGE: 1.7.0: this is now called after png_struct::row_number and - * png_struct::pass have been updated and, at the end of the image, after the - * deflate stream has been closed. The order of the call with respect to the - * flush operation has also changed. The callback can't discover any of this - * unless it relies on the write callbacks to find the row data, and that was - * never predictable. - * - * - * TODO: API CHANGE: pass the row bytes to this function, it would be more - * useful. - */ - if (png_ptr->write_row_fn != NULL) - (*(png_ptr->write_row_fn))(png_ptr, row_number, pass); + if (row_number+1U < png_ptr->height) + png_ptr->row_number = row_number+1U; + + else + { + png_ptr->row_number = 0U; + png_ptr->pass = 0x7U & (pass+1U); + } + + return write_row; +} +#endif /* WRITE_INTERLACE */ + +/* Called by user to write a row of image data */ +void PNGAPI +png_write_row(png_structrp png_ptr, png_const_bytep row) +{ + if (png_ptr != NULL) + { + const unsigned int pass = png_ptr->pass; + const png_uint_32 row_number = png_ptr->row_number; + + /* Unlike the read code initialization happens automatically: */ + if (row_number == 0 && pass == 0) + { + png_init_row_info(png_ptr); /* doesn't change row/pass/width */ + +# ifdef PNG_WRITE_TRANSFORMS_SUPPORTED + /* If the app takes a png_info from a read operation and if the app + * has performed transforms on the data the png_info can contain + * IHDR information that cannot be represented in PNG. The code + * that writes the IHDR takes the color type from the + * png_info::format. The app adds transforms, before or after + * writing the IHDR, then the IHDR color_type stored in + * png_struct::color_type is used in png_init_row_info above to work + * out the actual row format. + * + * Prior to 1.7.0 this was not verified (there was no easy way to do + * so). Now we can check it here, however this is an: + * + * API CHANGE: in 1.7.0 an error may be flagged against bogus + * info_struct formats even though the app had removed them itself. + * It's just a warning at present. + * + * The test is that either the row_format produced by the write + * transforms exactly matches that in the original + * info_struct::format or that the info_struct::format was a simple + * mapping of the color_type that ended up in the IHDR: + */ + if (png_ptr->row_format != png_ptr->info_format && + PNG_FORMAT_FROM_COLOR_TYPE(png_ptr->color_type) != + png_ptr->info_format) + png_app_warning(png_ptr, + "info_struct format does not match IHDR"); +# endif /* WRITE_TRANSFORMS */ + } + + else if (pass == 7U) /* too many calls; write already ended */ + { + debug(png_ptr->row_number == 0U); + png_app_error(png_ptr, "Too many calls to png_write_row"); + return; + } + + /* Make sure there is a row to write: */ + if (row == NULL) + { + png_app_error(png_ptr, "NULL row pointer to png_write_row"); + return; + } + + if (png_ptr->interlaced == PNG_INTERLACE_NONE) + write_row_non_interlaced(png_ptr, row); + +# ifdef PNG_WRITE_INTERLACE_SUPPORTED + /* Optional: libpng does the interlacing, app passes every row of the + * image the required number of times. + */ + else if (png_ptr->do_interlace != 0U) + { + if (!interlace_row(png_ptr, row)) + return; /* row skipped; skip the write callback */ + } +# endif + + else /* app does the interlacing */ + write_row_interlaced(png_ptr, row); + + /* API CHANGE: 1.7.0: this is now called after png_struct::row_number and + * png_struct::pass have been updated and, at the end of the image, after + * the deflate stream has been closed. The order of the call with respect + * to the flush operation has also changed. The callback can't discover + * any of this unless it relies on the write callbacks to find the row + * data, and that was never predictable. + */ + if (png_ptr->write_row_fn != NULL) + (*(png_ptr->write_row_fn))(png_ptr, row_number, pass); + } /* png_ptr != NULL */ } #ifdef PNG_WRITE_FLUSH_SUPPORTED @@ -1050,15 +1149,6 @@ png_write_destroy(png_structrp png_ptr) png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list); png_free(png_ptr, png_ptr->row_buffer); png_ptr->row_buffer = NULL; -#ifdef PNG_WRITE_FILTER_SUPPORTED - png_free(png_ptr, png_ptr->alt_buffer); - png_ptr->alt_buffer = NULL; - png_free(png_ptr, png_ptr->write_row[0]); - png_ptr->write_row[0] = NULL; - png_free(png_ptr, png_ptr->write_row[1]); - png_ptr->write_row[1] = NULL; -#endif - #ifdef PNG_TRANSFORM_MECH_SUPPORTED png_transform_free(png_ptr, &png_ptr->transform_list); #endif @@ -1101,249 +1191,6 @@ png_destroy_write_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr) } } -#ifdef PNG_WRITE_FILTER_SUPPORTED -/* Allow the application to select one or more row filters to use. */ -void PNGAPI -png_set_filter(png_structrp png_ptr, int method, int filters) -{ - png_debug(1, "in png_set_filter"); - - if (png_ptr == NULL) - return; - - if (png_ptr->read_struct) - { - png_app_error(png_ptr, "png_set_filter: cannot be used when reading"); - return; - } - - if (method != png_ptr->filter_method) - { - png_app_error(png_ptr, "png_set_filter: method does not match IHDR"); - return; - } - - /* PNG and MNG use the same base adaptive filter types: */ - if (method != PNG_FILTER_TYPE_BASE && method != PNG_INTRAPIXEL_DIFFERENCING) - { - png_app_error(png_ptr, "png_set_filter: unsupported method"); - return; - } - - /* Notice that PNG_NO_FILTERS is 0 and passes this test; this is OK - * because filters then gets set to PNG_FILTER_NONE, as is required. - */ - if (filters < PNG_FILTER_VALUE_LAST) - filters = 0x08 << filters; - - else if ((filters & PNG_BIC_MASK(PNG_ALL_FILTERS)) != 0) - { - png_app_error(png_ptr, "png_set_filter: invalid filters mask/value"); - - /* Prior to 1.7.0 this ignored the error and just used the bits that are - * present, now it does nothing; this seems a lot safer. - */ - return; - } - - /* Finally store the value. */ - png_ptr->next_filter = PNG_BYTE(filters); -} -#endif /* WRITE_FILTER */ - -#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* GRR 970116 */ -/* Legacy API that weighted the filter metric by the number of times it had been - * used before. - */ -#ifdef PNG_FLOATING_POINT_SUPPORTED -PNG_FUNCTION(void,PNGAPI -png_set_filter_heuristics,(png_structrp png_ptr, int heuristic_method, - int num_weights, png_const_doublep filter_weights, - png_const_doublep filter_costs),PNG_DEPRECATED) -{ - png_app_warning(png_ptr, "weighted filter heuristics not implemented"); - PNG_UNUSED(heuristic_method) - PNG_UNUSED(num_weights) - PNG_UNUSED(filter_weights) - PNG_UNUSED(filter_costs) -} -#endif /* FLOATING_POINT */ - -#ifdef PNG_FIXED_POINT_SUPPORTED -PNG_FUNCTION(void,PNGAPI -png_set_filter_heuristics_fixed,(png_structrp png_ptr, int heuristic_method, - int num_weights, png_const_fixed_point_p filter_weights, - png_const_fixed_point_p filter_costs),PNG_DEPRECATED) -{ - png_app_warning(png_ptr, "weighted filter heuristics not implemented"); - PNG_UNUSED(heuristic_method) - PNG_UNUSED(num_weights) - PNG_UNUSED(filter_weights) - PNG_UNUSED(filter_costs) -} -#endif /* FIXED_POINT */ -#endif /* WRITE_WEIGHTED_FILTER */ - -#ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED -void PNGAPI -png_set_compression_level(png_structrp png_ptr, int level) -{ - png_debug(1, "in png_set_compression_level"); - - if (png_ptr == NULL) - return; - - png_ptr->zlib_level = level; -} - -void PNGAPI -png_set_compression_mem_level(png_structrp png_ptr, int mem_level) -{ - png_debug(1, "in png_set_compression_mem_level"); - - if (png_ptr == NULL) - return; - - png_ptr->zlib_mem_level = mem_level; -} - -void PNGAPI -png_set_compression_strategy(png_structrp png_ptr, int strategy) -{ - png_debug(1, "in png_set_compression_strategy"); - - if (png_ptr == NULL) - return; - - /* The flag setting here prevents the libpng dynamic selection of strategy. - */ - png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY; - png_ptr->zlib_strategy = strategy; -} - -/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a - * smaller value of window_bits if it can do so safely. - */ -void PNGAPI -png_set_compression_window_bits(png_structrp png_ptr, int window_bits) -{ - if (png_ptr == NULL) - return; - - /* Prior to 1.6.0 this would warn but then set the window_bits value. This - * meant that negative window bits values could be selected that would cause - * libpng to write a non-standard PNG file with raw deflate or gzip - * compressed IDAT or ancillary chunks. Such files can be read and there is - * no warning on read, so this seems like a very bad idea. - */ - if (window_bits > 15) - { - png_warning(png_ptr, "Only compression windows <= 32k supported by PNG"); - window_bits = 15; - } - - else if (window_bits < 8) - { - png_warning(png_ptr, "Only compression windows >= 256 supported by PNG"); - window_bits = 8; - } - - png_ptr->zlib_window_bits = window_bits; -} - -void PNGAPI -png_set_compression_method(png_structrp png_ptr, int method) -{ - png_debug(1, "in png_set_compression_method"); - - if (png_ptr == NULL) - return; - - /* This would produce an invalid PNG file if it worked, but it doesn't and - * deflate will fault it, so it is harmless to just warn here. - */ - if (method != 8) - png_warning(png_ptr, "Only compression method 8 is supported by PNG"); - - png_ptr->zlib_method = method; -} -#endif /* WRITE_CUSTOMIZE_COMPRESSION */ - -/* The following were added to libpng-1.5.4 */ -#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED -void PNGAPI -png_set_text_compression_level(png_structrp png_ptr, int level) -{ - png_debug(1, "in png_set_text_compression_level"); - - if (png_ptr == NULL) - return; - - png_ptr->zlib_text_level = level; -} - -void PNGAPI -png_set_text_compression_mem_level(png_structrp png_ptr, int mem_level) -{ - png_debug(1, "in png_set_text_compression_mem_level"); - - if (png_ptr == NULL) - return; - - png_ptr->zlib_text_mem_level = mem_level; -} - -void PNGAPI -png_set_text_compression_strategy(png_structrp png_ptr, int strategy) -{ - png_debug(1, "in png_set_text_compression_strategy"); - - if (png_ptr == NULL) - return; - - png_ptr->zlib_text_strategy = strategy; -} - -/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a - * smaller value of window_bits if it can do so safely. - */ -void PNGAPI -png_set_text_compression_window_bits(png_structrp png_ptr, int window_bits) -{ - if (png_ptr == NULL) - return; - - if (window_bits > 15) - { - png_warning(png_ptr, "Only compression windows <= 32k supported by PNG"); - window_bits = 15; - } - - else if (window_bits < 8) - { - png_warning(png_ptr, "Only compression windows >= 256 supported by PNG"); - window_bits = 8; - } - - png_ptr->zlib_text_window_bits = window_bits; -} - -void PNGAPI -png_set_text_compression_method(png_structrp png_ptr, int method) -{ - png_debug(1, "in png_set_text_compression_method"); - - if (png_ptr == NULL) - return; - - if (method != 8) - png_warning(png_ptr, "Only compression method 8 is supported by PNG"); - - png_ptr->zlib_text_method = method; -} -#endif /* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */ -/* end of API added to libpng-1.5.4 */ - void PNGAPI png_set_write_status_fn(png_structrp png_ptr, png_write_status_ptr write_row_fn) { diff --git a/pngwtran.c b/pngwtran.c index 56ff95053..4c9db2f1a 100644 --- a/pngwtran.c +++ b/pngwtran.c @@ -14,236 +14,6 @@ #include "pngpriv.h" #define PNG_SRC_FILE PNG_SRC_FILE_pngwtran -#ifdef PNG_WRITE_INTERLACING_SUPPORTED -/* Pick out the correct pixels for the interlace pass. - * The basic idea here is to go through the row with a source - * pointer and a destination pointer (sp and dp), and copy the - * correct pixels for the pass. As the row gets compacted, - * sp will always be >= dp, so we should never overwrite anything. - * See the default: case for the easiest code to understand. - */ -static void -png_do_write_interlace_lbd(png_transformp *transform, png_transform_controlp tc) -{ - const png_const_structrp png_ptr = tc->png_ptr; - const unsigned int pass = png_ptr->pass; - const png_uint_32 row_width = tc->width; - const png_uint_32 output_width = PNG_PASS_COLS(row_width, pass); - png_uint_32 i = PNG_PASS_START_COL(pass); - - - png_debug(1, "in png_do_write_interlace"); - debug(!tc->init); - - /* The data can be used in place (tc->sp) if the width isn't changed or - * the first pixel in the output is the first in the input and there is - * only one pixel in the output; this covers the last pass (PNG pass 7, - * libpng 6) and PNG passes 1, 3 and 5 with narrow images. - */ - tc->width = output_width; - - if (row_width != output_width && (output_width != 1 || i > 0)) - { - /* For passes before the last the pixels must be picked from the input - * row (tc->sp) and placed into the output row (tc->dp). - */ - png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); - png_bytep dp = png_voidcast(png_bytep, tc->dp); - const unsigned int inc = PNG_PASS_COL_OFFSET(pass); - unsigned int B = (*transform)->args & 0x3; /* 0, 1 or 2 */ - - /* The row data will be moved, so do this now before 'dp' is advanced: - */ - tc->sp = dp; - - /* For pixels less than one byte wide the correct pixels have to be - * extracted from the input bytes. Because we are reading data in - * the application memory format we cannot rely on the PNG big - * endian order. Notice that this was apparently broken before - * 1.7.0. - * - * In libpng 1.7.0 libpng uses a classic bit-pump to optimize the - * extraction. In all passes before the last (6/7) no two pixels - * are adjacent in the input, so we are always extracting 1 bit. - * At present the code uses an 8-bit buffer to avoid coding for - * different byte sexes, but this could easily be changed. - * - * 'i' is the bit-index of bit in the input (sp[]), so, - * considering the 1-bit per pixel case, sp[i>>3] is the byte - * and the bit is bit (i&7) (0 lowest) on swapped (little endian) - * data or 7-(i&7) on PNG default (big-endian) data. - * - * Define these macros, where: - * - * B: the log2 bit depth (0, 1, 2 for 1bpp, 2bpp or 4bpp) of - * the data; this should be a constant. - * sp: the source pointer (sp) (a png_const_bytep) - * i: the pixel index in the input (png_uint_32) - * j: the bit index in the output (unsigned int) - * - * Unlike 'i', 'j' is interpreted directly; for LSB bytes it counts - * up, for MSB it counts down. - * - * NOTE: this could all be expanded to eliminate the code below by - * the time honoured copy'n'paste into three separate functions. This - * might be worth doing in the future. - */ -# define PIXEL_MASK ((1U << (1<>(3-(B))]) /* byte to use */ -# define SP_OFFSET_LSB ((BIT_MASK & i) << (B)) -# define SP_OFFSET_MSB ((BIT_MASK & ~i) << (B)) -# define SP_PIXEL(sex) ((SP_BYTE >> SP_OFFSET_ ## sex) & PIXEL_MASK) - { - unsigned int j; - unsigned int d; - -# ifdef PNG_WRITE_PACKSWAP_SUPPORTED - if (tc->format & PNG_FORMAT_FLAG_SWAPPED) - for (j = 0, d = 0; i < row_width; i += inc) - { /* little-endian */ - d |= SP_PIXEL(LSB) << j; - j += 1<fn = NULL; /* remove me to caller */ -} - -static void -png_do_write_interlace_byte(png_transformp *transform, - png_transform_controlp tc) -{ - const png_const_structrp png_ptr = tc->png_ptr; - const unsigned int pass = png_ptr->pass; - const png_uint_32 row_width = tc->width; - const png_uint_32 output_width = PNG_PASS_COLS(row_width, pass); - png_uint_32 i = PNG_PASS_START_COL(pass); - - png_debug(1, "in png_do_write_interlace"); - debug(!tc->init); - - /* The data can be used in place (tc->sp) if the width isn't changed or - * the first pixel in the output is the first in the input and there is - * only one pixel in the output; this covers the last pass (PNG pass 7, - * libpng 6) and PNG passes 1, 3 and 5 with narrow images. - */ - tc->width = output_width; - - if (row_width != output_width && (output_width != 1 || i > 0)) - { - /* For passes before the last the pixels must be picked from the input - * row (tc->sp) and placed into the output row (tc->dp). - */ - png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp); - png_bytep dp = png_voidcast(png_bytep, tc->dp); - const unsigned int inc = PNG_PASS_COL_OFFSET(pass); - unsigned int cbytes = (*transform)->args; - - /* The row data will be moved, so do this now before 'dp' is advanced: - */ - tc->sp = dp; - - /* Loop through the input copying each pixel to the correct place - * in the output. Note that the loop may be executed 0 times if - * this is called on a narrow image that does not contain this - * pass. - */ - for (sp += i * cbytes; i < row_width; - i += inc, sp += inc * cbytes, dp += cbytes) - if (dp != sp) /* cannot happen in practice */ - memcpy(dp, sp, cbytes); - } - - /* The transform is removed on the last pass. It can be removed earlier - * in other cases if the row width (the image width) is only 1, however - * this does not seem worth the overhead to check; PNG pass 5(4) happens - * if there are just three rows. - */ - else /* the source can be used in place */ if (pass == 6) - (*transform)->fn = NULL; /* remove me to caller */ -} - -static void -png_init_write_interlace(png_transformp *transform, png_transform_controlp tc) -{ -# define png_ptr (tc->png_ptr) - - png_debug(1, "in png_do_write_interlace"); - debug(tc->init); - - /* Do nothing on PNG_TC_INIT_FORMAT because we don't change the format, bit - * depth or gamma of the data. - */ - if (tc->init == PNG_TC_INIT_FINAL) - { - png_transformp tf = *transform; - unsigned int pixel_depth = PNG_TC_PIXEL_DEPTH(*tc); - png_uint_16 B = 0; - - switch (pixel_depth) - { - case 4: /* B == 2 */ - ++B; - /* FALL THROUGH */ - case 2: /* B == 1 */ - ++B; - /* FALL THROUGH */ - case 1: /* B == 0 */ - /* This is the low bit depth case: */ - tf->args = B; - tf->fn = png_do_write_interlace_lbd; - break; - - default: - affirm((pixel_depth & 7) == 0); - pixel_depth >>= 3; - affirm(pixel_depth > 0 && pixel_depth <= 8); - tf->args = pixel_depth & 0xf; - tf->fn = png_do_write_interlace_byte; - break; - } - } -# undef png_ptr -} - -void /* PRIVATE */ -png_set_write_interlace(png_structrp png_ptr) -{ - /* This is checked in the caller: */ - debug(png_ptr->interlaced == PNG_INTERLACE_ADAM7); - png_add_transform(png_ptr, 0, png_init_write_interlace, PNG_TR_INTERLACE); -} -#endif /* WRITE_INTERLACING */ - #ifdef PNG_WRITE_PACK_SUPPORTED /* Pack pixels into bytes. */ static void diff --git a/pngwutil.c b/pngwutil.c index f9bfe28ba..c16826e84 100644 --- a/pngwutil.c +++ b/pngwutil.c @@ -339,13 +339,17 @@ png_deflate_claim(png_structrp png_ptr, png_uint_32 owner, #ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED if ((png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_STRATEGY) != 0) strategy = png_ptr->zlib_strategy; - - else if (png_ptr->next_filter != PNG_FILTER_NONE) - strategy = PNG_Z_DEFAULT_STRATEGY; - else -#endif - strategy = PNG_Z_DEFAULT_NOFILTER_STRATEGY; +#endif /* WRITE_CUSTOMIZE_COMPRESSION */ + +#ifdef PNG_WRITE_FILTER_SUPPORTED + if (png_ptr->filter_mask != PNG_FILTER_NONE) + strategy = PNG_Z_DEFAULT_STRATEGY; + else +#endif /* WRITE_FILTER */ + + /* The default with no filters: */ + strategy = PNG_Z_DEFAULT_NOFILTER_STRATEGY; } else @@ -889,14 +893,14 @@ png_write_IHDR(png_structrp png_ptr, png_uint_32 width, png_uint_32 height, # ifdef PNG_WRITE_FILTER_SUPPORTED /* TODO: review this setting */ - if (png_ptr->next_filter == PNG_NO_FILTERS /* not yet set */) + if (png_ptr->filter_mask == PNG_NO_FILTERS /* not yet set */) { if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE || png_ptr->bit_depth < 8) - png_ptr->next_filter = PNG_FILTER_NONE; + png_ptr->filter_mask = PNG_FILTER_NONE; else - png_ptr->next_filter = PNG_ALL_FILTERS; + png_ptr->filter_mask = PNG_ALL_FILTERS; } # endif @@ -1952,409 +1956,726 @@ png_write_tIME(png_structrp png_ptr, png_const_timep mod_time) } #endif +static void +write_filtered_row(png_structrp png_ptr, png_const_bytep filtered_row, + png_uint_32 row_bytes, png_byte filter /*if at start of row*/, + int end_of_image) +{ + /* This handles writing a row that has been filtered, or did not need to be + * filtered. If the data row has a partial pixel it must have been handled + * correctly in the caller; filters generate a full 8 bits even if the pixel + * only has one significant bit! + */ + debug(row_bytes > 0); + + if (filter < PNG_FILTER_VALUE_LAST) /* start of row */ + { + png_byte buffer[1]; + + buffer[0] = filter; + png_compress_IDAT(png_ptr, buffer, 1U/*len*/, Z_NO_FLUSH); + } + + png_compress_IDAT(png_ptr, filtered_row, row_bytes, + end_of_image ? Z_FINISH : Z_NO_FLUSH); +} + +static void +write_unfiltered_rowbits(png_structrp png_ptr, png_const_bytep filtered_row, + png_uint_32 row_bits, png_byte filter /*if at start of row*/, + int end_of_image) +{ + /* Same as above, but it correctly clears the unused bits in a partial + * byte. + */ + const png_uint_32 row_bytes = row_bits >> 3; + + debug(filter == PNG_FILTER_VALUE_NONE || filter == PNG_FILTER_VALUE_LAST); + + if (row_bytes > 0U) + { + row_bits -= row_bytes << 3; + write_filtered_row(png_ptr, filtered_row, row_bytes, filter, + end_of_image && row_bits == 0U); + filter = PNG_FILTER_VALUE_LAST; /* written */ + } + + /* Handle a partial byte. */ + if (row_bits > 0U) + { + png_byte buffer[1]; + + buffer[0] = PNG_BYTE(filtered_row[row_bytes] & ~(0xFFU >> row_bits)); + write_filtered_row(png_ptr, buffer, 1U, filter, end_of_image); + } +} + +#ifdef PNG_WRITE_FILTER_SUPPORTED +static void +filter_block_singlebyte(png_alloc_size_t row_bytes, png_bytep sub_row, + png_bytep up_row, png_bytep avg_row, png_bytep paeth_row, + png_const_bytep row, png_const_bytep prev_row, png_bytep prev_pixels) +{ + /* Calculate rows for all four filters where the input has one byte per pixel + * (more accurately per filter-unit). + */ + png_byte a = prev_pixels[0]; + png_byte c = prev_pixels[1]; + + while (row_bytes-- > 0U) + { + const png_byte x = *row++; + const png_byte b = prev_row == NULL ? 0U : *prev_row++; + + /* Calculate each filtered byte in turn: */ + if (sub_row != NULL) *sub_row++ = 0xFFU & (x - a); + if (up_row != NULL) *up_row++ = 0xFFU & (x - b); + if (avg_row != NULL) *avg_row++ = 0xFFU & (x - (a+b)/2U); + + /* Paeth is a little more difficult: */ + if (paeth_row != NULL) + { + int pa = b-c; /* a+b-c - a */ + int pb = a-c; /* a+b-c - b */ + int pc = pa+pb; /* a+b-c - c = b-c + a-c */ + png_byte p = a; + + pa = abs(pa); + pb = abs(pb); + if (pa > pb) pa = pb, p = b; + if (pa > abs(pc)) p = c; + + *paeth_row++ = 0xFFU & (x - p); + } + + /* And set a and c for the next pixel: */ + a = x; + c = b; + } + + /* Store a and c for the next block: */ + prev_pixels[0] = a; + prev_pixels[1] = c; +} + +static void +filter_block_multibyte(png_alloc_size_t row_bytes, + const unsigned int bpp, png_bytep sub_row, png_bytep up_row, + png_bytep avg_row, png_bytep paeth_row, png_const_bytep row, + png_const_bytep prev_row, png_bytep prev_pixels) +{ + /* Calculate rows for all four filters, the input is a block of bytes such + * that row_bytes is a multiple of bpp. bpp can be 2, 3, 4, 6 or 8. + * prev_pixels will be updated to the last pixels processed. + */ + while (row_bytes >= bpp) + { + unsigned int i; + + for (i=0; i pb) pa = pb, p = b; + if (pa > abs(pc)) p = c; + + *paeth_row++ = 0xFFU & (x - p); + } + } + + row_bytes -= i; + } +} + +static void +filter_row(png_structrp png_ptr, png_const_bytep prev_row, + png_bytep prev_pixels, png_const_bytep unfiltered_row, + const png_uint_32 row_bits, unsigned int bpp, unsigned int filters_to_try, + int start_of_row, int end_of_image) +{ + /* filters_to_try identifies a single filter and it is not PNG_FILTER_NONE. + */ + png_uint_32 row_bytes = row_bits >> 3; /* complete bytes */ + png_byte filter = PNG_FILTER_VALUE_LAST /* not at start */; + png_byte filtered_row[PNG_ROW_BUFFER_SIZE]; + + debug((row_bits % bpp) == 0U); + + if (start_of_row) switch (filters_to_try) + { + case PNG_FILTER_SUB: filter = PNG_FILTER_VALUE_SUB; break; + case PNG_FILTER_UP: filter = PNG_FILTER_VALUE_UP; break; + case PNG_FILTER_AVG: filter = PNG_FILTER_VALUE_AVG; break; + case PNG_FILTER_PAETH: filter = PNG_FILTER_VALUE_PAETH; break; + default: + impossible("filter list"); + } + + if (bpp <= 8U) + { + /* There may be a partial byte at the end. */ + if (row_bytes > 0) + filter_block_singlebyte(row_bytes, + filters_to_try & PNG_FILTER_SUB ? filtered_row : NULL, + filters_to_try & PNG_FILTER_UP ? filtered_row : NULL, + filters_to_try & PNG_FILTER_AVG ? filtered_row : NULL, + filters_to_try & PNG_FILTER_PAETH ? filtered_row : NULL, + unfiltered_row, prev_row, prev_pixels); + + /* The partial byte must be handled correctly here; both the previous row + * value and the current value need to have non-present bits cleared. + */ + if ((row_bits & 7U) != 0) + { + const png_byte mask = PNG_BYTE(~(0xFFU >> (row_bits & 7U))); + png_byte buffer[2]; + + buffer[0] = unfiltered_row[row_bytes] & mask; + + if (prev_row != NULL) + buffer[1U] = prev_row[row_bytes] & mask; + + else + buffer[1U] = 0U; + + filter_block_singlebyte(1U, + filters_to_try & PNG_FILTER_SUB ? filtered_row+row_bytes : NULL, + filters_to_try & PNG_FILTER_UP ? filtered_row+row_bytes : NULL, + filters_to_try & PNG_FILTER_AVG ? filtered_row+row_bytes : NULL, + filters_to_try & PNG_FILTER_PAETH ? filtered_row+row_bytes : NULL, + buffer, buffer+1U, prev_pixels); + + ++row_bytes; /* for write_filtered_row below */ + } + } + + else + { + debug((bpp & 7U) == 0U && row_bits == (row_bytes << 3)); + filter_block_multibyte(row_bytes, bpp >> 3, + filters_to_try & PNG_FILTER_SUB ? filtered_row : NULL, + filters_to_try & PNG_FILTER_UP ? filtered_row : NULL, + filters_to_try & PNG_FILTER_AVG ? filtered_row : NULL, + filters_to_try & PNG_FILTER_PAETH ? filtered_row : NULL, + unfiltered_row, prev_row, prev_pixels); + } + + write_filtered_row(png_ptr, filtered_row, row_bytes, filter, end_of_image); +} + +static void +find_filter(png_structrp png_ptr, png_const_bytep prev_row, + png_bytep prev_pixels, png_const_bytep unfiltered_row, + png_uint_32 row_bits, unsigned int bpp, unsigned int filters_to_try, + int start_of_row, int end_of_image) +{ + /* filters_to_try identifies multiple filters, up to all five. */ + /* TODO: reimplement this, currently this just selects the first filter */ + filters_to_try &= -filters_to_try; + if (filters_to_try == PNG_FILTER_NONE) + write_unfiltered_rowbits(png_ptr, unfiltered_row, row_bits, + start_of_row ? PNG_FILTER_VALUE_NONE : PNG_FILTER_VALUE_LAST, + end_of_image); + + else + filter_row(png_ptr, prev_row, prev_pixels, unfiltered_row, row_bits, bpp, + filters_to_try & -filters_to_try, start_of_row, end_of_image); +} + /* 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 * chosen filter. */ -#ifdef PNG_WRITE_FILTER_SUPPORTED -static png_size_t /* PRIVATE */ -png_setup_sub_row(const int bpp/*BYTES per pixel*/, - const png_alloc_size_t row_bytes, const png_size_t lmins, png_const_bytep rp, - png_bytep dp) +unsigned int /* PRIVATE */ +png_write_filter_row(png_structrp png_ptr, png_bytep prev_pixels, + png_const_bytep unfiltered_row, png_uint_32 x, + png_uint_32 width/*pixels*/, int first_row_in_pass, int last_pass_row, + unsigned int filters_to_try, int end_of_image) { - png_size_t sum = 0; + png_bytep prev_row = png_ptr->row_buffer; + const unsigned int bpp = png_ptr->row_output_pixel_depth; + const png_uint_32 row_bits = width * bpp; - /* Advance one pixel, or one byte, whichever is greater: */ + /* These invariants are expected from the caller: */ + affirm(width < 65536U && bpp <= 64U && row_bits <= 8U*PNG_ROW_BUFFER_SIZE); + + if (x == 0U) /* start of row */ { - int i; + /* Delaying initialization of the filter stuff. */ + if (png_ptr->filter_mask == 0U) + png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, PNG_ALL_FILTERS); - for (i = 0; i < bpp; i++, rp++, dp++) + /* Now work out the filters to try for this row: */ + filters_to_try = png_ptr->filter_mask; /* else caller must preserve */ + + /* If this has a previous row filter in the set to try ensure the row + * buffer exists and ensure it is empty when first allocated and at + * the start of the pass. + */ + if ((filters_to_try & (PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH)) + != 0U) { - int v = *dp = *rp; - sum += (v < 128) ? v : 256 - v; - } - } - - /* Do the 'sub' filter on the corresponding preceding byte: */ - { - png_alloc_size_t i; - - for (i = bpp; i < row_bytes; i++, rp++, dp++) - { - int v = *dp = PNG_BYTE(rp[0] - rp[-bpp]); - - if (lmins) + if (prev_row == NULL) { - sum += (v < 128) ? v : 256 - v; + /* Just allocate for the total output row bytes; a three-row + * interlaced image requires less, but this is safe. + */ + prev_row = png_voidcast(png_bytep, png_malloc(png_ptr, + png_calc_rowbytes(png_ptr, bpp, png_ptr->width))); + png_ptr->row_buffer = prev_row; - if (sum > lmins) /* We are already worse, don't continue. */ - break; + /* If that buffer would have been required for this row issue an + * app warning and disable the filters that would have required + * the data. + */ + if (!first_row_in_pass) + { + png_app_warning(png_ptr, "Previous row filters ignored"); + /* And always turn off the filters, to prevent using + * uninitialized data. + */ + filters_to_try &= PNG_BIC_MASK( + PNG_FILTER_UP|PNG_FILTER_AVG|PNG_FILTER_PAETH); + + if (filters_to_try == 0U) + filters_to_try = PNG_FILTER_NONE; + } } } - } - return sum; -} - -static png_size_t /* PRIVATE */ -png_setup_up_row(const png_alloc_size_t row_bytes, const png_size_t lmins, - png_const_bytep rp, png_const_bytep pp, png_bytep dp) -{ - png_alloc_size_t i; - png_size_t sum = 0; - - for (i = 0; i < row_bytes; i++, rp++, pp++, dp++) - { - int v = *dp = PNG_BYTE(*rp - *pp); - - if (lmins) + /* The filters are pre-calculated in png_set_filter, however if the + * image is interlaced some passes may still be too narrow or short to + * allow certain filters. In any case the first row of the pass + * doesn't need to consider PAETH or UP (AVG is still different). + */ + if (first_row_in_pass) { - sum += (v < 128) ? v : 256 - v; - - if (sum > lmins) /* We are already worse, don't continue. */ - break; - } - } - - return sum; -} - -static png_size_t /* PRIVATE */ -png_setup_avg_row(const int bpp, const png_alloc_size_t row_bytes, - const png_size_t lmins, png_const_bytep rp, png_const_bytep pp, png_bytep dp) -{ - png_size_t sum = 0; - - { - int i; - - for (i = 0; i < bpp; i++, rp++, pp++, dp++) - { - unsigned int v = *dp = PNG_BYTE(*rp - *pp / 2); - sum += (v < 128) ? v : 256 - v; - } - } - - { - png_alloc_size_t i; - - for (i = bpp; i < row_bytes; i++, rp++, pp++, dp++) - { - unsigned int v = *dp = PNG_BYTE(rp[0] - (*pp + rp[-bpp]) / 2); - - if (lmins) + if ((filters_to_try & PNG_FILTER_UP) != 0U) { - sum += (v < 128) ? v : 256 - v; - - if (sum > lmins) /* We are already worse, don't continue. */ - break; + filters_to_try &= PNG_BIC_MASK(PNG_FILTER_UP); + filters_to_try |= PNG_FILTER_NONE; } - } - } - return sum; -} + if ((filters_to_try & PNG_FILTER_PAETH) != 0U) + { + filters_to_try &= PNG_BIC_MASK(PNG_FILTER_PAETH); + filters_to_try |= PNG_FILTER_SUB/*equialent to PAETH here*/; + } -static png_size_t /* PRIVATE */ -png_setup_paeth_row(const int bpp, const png_alloc_size_t row_bytes, - const png_size_t lmins, png_const_bytep rp, png_const_bytep pp, png_bytep dp) -{ - png_size_t sum = 0; - - { - int i; - - for (i = 0; i < bpp; i++, dp++, rp++, pp++) - { - int v = *dp = PNG_BYTE(*rp - *pp); /* UP for the first pixel */ - sum += (v < 128) ? v : 256 - v; - } - } - - { - png_alloc_size_t i; - - for (i = bpp; i < row_bytes; i++, pp++, rp++, dp++) - { - int a, b, c, pa, pb, pc, p, v; - - b = pp[0]; - c = pp[-bpp]; - a = rp[-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; +#if 0 + /* If this leaves the AVG filter it will be used on the first row + * this is handled in the filter implementation by setting prev_row + * to NULL below. + */ +#else /* DOESN'T WORK (problems in the read code?) */ + if ((filters_to_try & PNG_FILTER_AVG) != 0U) + { + filters_to_try &= PNG_BIC_MASK(PNG_FILTER_AVG); + filters_to_try |= PNG_FILTER_NONE; + } #endif + } - p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c; - v = *dp = PNG_BYTE(*rp - p); - - if (lmins) + /* Check for a narrow image; the blocking will never return just one + * pixel at the start unless the pass is only one pixel wide, this test + * needs to happen after the one above on PAETH: + */ + if (width == 1U) + { + if ((filters_to_try & PNG_FILTER_SUB) != 0U) { - sum += (v < 128) ? v : 256 - v; - - if (sum > lmins) /* We are already worse, don't continue. */ - break; + filters_to_try &= PNG_BIC_MASK(PNG_FILTER_SUB); + filters_to_try |= PNG_FILTER_NONE; } } + } /* start of row */ + + else if (prev_row != NULL) + { + /* Advance prev_row to the corresponding pixel above row[x], must use + * png_calc_rowbytes here otherwise the calculation using x might + * overflow. + */ + debug(((x * bpp) & 7U) == 0U); + prev_row += png_calc_rowbytes(png_ptr, bpp, x); } - return sum; + /* Now choose the correct filter implementation according to the number of + * filters in the filters_to_try list. The prev_row parameter is made NULL + * on the first row because it is uninitialized at that point. + */ + if (filters_to_try == PNG_FILTER_NONE) + write_unfiltered_rowbits(png_ptr, unfiltered_row, row_bits, + x == 0 ? PNG_FILTER_VALUE_NONE : PNG_FILTER_VALUE_LAST, + end_of_image); + + else if ((filters_to_try & -filters_to_try) == filters_to_try) /* 1 filter */ + filter_row(png_ptr, first_row_in_pass ? NULL : prev_row, + prev_pixels, unfiltered_row, row_bits, bpp, filters_to_try, x == 0, + end_of_image); + + else + find_filter(png_ptr, first_row_in_pass ? NULL : prev_row, + prev_pixels, unfiltered_row, row_bits, bpp, filters_to_try, x == 0, + end_of_image); + + /* 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 + * prev_row may have garbage in a partial byte at the end. + */ + if (prev_row != NULL && !last_pass_row) + memcpy(prev_row, unfiltered_row, (row_bits + 7U) >> 3); + + return filters_to_try; } -static png_bytep -test_buffer(png_structrp png_ptr, png_const_bytep in_use) - /* Return an output row sized buffer from the two available, but never the - * one pointed to by 'in_use'. Dynamically allocates buffers as required. - * The buffers are always big enough for a full output row because they are - * retained until the whole image has been written. - */ +/* Allow the application to select one or more row filters to use. */ +void PNGAPI +png_set_filter(png_structrp png_ptr, int method, int filtersIn) { - int n = 0; - png_bytep p = png_ptr->write_row[n]; + unsigned int filters; - debug(in_use != NULL); + png_debug(1, "in png_set_filter"); - if (p == in_use) - p = png_ptr->write_row[++n]; + if (png_ptr == NULL) + return; - if (p == NULL) - png_ptr->write_row[n] = p = png_voidcast(png_bytep, png_malloc(png_ptr, - PNG_ROWBYTES(PNG_PIXEL_DEPTH(*png_ptr), png_ptr->width))); + if (png_ptr->read_struct) + { + png_app_error(png_ptr, "png_set_filter: cannot be used when reading"); + return; + } - return p; + if (method != png_ptr->filter_method) + { + png_app_error(png_ptr, "png_set_filter: method does not match IHDR"); + return; + } + + /* PNG and MNG use the same base adaptive filter types: */ + if (method != PNG_FILTER_TYPE_BASE && method != PNG_INTRAPIXEL_DIFFERENCING) + { + png_app_error(png_ptr, "png_set_filter: unsupported method"); + return; + } + + /* Notice that PNG_NO_FILTERS is 0 and passes this test; this is OK + * because filters then gets set to PNG_FILTER_NONE, as is required. + */ + if (filtersIn >= 0 && filtersIn < PNG_FILTER_VALUE_LAST) + filters = 8U << filtersIn; + + else if ((filtersIn & PNG_BIC_MASK(PNG_ALL_FILTERS)) == 0) + filters = filtersIn & PNG_ALL_FILTERS; + + else + { + png_app_error(png_ptr, "png_set_filter: invalid filters mask/value"); + + /* Prior to 1.7.0 this ignored the error and just used the bits that + * are present, now it does nothing; this seems a lot safer. + */ + return; + } + + /* New in 1.7.0: adjust the mask according to the image characteristics. + * This used to happen on every row, doing it here means that these checks + * happen only once every png_set_filter call, or once per image. + */ + if (filters != PNG_FILTER_NONE) + { + /* Test to see if there are enough rows to allow previous-row filters to + * work. Note that the AVG filter is still significant because it uses + * half the value of the previous pixel as the predictor, but it is + * ignored in this case. + */ + if (png_ptr->height <= (png_ptr->interlaced == PNG_INTERLACE_NONE ? 1U : + (png_ptr->width == 1U ? 3U : 2U))) + { + /* Replace 'up' by the equivalent 'none': */ + if ((filters & (PNG_FILTER_UP)) != 0) + { + filters &= PNG_BIC_MASK(PNG_FILTER_UP); + filters |= PNG_FILTER_NONE; + } + + /* Replace 'paeth' by the equivalent 'sub': */ + if ((filters & PNG_FILTER_PAETH) != 0) + { + filters &= PNG_BIC_MASK(PNG_FILTER_PAETH); + filters |= PNG_FILTER_SUB; + } + + /* Remove 'avg' unless it is the only filter in which case 'none' is + * used. (This chooses compression speed of very short images over a + * probably pointless compression option for a one line image; short + * images are common, the sub-case which benefits from AVG is not. + */ + if ((filters & PNG_FILTER_AVG) != 0) + { + filters &= PNG_BIC_MASK(PNG_FILTER_AVG); + if (filters == 0U) + filters |= PNG_FILTER_NONE; + } + } + + /* Also check for SUB on narrow images; it's equivalent to NONE on the + * first pixel. + */ + if (png_ptr->width <= (png_ptr->interlaced == PNG_INTERLACE_NONE ? 1U : + (png_ptr->height == 1U ? 3U : 1U))) + { + if ((filters & PNG_FILTER_SUB) != 0) + { + filters &= PNG_BIC_MASK(PNG_FILTER_SUB); + filters |= PNG_FILTER_NONE; + } + } + } + + debug(filters != 0U && (filters & PNG_BIC_MASK(PNG_ALL_FILTERS)) == 0U); + + png_ptr->filter_mask = png_check_bits(png_ptr, filters, 8); } - -png_const_bytep /* PRIVATE */ -png_write_filter_row(png_structrp png_ptr, png_const_bytep row_buf, - int first_pass_row, png_const_bytep prev_row, png_alloc_size_t row_bytes, - unsigned int bpp, png_bytep filter_byte) +#else /* !WRITE_FILTER */ +unsigned int /* PRIVATE */ +png_write_filter_row(png_structrp png_ptr, png_bytep prev_pixels, + png_const_bytep unfiltered_row, png_uint_32 x, + png_uint_32 width/*pixels*/, int first_row_in_pass, int last_pass_row, + unsigned int filters_to_try/*from previous call*/, int end_of_image) { - png_const_bytep best_row; - png_size_t mins; - unsigned int filter_to_do = png_ptr->next_filter; - png_byte best_filter; + const unsigned int bpp = png_ptr->row_output_pixel_depth; + png_uint_32 row_bits; - png_debug(1, "in png_write_find_filter"); + row_bits = width; + row_bits *= bpp; + /* These invariants are expected from the caller: */ + affirm(width < 65536U && bpp <= 64U && row_bits <= 8U*PNG_ROW_BUFFER_SIZE); - /* API CHANGE: 1.7.0: previously it was possible to select AVG, PAETH and UP - * on the first row, but this complicates the code. In practice with the - * default settings only AVG was tried because 'UP' is the same as 'NONE' and - * 'PAETH' is the same as 'SUB' - * - * There are two cases; the row at the start of pass and the case where - * the application set the filters so that libpng did not save the - * previous row. The first case switches the filters, the second just - * clears them from the list. - */ - if (first_pass_row) - { - if (filter_to_do & PNG_FILTER_UP) filter_to_do |= PNG_FILTER_NONE; - if (filter_to_do & PNG_FILTER_PAETH) filter_to_do |= PNG_FILTER_SUB; - /* AVG not handled */ - } + write_unfiltered_rowbits(png_ptr, unfiltered_row, row_bits, + x == 0 ? PNG_FILTER_VALUE_NONE : PNG_FILTER_VALUE_LAST, end_of_image); - if (first_pass_row || prev_row == NULL) - filter_to_do &= - PNG_BIC_MASK(PNG_FILTER_UP+PNG_FILTER_AVG+PNG_FILTER_PAETH); + return filters_to_try; - /* A second optimization is possible for narrow images. If an interlaced - * image is 5-12 pixels wide pass 2 will have only one column. 1bpp - * grayscale images 8 pixels or less wide only have one byte per row (and the - * filter acts on the bytes for low bit depth images.) 1bpp grayscale - * interlaced images will only have 1 byte in early passes (1 and 2) when the - * image is 64 or fewer pixels wide. In these cases the 'SUB' filter reduces - * to 'NONE' - */ - if (row_bytes <= bpp && (filter_to_do & PNG_FILTER_SUB) != 0) - { - filter_to_do |= PNG_FILTER_NONE; - filter_to_do &= PNG_BIC_MASK(PNG_FILTER_SUB); - } - - 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 - * as the "minimum sum of absolute differences" heuristic. Other - * heuristics are the "weighted minimum sum of absolute differences" - * (experimental and can in theory improve compression), and the "zlib - * predictive" method (not implemented yet), which does test compressions - * of lines using different filter methods, and then chooses the - * (series of) filter(s) that give minimum compressed data size (VERY - * computationally expensive). - * - * GRR 980525: consider also - * - * (1) minimum sum of absolute differences from running average (i.e., - * keep running sum of non-absolute differences & count of bytes) - * [track dispersion, too? restart average if dispersion too large?] - * - * (1b) minimum sum of absolute differences from sliding average, probably - * with window size <= deflate window (usually 32K) - * - * (2) minimum sum of squared differences from zero or running average - * (i.e., ~ root-mean-square approach) - */ - - /* 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. - */ - if (filter_to_do <= PNG_FILTER_NONE) /* no filters to chose from */ - { - *filter_byte = PNG_FILTER_VALUE_NONE; - return row_buf; - } - - best_row = row_buf; - best_filter = PNG_FILTER_VALUE_NONE; - - /* TODO: make this into a loop to avoid all the code replication */ - if ((filter_to_do & PNG_FILTER_NONE) != 0) - { - png_const_bytep rp; - png_size_t sum = 0; - png_size_t i; - int v; - - if (PNG_SIZE_MAX/128 <= row_bytes) - { - for (i = 0, rp = row_buf; i < row_bytes; i++, rp++) - { - /* Check for overflow */ - if (sum > PNG_SIZE_MAX/128 - 256) - break; - - v = *rp; - sum += (v < 128) ? v : 256 - v; - } - } - else /* Overflow is not possible */ - { - for (i = 0, rp = row_buf; i < row_bytes; i++, rp++) - { - v = *rp; - sum += (v < 128) ? v : 256 - v; - } - } - - mins = sum; - } - - /* Sub filter */ - if (filter_to_do == PNG_FILTER_SUB) - /* It's the only filter so no testing is needed */ - { - png_bytep tst_row = test_buffer(png_ptr, best_row); - (void)png_setup_sub_row(bpp, row_bytes, 0, row_buf, tst_row); - best_row = tst_row; - best_filter = PNG_FILTER_VALUE_SUB; - } - - else if ((filter_to_do & PNG_FILTER_SUB) != 0) - { - png_size_t sum; - png_bytep tst_row = test_buffer(png_ptr, best_row); - - sum = png_setup_sub_row(bpp, row_bytes, mins, row_buf, tst_row); - - if (sum < mins) - { - mins = sum; - best_row = tst_row; - best_filter = PNG_FILTER_VALUE_SUB; - } - } - - /* Up filter */ - if (filter_to_do == PNG_FILTER_UP) - { - png_bytep tst_row = test_buffer(png_ptr, best_row); - (void)png_setup_up_row(row_bytes, 0, row_buf, prev_row, tst_row); - best_row = tst_row; - best_filter = PNG_FILTER_VALUE_UP; - } - - else if ((filter_to_do & PNG_FILTER_UP) != 0) - { - png_size_t sum; - png_bytep tst_row = test_buffer(png_ptr, best_row); - - sum = png_setup_up_row(row_bytes, mins, row_buf, prev_row, tst_row); - - if (sum < mins) - { - mins = sum; - best_row = tst_row; - best_filter = PNG_FILTER_VALUE_UP; - } - } - - /* Avg filter */ - if (filter_to_do == PNG_FILTER_AVG) - { - png_bytep tst_row = test_buffer(png_ptr, best_row); - (void)png_setup_avg_row(bpp, row_bytes, 0, row_buf, prev_row, tst_row); - best_row = tst_row; - best_filter = PNG_FILTER_VALUE_AVG; - } - - else if ((filter_to_do & PNG_FILTER_AVG) != 0) - { - png_size_t sum; - png_bytep tst_row = test_buffer(png_ptr, best_row); - - sum = png_setup_avg_row(bpp, row_bytes, mins, row_buf, prev_row, - tst_row); - - if (sum < mins) - { - mins = sum; - best_row = tst_row; - best_filter = PNG_FILTER_VALUE_AVG; - } - } - - /* Paeth filter */ - if (filter_to_do == PNG_FILTER_PAETH) - { - png_bytep tst_row = test_buffer(png_ptr, best_row); - (void)png_setup_paeth_row(bpp, row_bytes, 0, row_buf, prev_row, tst_row); - best_row = tst_row; - best_filter = PNG_FILTER_VALUE_PAETH; - } - - else if ((filter_to_do & PNG_FILTER_PAETH) != 0) - { - png_size_t sum; - png_bytep tst_row = test_buffer(png_ptr, best_row); - - sum = png_setup_paeth_row(bpp, row_bytes, mins, row_buf, prev_row, - tst_row); - - if (sum < mins) - { - mins = sum; - best_row = tst_row; - best_filter = PNG_FILTER_VALUE_PAETH; - } - } - - /* Do the actual writing of the filtered row data from the chosen filter. */ - affirm(best_filter < PNG_FILTER_VALUE_LAST); - *filter_byte = best_filter; - return best_row; + PNG_UNUSED(first_row_in_pass); + PNG_UNUSED(prev_pixels); + PNG_UNUSED(last_pass_row); } -#endif /* WRITE_FILTER */ +#endif /* !WRITE_FILTER */ + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* GRR 970116 */ +/* Legacy API that weighted the filter metric by the number of times it had been + * used before. + */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_FUNCTION(void,PNGAPI +png_set_filter_heuristics,(png_structrp png_ptr, int heuristic_method, + int num_weights, png_const_doublep filter_weights, + png_const_doublep filter_costs),PNG_DEPRECATED) +{ + png_app_warning(png_ptr, "weighted filter heuristics not implemented"); + PNG_UNUSED(heuristic_method) + PNG_UNUSED(num_weights) + PNG_UNUSED(filter_weights) + PNG_UNUSED(filter_costs) +} +#endif /* FLOATING_POINT */ + +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_FUNCTION(void,PNGAPI +png_set_filter_heuristics_fixed,(png_structrp png_ptr, int heuristic_method, + int num_weights, png_const_fixed_point_p filter_weights, + png_const_fixed_point_p filter_costs),PNG_DEPRECATED) +{ + png_app_warning(png_ptr, "weighted filter heuristics not implemented"); + PNG_UNUSED(heuristic_method) + PNG_UNUSED(num_weights) + PNG_UNUSED(filter_weights) + PNG_UNUSED(filter_costs) +} +#endif /* FIXED_POINT */ +#endif /* WRITE_WEIGHTED_FILTER */ + +#ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED +void PNGAPI +png_set_compression_level(png_structrp png_ptr, int level) +{ + png_debug(1, "in png_set_compression_level"); + + if (png_ptr == NULL) + return; + + png_ptr->zlib_level = level; +} + +void PNGAPI +png_set_compression_mem_level(png_structrp png_ptr, int mem_level) +{ + png_debug(1, "in png_set_compression_mem_level"); + + if (png_ptr == NULL) + return; + + png_ptr->zlib_mem_level = mem_level; +} + +void PNGAPI +png_set_compression_strategy(png_structrp png_ptr, int strategy) +{ + png_debug(1, "in png_set_compression_strategy"); + + if (png_ptr == NULL) + return; + + /* The flag setting here prevents the libpng dynamic selection of strategy. + */ + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY; + png_ptr->zlib_strategy = strategy; +} + +/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a + * smaller value of window_bits if it can do so safely. + */ +void PNGAPI +png_set_compression_window_bits(png_structrp png_ptr, int window_bits) +{ + if (png_ptr == NULL) + return; + + /* Prior to 1.6.0 this would warn but then set the window_bits value. This + * meant that negative window bits values could be selected that would cause + * libpng to write a non-standard PNG file with raw deflate or gzip + * compressed IDAT or ancillary chunks. Such files can be read and there is + * no warning on read, so this seems like a very bad idea. + */ + if (window_bits > 15) + { + png_warning(png_ptr, "Only compression windows <= 32k supported by PNG"); + window_bits = 15; + } + + else if (window_bits < 8) + { + png_warning(png_ptr, "Only compression windows >= 256 supported by PNG"); + window_bits = 8; + } + + png_ptr->zlib_window_bits = window_bits; +} + +void PNGAPI +png_set_compression_method(png_structrp png_ptr, int method) +{ + png_debug(1, "in png_set_compression_method"); + + if (png_ptr == NULL) + return; + + /* This would produce an invalid PNG file if it worked, but it doesn't and + * deflate will fault it, so it is harmless to just warn here. + */ + if (method != 8) + png_warning(png_ptr, "Only compression method 8 is supported by PNG"); + + png_ptr->zlib_method = method; +} +#endif /* WRITE_CUSTOMIZE_COMPRESSION */ + +/* The following were added to libpng-1.5.4 */ +#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED +void PNGAPI +png_set_text_compression_level(png_structrp png_ptr, int level) +{ + png_debug(1, "in png_set_text_compression_level"); + + if (png_ptr == NULL) + return; + + png_ptr->zlib_text_level = level; +} + +void PNGAPI +png_set_text_compression_mem_level(png_structrp png_ptr, int mem_level) +{ + png_debug(1, "in png_set_text_compression_mem_level"); + + if (png_ptr == NULL) + return; + + png_ptr->zlib_text_mem_level = mem_level; +} + +void PNGAPI +png_set_text_compression_strategy(png_structrp png_ptr, int strategy) +{ + png_debug(1, "in png_set_text_compression_strategy"); + + if (png_ptr == NULL) + return; + + png_ptr->zlib_text_strategy = strategy; +} + +/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a + * smaller value of window_bits if it can do so safely. + */ +void PNGAPI +png_set_text_compression_window_bits(png_structrp png_ptr, int window_bits) +{ + if (png_ptr == NULL) + return; + + if (window_bits > 15) + { + png_warning(png_ptr, "Only compression windows <= 32k supported by PNG"); + window_bits = 15; + } + + else if (window_bits < 8) + { + png_warning(png_ptr, "Only compression windows >= 256 supported by PNG"); + window_bits = 8; + } + + png_ptr->zlib_text_window_bits = window_bits; +} + +void PNGAPI +png_set_text_compression_method(png_structrp png_ptr, int method) +{ + png_debug(1, "in png_set_text_compression_method"); + + if (png_ptr == NULL) + return; + + if (method != 8) + png_warning(png_ptr, "Only compression method 8 is supported by PNG"); + + png_ptr->zlib_text_method = method; +} +#endif /* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */ +/* end of API added to libpng-1.5.4 */ + #endif /* WRITE */ diff --git a/scripts/pnglibconf.dfa b/scripts/pnglibconf.dfa index 9331e0267..e13680e98 100644 --- a/scripts/pnglibconf.dfa +++ b/scripts/pnglibconf.dfa @@ -593,9 +593,10 @@ option WRITE_SWAP_ALPHA requires WRITE_TRANSFORMS enables TRANSFORM_MECH option WRITE_INVERT_ALPHA requires WRITE_TRANSFORMS option WRITE_USER_TRANSFORM requires WRITE_TRANSFORMS enables TRANSFORM_MECH -# This is not required for PNG-compliant encoders, but can cause -# trouble if left undefined -option WRITE_INTERLACING requires WRITE enables TRANSFORM_MECH +# This just disables the code within libpng to implement image interlacing on +# write; the app can still write interlaced images by doing it itself. +option WRITE_INTERLACING disabled +option WRITE_INTERLACE requires WRITE enables WRITE_INTERLACING # The following is no longer implemented: option WRITE_WEIGHTED_FILTER requires WRITE diff --git a/tests/pngvalid-interlace-transform b/tests/pngvalid-interlace-transform new file mode 100755 index 000000000..c0cef1ce6 --- /dev/null +++ b/tests/pngvalid-interlace-transform @@ -0,0 +1,2 @@ +#!/bin/sh +exec ./pngvalid --strict --transform --interlace