summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Bowler <jbowler@acm.org>2010-12-19 06:22:23 -0600
committerGlenn Randers-Pehrson <glennrp at users.sourceforge.net>2010-12-19 06:22:23 -0600
commit660c6e4d70b8b7e688031e65f65b818ac99b5e8b (patch)
tree8becdd6260e0efbc4e59546cfe1021355dc9da86
parent4c93a7cb6fad9692fcce27539209d90b02a7e234 (diff)
downloadlibpng-660c6e4d70b8b7e688031e65f65b818ac99b5e8b.tar.gz
[devel] Fixed interlace image handling and add test cases (John Bowler)
-rw-r--r--ANNOUNCE7
-rw-r--r--CHANGES3
-rw-r--r--libpng-manual.txt228
-rw-r--r--png.h81
-rw-r--r--pngrutil.c58
-rw-r--r--pngvalid.c1186
-rw-r--r--scripts/pngwin.def5
-rw-r--r--scripts/symbols.def5
-rwxr-xr-xtest-pngvalid-simple.sh8
9 files changed, 1300 insertions, 281 deletions
diff --git a/ANNOUNCE b/ANNOUNCE
index c523b2870..8e84ed9d7 100644
--- a/ANNOUNCE
+++ b/ANNOUNCE
@@ -1,5 +1,5 @@
-Libpng 1.5.0beta58 - December 9, 2010
+Libpng 1.5.0beta58 - December 10, 2010
This is not intended to be a public release. It will be replaced
within a few weeks by a public version or by another test version.
@@ -226,7 +226,7 @@ version 1.5.0beta24 [May 7, 2010]
offset of the png_ptr->rowbuf pointer into png_ptr->big_row_buf.
Added more blank lines for readability.
-version 1.5.0beta25 [December 9, 2010]
+version 1.5.0beta25 [December 10, 2010]
In pngpread.c: png_push_have_row() add check for new_row > height
Removed the now-redundant check for out-of-bounds new_row from example.c
@@ -473,7 +473,8 @@ Version 1.5.0beta57 [December 9, 2010]
Added "--with-zprefix=<string>" to configure.ac
Updated the prebuilt configuration files to autoconf version 2.68
-Version 1.5.0beta58 [December 9, 2010]
+Version 1.5.0beta58 [December 19, 2010]
+ Fixed interlace image handling and add test cases (John Bowler)
Send comments/corrections/commendations to png-mng-implement at lists.sf.net:
(subscription required; visit
diff --git a/CHANGES b/CHANGES
index 2f0d368af..12dd7ac51 100644
--- a/CHANGES
+++ b/CHANGES
@@ -3111,7 +3111,8 @@ Version 1.5.0beta57 [December 9, 2010]
Added "--with-zprefix=<string>" to configure.ac
Updated the prebuilt configuration files to autoconf version 2.68
-Version 1.5.0beta58 [December 9, 2010]
+Version 1.5.0beta58 [December 19, 2010]
+ Fixed interlace image handling and add test cases (John Bowler)
Send comments/corrections/commendations to png-mng-implement at lists.sf.net
(subscription required; visit
diff --git a/libpng-manual.txt b/libpng-manual.txt
index 26864c685..73e660402 100644
--- a/libpng-manual.txt
+++ b/libpng-manual.txt
@@ -1,6 +1,6 @@
libpng-manual.txt - A description on how to use and modify libpng
- libpng version 1.5.0beta58 - December 9, 2010
+ libpng version 1.5.0beta58 - December 10, 2010
Updated and distributed by Glenn Randers-Pehrson
<glennrp at users.sourceforge.net>
Copyright (c) 1998-2010 Glenn Randers-Pehrson
@@ -11,7 +11,7 @@ libpng-manual.txt - A description on how to use and modify libpng
Based on:
- libpng versions 0.97, January 1998, through 1.5.0beta58 - December 9, 2010
+ libpng versions 0.97, January 1998, through 1.5.0beta58 - December 10, 2010
Updated and distributed by Glenn Randers-Pehrson
Copyright (c) 1998-2010 Glenn Randers-Pehrson
@@ -1475,13 +1475,15 @@ a single row_pointer instead of an array of row_pointers:
If the file is interlaced (interlace_type != 0 in the IHDR chunk), things
get somewhat harder. The only current (PNG Specification version 1.2)
-interlacing type for PNG is (interlace_type == PNG_INTERLACE_ADAM7)
-is a somewhat complicated 2D interlace scheme, known as Adam7, that
+interlacing type for PNG is (interlace_type == PNG_INTERLACE_ADAM7);
+a somewhat complicated 2D interlace scheme, known as Adam7, that
breaks down an image into seven smaller images of varying size, based
-on an 8x8 grid.
+on an 8x8 grid. This number is defined (from libpng 1.5) as
+PNG_INTERLACE_ADAM7_PASSES in png.h
libpng can fill out those images or it can give them to you "as is".
-If you want them filled out, there are two ways to do that. The one
+It is almost always better to have libpng handle the interlacing for you.
+If you want the images filled out, there are two ways to do that. The one
mentioned in the PNG specification is to expand each pixel to cover
those pixels that have not been read yet (the "rectangle" method).
This results in a blocky image for the first pass, which gradually
@@ -1491,65 +1493,20 @@ rest of the image remaining whatever colors they were initialized to
before the start of the read. The first method usually looks better,
but tends to be slower, as there are more pixels to put in the rows.
-If you don't want libpng to handle the interlacing details, just call
-png_read_rows() seven times to read in all seven images. Each of the
-images is a valid image by itself, or they can all be combined on an
-8x8 grid to form a single image (although if you intend to combine them
-you would be far better off using the libpng interlace handling).
-
-The first pass will return an image 1/8 as wide as the entire image
-(every 8th column starting in column 0) and 1/8 as high as the original
-(every 8th row starting in row 0), the second will be 1/8 as wide
-(starting in column 4) and 1/8 as high (also starting in row 0). The
-third pass will be 1/4 as wide (every 4th pixel starting in column 0) and
-1/8 as high (every 8th row starting in row 4), and the fourth pass will
-be 1/4 as wide and 1/4 as high (every 4th column starting in column 2,
-and every 4th row starting in row 0). The fifth pass will return an
-image 1/2 as wide, and 1/4 as high (starting at column 0 and row 2),
-while the sixth pass will be 1/2 as wide and 1/2 as high as the original
-(starting in column 1 and row 0). The seventh and final pass will be as
-wide as the original, and 1/2 as high, containing all of the odd
-numbered scanlines. Phew!
-
-If you want to retrieve the separate images you must pass the correct
-number of rows to each successive call of png_read_rows().
-Calculating the number isn't quite as straightforward as the previous
-paragraph might suggest; think about what happens with an image with a odd
-number of rows, which passes get the extra row? To help you libpng 1.5.0
-implements a function to return the number of rows and columns in the current
-pass:
-
- int number_of_rows = png_get_num_rows(png_ptr);
- int number_of_cols = png_get_num_cols(png_ptr);
-
-Simply call that before each call to png_read_rows(). You must call
-png_start_read_image() (or png_read_update_info) before the first call to
-ensure that the number libpng holds internally has been updated.
-
-For very small interlaced images the number of rows or columns in a pass
-can be zero.
-You don't need to call png_read_rows() in this case, libpng will simply
-skip to the next pass.
-
-If you want libpng to expand the images, call this before calling
-png_start_read_image() or png_read_update_info():
+If, as is likely, you want libpng to expand the images, call this before
+calling png_start_read_image() or png_read_update_info():
if (interlace_type == PNG_INTERLACE_ADAM7)
number_of_passes
= png_set_interlace_handling(png_ptr);
-This will return the number of passes needed. Currently, this
-is seven, but may change if another interlace type is added.
-This function can be called even if the file is not interlaced,
-where it will return one pass.
-
-If you need to get the number of passes later (for example after the call
-to png_start_read_image()) just call:
-
- number_of_passes = png_get_num_passes(png_ptr);
-
-This function just returns the number - it doesn't make any changes to the
-libpng state.
+This will return the number of passes needed. Currently, this is seven,
+but may change if another interlace type is added. This function can be
+called even if the file is not interlaced, where it will return one pass.
+You then need to read the whole image 'number_of_passes' times. Each time
+will distribute the pixels from the current pass to the correct place in
+the output image, so you need to supply the same rows to png_read_rows in
+each pass.
If you are not going to display the image after each pass, but are
going to wait until the entire image is read in, use the sparkle
@@ -1575,6 +1532,94 @@ the second parameter NULL.
png_read_rows(png_ptr, NULL, row_pointers,
number_of_rows);
+If you don't want libpng to handle the interlacing details, just call
+png_read_rows() PNG_INTERLACE_ADAM7_PASSES times to read in all the images.
+Each of the images is a valid image by itself, however you will almost
+certainly need to distribute the pixels from each sub-image to the
+correct place. This is where everything gets very tricky.
+
+If you want to retrieve the separate images you must pass the correct
+number of rows to each successive call of png_read_rows(). The calculation
+gets pretty complicated for small images, where some sub-images may
+not even exist because either their width or height ends up zero.
+libpng provides two macros to help you in 1.5 and later versions:
+
+ png_uint_32 width = PNG_PASS_COLS(image_width, pass_number);
+ png_uint_32 height = PNG_PASS_ROWS(image_height, pass_number);
+
+Respectively these tell you the width and height of the sub-image
+corresponding to the numbered pass. 'pass' is in in the range 0 to 6 -
+this can be confusing because the specification refers to the same passes
+as 1 to 7! Be careful, you must check both the width and height before
+calling png_read_rows() and not call it for that pass if either is zero.
+
+You can, of course, read each sub-image row by row. If you want to
+produce optimal code to make a pixel-by-pixel transformation of an
+interlaced image this is the best approach; read each row of each pass,
+transform it, and write it out to a new interlaced image.
+
+If you want to de-interlace the image yourself libpng provides further
+macros to help that tell you where to place the pixels in the output image.
+Because the interlacing scheme is rectangular - sub-image pixels are always
+arranged on a rectangular grid - all you need to know for each pass is the
+starting column and row in the output image of the first pixel plus the
+spacing between each pixel. As of libpng 1.5 there are four macros to
+retrieve this information:
+
+ png_uint_32 x = PNG_PASS_START_COL(pass);
+ png_uint_32 y = PNG_PASS_START_ROW(pass);
+ png_uint_32 xStep = 1U << PNG_PASS_COL_SHIFT(pass);
+ png_uint_32 yStep = 1U << PNG_PASS_ROW_SHIFT(pass);
+
+These allow you to write the obvious loop:
+
+ png_uint_32 input_y = 0;
+ png_uint_32 output_y = PNG_PASS_START_ROW(pass);
+
+ while (output_y < output_image_height)
+ {
+ png_uint_32 input_x = 0;
+ png_uint_32 output_x = PNG_PASS_START_COL(pass);
+
+ while (output_x < output_image_width)
+ {
+ image[output_y][output_x] = subimage[pass][input_y][input_x++];
+ output_x += xStep;
+ }
+
+ ++input_y;
+ ouput_y += yStep;
+ }
+
+Notice that the steps between successive output rows and columns are
+returned as shifts. This is possible because the pixels in the subimages
+are always a power of 2 apart - 1, 2, 4 or 8 pixels - in the original
+image. In practice you may need to directly calculate the output coordinate
+given an input coordinate. libpng provides two further macros for this
+purpose:
+
+ png_uint_32 output_x = PNG_COL_FROM_PASS_COL(input_x, pass);
+ png_uint_32 output_y = PNG_ROW_FROM_PASS_ROW(input_y, pass);
+
+Finally a pair of macros are provided to tell you if a particular image
+row or column appears in a given pass:
+
+ int col_in_pass = PNG_COL_IN_INTERLACE_PASS(output_x, pass);
+ int row_in_pass = PNG_ROW_IN_INTERLACE_PASS(output_y, pass);
+
+Bear in mind that you will probably also need to check the width and height
+of the pass in addition to the above to be sure the pass even exists!
+
+With any luck you are convinced by now that you don't want to do your own
+interlace handling. In reality normally the only good reason for doing this
+is if you are processing PNG files on a pixel-by-pixel basis and don't want
+to load the whole file into memory when it is interlaced.
+
+libpng includes a test program, pngvalid, that illustrates reading and
+writing of interlaced images. If you can't get interlacing to work in your
+code and don't want to leave it to libpng (the recommended approach) see
+how pngvalid.c does it.
+
Finishing a sequential read
After you are finished reading the image through the
@@ -1780,6 +1825,9 @@ png_infop info_ptr;
any). You may start getting rows before
png_process_data() returns, so this is your
last chance to prepare for that.
+
+ This is where you turn on interlace handling,
+ assuming you don't want to do it yourself.
*/
}
@@ -1800,14 +1848,22 @@ png_infop info_ptr;
supplying them because it may make your life
easier.
- For the non-NULL rows of interlaced images,
+ If you did not turn on interlace handling then
+ the callback is called for each row of each
+ sub-image when the image is interlaced. In this
+ case 'row_num' is the row in the sub-image, not
+ the row in the output image as it is in all other
+ cases.
+
+ For the non-NULL rows of interlaced images when
+ you have switched on libpng interlace handling,
you must call png_progressive_combine_row()
passing in the row and the old row. You can
call this function for NULL rows (it will just
return) and for non-interlaced images (it just
does the memcpy for you) if it will make the
code easier. Thus, you can just do this for
- all cases:
+ all cases if you switch on interlace handling;
*/
png_progressive_combine_row(png_ptr, old_row,
@@ -1983,11 +2039,10 @@ filter types.
PNG_FILTER_PAETH | PNG_FILTER_VALUE_PAETH|
PNG_ALL_FILTERS);
-If an application
-wants to start and stop using particular filters during compression,
-it should start out with all of the filters (to ensure that the previous
-row of pixels will be stored in case it's needed later), and then add
-and remove them after the start of compression.
+If an application wants to start and stop using particular filters during
+compression, it should start out with all of the filters (to ensure that
+the previous row of pixels will be stored in case it's needed later),
+and then add and remove them after the start of compression.
If you are writing a PNG datastream that is to be embedded in a MNG
datastream, the second parameter can be either 0 or 64.
@@ -2586,25 +2641,39 @@ for details of which pixels to write when.
If you don't want libpng to handle the interlacing details, just
use png_set_interlace_handling() and call png_write_rows() the
-correct number of times to write all seven sub-images.
+correct number of times to write all the sub-images
+(png_set_interlace_handling() returns the number of sub-images.)
If you want libpng to build the sub-images, call this before you start
writing any rows:
- number_of_passes =
- png_set_interlace_handling(png_ptr);
+ number_of_passes = png_set_interlace_handling(png_ptr);
This will return the number of passes needed. Currently, this is seven,
but may change if another interlace type is added.
Then write the complete image number_of_passes times.
- png_write_rows(png_ptr, row_pointers,
- number_of_rows);
+ png_write_rows(png_ptr, row_pointers, number_of_rows);
+
+Think carefully before you write an interlaced image. Typically code that
+reads such images reads all the image data into memory, uncompressed, before
+doing any processing. Only code that can display an image on the fly can
+take advantage of the interlacing and even then the image has to be exactly
+the correct size for the output device, because scaling an image requires
+adjacent pixels and these are not available until all the passes have been
+read.
+
+If you do write an interlaced image you will hardly ever need to handle
+the interlacing yourself. Call png_set_interlace_handling() and use the
+approach described above.
-As some of these rows are not used, and thus return immediately, you may
-want to read about interlacing in the PNG specification, and only update
-the rows that are actually used.
+The only time it is conceivable that you will really need to write an
+interlaced image pass-by-pass is when you have read one pass by pass and
+made some pixel-by-pixel transformation to it, as described in the read
+code above. In this case use the PNG_PASS_ROWS and PNG_PASS_COLS macros
+to determine the size of each sub-image in turn and simply write the rows
+you obtained from the read code.
Finishing a sequential write
@@ -3382,9 +3451,10 @@ Any program that compiled against libpng 1.4 and did not use deprecated
features or access internal library structures should compile and work
against libpng 1.5.
-libpng 1.5.0 adds png_get_num_ functions to help in the reading of
-interlaced images. The functions return the number of interlace passes
-and the number of rows and columns in each pass.
+libpng 1.5.0 adds PNG_ PASS macros to help in the reading and writing of
+interlaced images. The macros return the number of rows and columns in
+each pass and information that can be used to de-interlace and (if
+absolutely necessary) interlace an image.
libpng 1.5.0 adds an API png_longjmp(png_ptr, value). This API calls
the application provided png_longjmp_ptr on the internal, but application
@@ -3740,7 +3810,7 @@ Other rules can be inferred by inspecting the libpng source.
XIV. Y2K Compliance in libpng
-December 9, 2010
+December 10, 2010
Since the PNG Development group is an ad-hoc body, we can't make
an official declaration.
diff --git a/png.h b/png.h
index cc620b6ef..4e4758c81 100644
--- a/png.h
+++ b/png.h
@@ -1,7 +1,7 @@
/* png.h - header file for PNG reference library
*
- * libpng version 1.5.0beta58 - December 9, 2010
+ * libpng version 1.5.0beta58 - December 10, 2010
* Copyright (c) 1998-2010 Glenn Randers-Pehrson
* (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
* (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
@@ -11,7 +11,7 @@
* Authors and maintainers:
* libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat
* libpng versions 0.89c, June 1996, through 0.96, May 1997: Andreas Dilger
- * libpng versions 0.97, January 1998, through 1.5.0beta58 - December 9, 2010: Glenn
+ * libpng versions 0.97, January 1998, through 1.5.0beta58 - December 10, 2010: Glenn
* See also "Contributing Authors", below.
*
* Note about libpng version numbers:
@@ -173,7 +173,7 @@
*
* This code is released under the libpng license.
*
- * libpng versions 1.2.6, August 15, 2004, through 1.5.0beta58, December 9, 2010, are
+ * libpng versions 1.2.6, August 15, 2004, through 1.5.0beta58, December 10, 2010, are
* Copyright (c) 2004, 2006-2010 Glenn Randers-Pehrson, and are
* distributed according to the same disclaimer and license as libpng-1.2.5
* with the following individual added to the list of Contributing Authors:
@@ -285,7 +285,7 @@
* Y2K compliance in libpng:
* =========================
*
- * December 9, 2010
+ * December 10, 2010
*
* Since the PNG Development group is an ad-hoc body, we can't make
* an official declaration.
@@ -349,7 +349,7 @@
/* Version information for png.h - this should match the version in png.c */
#define PNG_LIBPNG_VER_STRING "1.5.0beta58"
#define PNG_HEADER_VERSION_STRING \
- " libpng version 1.5.0beta58 - December 9, 2010\n"
+ " libpng version 1.5.0beta58 - December 10, 2010\n"
#define PNG_LIBPNG_VER_SONUM 15
#define PNG_LIBPNG_VER_DLLNUM 15
@@ -1135,20 +1135,6 @@ PNG_EXPORT(44, void, png_set_shift, (png_structp png_ptr, png_const_color_8p
PNG_EXPORT(45, int, png_set_interlace_handling, (png_structp png_ptr));
#endif
-#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
-/* Alternatively call this to get the number of rows in the current pass.
- * Call this after png_read_update_info or png_start_read_image; if you
- * call it before it will just return the image height (which is also what
- * it returns for a non-interlaced image!).
- *
- * NOTE: you need to call these to find out how many times to call
- * png_read_row if you handle your own de-interlacing.
- */
-PNG_EXPORT(215, int, png_get_num_passes, (png_structp png_ptr));
-PNG_EXPORT(216, png_uint_32, png_get_num_rows, (png_structp png_ptr));
-PNG_EXPORT(218, png_uint_32, png_get_num_cols, (png_structp png_ptr));
-#endif
-
#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED)
/* Invert monochrome files */
PNG_EXPORT(46, void, png_set_invert_mono, (png_structp png_ptr));
@@ -1159,7 +1145,7 @@ PNG_EXPORT(46, void, png_set_invert_mono, (png_structp png_ptr));
PNG_FP_EXPORT(47, void, png_set_background, (png_structp png_ptr,
png_const_color_16p background_color, int background_gamma_code,
int need_expand, double background_gamma));
-PNG_FIXED_EXPORT(217, void, png_set_background_fixed, (png_structp png_ptr,
+PNG_FIXED_EXPORT(215, void, png_set_background_fixed, (png_structp png_ptr,
png_const_color_16p background_color, int background_gamma_code,
int need_expand, png_fixed_point background_gamma));
#endif
@@ -2072,6 +2058,59 @@ PNG_EXPORT(200, png_const_bytep, png_get_io_chunk_name, (png_structp png_ptr));
# define PNG_IO_MASK_LOC 0x00f0 /* current location: sig/hdr/data/crc */
#endif /* ?PNG_IO_STATE_SUPPORTED */
+/* Interlace support. The following macros are always defined so that if
+ * libpng interlace handling is turned off the macros may be used to handle
+ * interlaced images within the application.
+ */
+#define PNG_INTERLACE_ADAM7_PASSES 7
+
+/* Two macros to return the first row and first column of the original,
+ * full, image which appears in a given pass. 'pass' is in the range 0
+ * to 6 and the result is in the range 0 to 7.
+ */
+#define PNG_PASS_START_ROW(pass) (((1U&~(pass))<<(3-((pass)>>1)))&7)
+#define PNG_PASS_START_COL(pass) (((1U& (pass))<<(3-(((pass)+1)>>1)))&7)
+
+/* Two macros to help evaluate the number of rows or columns in each
+ * pass. This is expressed as a shift - effectively log2 of the number or
+ * rows or columns in each 8x8 tile of the original image.
+ */
+#define PNG_PASS_ROW_SHIFT(pass) ((pass)>2?(8-(pass))>>1:3)
+#define PNG_PASS_COL_SHIFT(pass) ((pass)>1?(7-(pass))>>1:3)
+
+/* Hence two macros to determine the number of rows or columns in a given
+ * pass of an image given its height or width. In fact these macros may
+ * return non-zero even though the sub-image is empty, because the other
+ * dimension may be empty for a small image.
+ */
+#define PNG_PASS_ROWS(height, pass) (((height)+(((1<<PNG_PASS_ROW_SHIFT(pass))\
+ -1)-PNG_PASS_START_ROW(pass)))>>PNG_PASS_ROW_SHIFT(pass))
+#define PNG_PASS_COLS(width, pass) (((width)+(((1<<PNG_PASS_COL_SHIFT(pass))\
+ -1)-PNG_PASS_START_COL(pass)))>>PNG_PASS_COL_SHIFT(pass))
+
+/* For the progressive reader it is necessary to find the row in the output
+ * image given a row in an interlaced image, so two more macros:
+ */
+#define PNG_ROW_FROM_PASS_ROW(yIn, pass) \
+ (((yIn)<<PNG_PASS_ROW_SHIFT(pass))+PNG_PASS_START_ROW(pass))
+#define PNG_COL_FROM_PASS_COL(xIn, pass) \
+ (((xIn)<<PNG_PASS_COL_SHIFT(pass))+PNG_PASS_START_COL(pass))
+
+/* Two macros which return a boolean (0 or 1) saying whether the given row
+ * or column is in a particular pass. These use a common utility macro that
+ * returns a mask for a given pass - the offset 'off' selects the row or
+ * column version. The mask has the appropriate bit set for each column in
+ * the tile.
+ */
+#define PNG_PASS_MASK(pass,off) ( \
+ ((0x110145AFU>>(((7-(off))-(pass))<<2)) & 0xFU) | \
+ ((0x01145AF0U>>(((7-(off))-(pass))<<2)) & 0xF0U))
+
+#define PNG_ROW_IN_INTERLACE_PASS(y, pass) \
+ ((PNG_PASS_MASK(pass,0) >> ((y)&7)) & 1)
+#define PNG_COL_IN_INTERLACE_PASS(x, pass) \
+ ((PNG_PASS_MASK(pass,1) >> ((x)&7)) & 1)
+
#ifdef PNG_READ_COMPOSITE_NODIV_SUPPORTED
/* With these routines we avoid an integer divide, which will be slower on
* most machines. However, it does take more operations than the corresponding
@@ -2174,7 +2213,7 @@ PNG_EXPORT(207, void, png_save_uint_16, (png_bytep buf, unsigned int i));
* one to use is one more than this.)
*/
#ifdef PNG_EXPORT_LAST_ORDINAL
- PNG_EXPORT_LAST_ORDINAL(218);
+ PNG_EXPORT_LAST_ORDINAL(215);
#endif
#ifdef __cplusplus
diff --git a/pngrutil.c b/pngrutil.c
index 6ff9a1e49..3f00de989 100644
--- a/pngrutil.c
+++ b/pngrutil.c
@@ -3293,15 +3293,12 @@ png_read_finish_row(png_structp png_ptr)
png_pass_yinc[png_ptr->pass] - 1 -
png_pass_ystart[png_ptr->pass]) /
png_pass_yinc[png_ptr->pass];
-
- if (!(png_ptr->num_rows))
- continue;
}
else /* if (png_ptr->transformations & PNG_INTERLACE) */
- break;
+ break; /* libpng deinterlacing sees every row */
- } while (png_ptr->iwidth == 0);
+ } while (png_ptr->num_rows == 0 || png_ptr->iwidth == 0);
if (png_ptr->pass < 7)
return;
@@ -3614,55 +3611,4 @@ defined(PNG_USER_TRANSFORM_PTR_SUPPORTED)
png_ptr->flags |= PNG_FLAG_ROW_INIT;
}
-
-#ifdef PNG_SEQUENTIAL_READ_SUPPORTED
-int PNGAPI
-png_get_num_passes(png_structp png_ptr)
-{
- if (png_ptr != NULL)
- {
- if (png_ptr->interlaced)
- return 7;
-
- else
- return 1;
- }
-
- /* Here on error */
- return 0;
-}
-
-png_uint_32 PNGAPI
-png_get_num_rows(png_structp png_ptr)
-{
- if (png_ptr != NULL)
- {
- if (png_ptr->flags & PNG_FLAG_ROW_INIT)
- return png_ptr->num_rows;
-
- else
- png_error(png_ptr, "Call png_start_read_image or png_read_update_info "
- "before png_get_num_rows");
- }
-
- /* Here on error */
- return 0;
-}
-
-png_uint_32 PNGAPI
-png_get_num_cols(png_structp png_ptr)
-{
- if (png_ptr != NULL)
- {
- if (png_ptr->flags & PNG_FLAG_ROW_INIT)
- return png_ptr->iwidth;
- else
- png_error(png_ptr, "Call png_start_read_image or png_read_update_info "
- "before png_get_num_cols");
- }
-
- /* Here on error */
- return 0;
-}
-#endif /* SEQUENTIAL READ */
#endif /* PNG_READ_SUPPORTED */
diff --git a/pngvalid.c b/pngvalid.c
index 840311bea..30d5a63eb 100644
--- a/pngvalid.c
+++ b/pngvalid.c
@@ -3,7 +3,7 @@
*
* Last changed in libpng 1.5.0 [(PENDING RELEASE)]
* Copyright (c) 2010 Glenn Randers-Pehrson
- * Written by John C. Bowler
+ * Written by John Cunningham Bowler
*
* This code is released under the libpng license.
* For conditions of distribution and use, see the disclaimer
@@ -45,7 +45,13 @@ define_exception_type(struct png_store*);
&(ps)->exception_context
#define context(ps,fault) anon_context(ps); png_store *fault
-/******************************* ERROR UTILITIES ******************************/
+/******************************* UTILITIES ************************************/
+/* Error handling is particularly problematic in production code - error
+ * handlers often themselves have bugs which lead to programs that detect
+ * minor errors crashing. The following functions deal with one very
+ * common class of errors in error handlers - attempting to format error or
+ * warning messages into buffers that are too small.
+ */
static size_t safecat(char *buffer, size_t bufsize, size_t pos,
PNG_CONST char *cat)
{
@@ -115,25 +121,45 @@ log2depth(png_byte bit_depth)
}
}
-/* A numeric ID based on PNG file characteristics: */
-#define FILEID(col, depth, interlace) \
- ((png_uint_32)((col) + ((depth)<<3)) + ((interlace)<<8))
+/* A numeric ID based on PNG file characteristics. The 'do_interlace' field
+ * simply records whether pngvalid did the interlace itself or whether it
+ * was done by libpng. Width and height must be less than 256.
+ */
+#define FILEID(col, depth, interlace, width, height, do_interlace) \
+ ((png_uint_32)((col) + ((depth)<<3) + ((interlace)<<8) + \
+ (((do_interlace)!=0)<<15) + ((width)<<16) + ((height)<<24)))
+
#define COL_FROM_ID(id) ((png_byte)((id)& 0x7U))
#define DEPTH_FROM_ID(id) ((png_byte)(((id) >> 3) & 0x1fU))
-#define INTERLACE_FROM_ID(id) ((int)(((id) >> 8) & 0xff))
+#define INTERLACE_FROM_ID(id) ((int)(((id) >> 8) & 0x3))
+#define DO_INTERLACE_FROM_ID(id) ((int)(((id)>>15) & 1))
+#define WIDTH_FROM_ID(id) (((id)>>16) & 0xff)
+#define HEIGHT_FROM_ID(id) (((id)>>24) & 0xff)
/* Utility to construct a standard name for a standard image. */
static size_t
standard_name(char *buffer, size_t bufsize, size_t pos, png_byte colour_type,
- int log_bit_depth, int interlace_type)
+ int log_bit_depth, int interlace_type, png_uint_32 w, png_uint_32 h,
+ int do_interlace)
{
pos = safecat(buffer, bufsize, pos, colour_types[colour_type]);
pos = safecat(buffer, bufsize, pos, " ");
pos = safecat(buffer, bufsize, pos, bit_depths[log_bit_depth]);
- pos = safecat(buffer, bufsize, pos, " bit");
+ pos = safecat(buffer, bufsize, pos, " bit ");
if (interlace_type != PNG_INTERLACE_NONE)
- pos = safecat(buffer, bufsize, pos, " interlaced");
+ pos = safecat(buffer, bufsize, pos, "interlaced");
+ if (do_interlace)
+ pos = safecat(buffer, bufsize, pos, "(pngvalid)");
+ else
+ pos = safecat(buffer, bufsize, pos, "(libpng)");
+ if (w > 0 || h > 0)
+ {
+ pos = safecat(buffer, bufsize, pos, " ");
+ pos = safecatn(buffer, bufsize, pos, w);
+ pos = safecat(buffer, bufsize, pos, "x");
+ pos = safecatn(buffer, bufsize, pos, h);
+ }
return pos;
}
@@ -142,7 +168,8 @@ static size_t
standard_name_from_id(char *buffer, size_t bufsize, size_t pos, png_uint_32 id)
{
return standard_name(buffer, bufsize, pos, COL_FROM_ID(id),
- log2depth(DEPTH_FROM_ID(id)), INTERLACE_FROM_ID(id));
+ log2depth(DEPTH_FROM_ID(id)), INTERLACE_FROM_ID(id),
+ WIDTH_FROM_ID(id), HEIGHT_FROM_ID(id), DO_INTERLACE_FROM_ID(id));
}
/* Convenience API and defines to list valid formats. Note that 16 bit read and
@@ -243,6 +270,72 @@ sample(png_const_bytep row, png_byte colour_type, png_byte bit_depth,
return (result >> (8-index-bit_depth)) & ((1U<<bit_depth)-1);
}
+/* Copy a single pixel, of a given size, from one buffer to another -
+ * while this is basically bit addressed there is an implicit assumption
+ * that pixels 8 or more bits in size are byte aligned and that pixels
+ * do not otherwise cross byte boundaries. (This is, so far as I know,
+ * universally true in bitmap computer graphics. [JCB 20101212])
+ *
+ * NOTE: The to and from buffers may be the same.
+ */
+static void
+pixel_copy(png_bytep toBuffer, png_uint_32 toIndex,
+ png_const_bytep fromBuffer, png_uint_32 fromIndex, unsigned int pixelSize)
+{
+ /* Assume we can multiply by 'size' without overflow because we are
+ * just working in a single buffer.
+ */
+ toIndex *= pixelSize;
+ fromIndex *= pixelSize;
+ if (pixelSize < 8) /* Sub-byte */
+ {
+ /* Mask to select the location of the copied pixel: */
+ unsigned int destMask = ((1U<<pixelSize)-1) << (8-pixelSize-(toIndex&7));
+ /* The following read the entire pixels and clears the extra: */
+ unsigned int destByte = toBuffer[toIndex >> 3] & ~destMask;
+ unsigned int sourceByte = fromBuffer[fromIndex >> 3];
+
+ /* Don't rely on << or >> supporting '0' here, just in case: */
+ fromIndex &= 7;
+ if (fromIndex > 0) sourceByte <<= fromIndex;
+ if ((toIndex & 7) > 0) sourceByte >>= toIndex & 7;
+
+ toBuffer[toIndex >> 3] = (png_byte)(destByte | (sourceByte & destMask));
+ }
+ else /* One or more bytes */
+ memmove(toBuffer+(toIndex>>3), fromBuffer+(fromIndex>>3), pixelSize>>3);
+}
+
+/* Compare pixels - they are assumed to start at the first byte in the
+ * given buffers.
+ */
+static int
+pixel_cmp(png_const_bytep pa, png_const_bytep pb, png_uint_32 bit_width)
+{
+ if (memcmp(pa, pb, bit_width>>3) == 0)
+ {
+ png_uint_32 p;
+
+ if ((bit_width & 7) == 0) return 0;
+
+ /* Ok, any differences? */
+ p = pa[bit_width >> 3];
+ p ^= pb[bit_width >> 3];
+
+ if (p == 0) return 0;
+
+ /* There are, but they may not be significant, remove the bits
+ * after the end (the low order bits in PNG.)
+ */
+ bit_width &= 7;
+ p >>= 8-bit_width;
+
+ if (p == 0) return 0;
+ }
+
+ return 1; /* Different */
+}
+
/*************************** BASIC PNG FILE WRITING ***************************/
/* A png_store takes data from the sequential writer or provides data
* to the sequential reader. It can also store the result of a PNG
@@ -420,6 +513,12 @@ store_ensure_image(png_store *ps, png_structp pp, size_t cb)
++(ps->image);
ps->cb_image = cb;
}
+
+ /* And, for error checking, the whole buffer is set to '1' - this
+ * matches what happens with the 'size' test images on write and also
+ * matches the unused bits in the test rows.
+ */
+ memset(ps->image, 0xff, cb);
}
static void
@@ -1172,6 +1271,9 @@ typedef struct png_modifier
/* Run the standard tests? */
unsigned int test_standard :1;
+ /* Run the odd-sized image and interlace read/write tests? */
+ unsigned int test_size :1;
+
/* When to use the use_input_precision option: */
unsigned int use_input_precision :1;
unsigned int use_input_precision_sbit :1;
@@ -1246,6 +1348,7 @@ modifier_init(png_modifier *pm)
pm->error_gray_16 = pm->error_color_8 = pm->error_color_16 = 0;
pm->interlace_type = PNG_INTERLACE_NONE;
pm->test_standard = 1;
+ pm->test_size = 0;
pm->use_input_precision = 0;
pm->use_input_precision_sbit = 0;
pm->use_input_precision_16to8 = 0;
@@ -1650,17 +1753,34 @@ set_modifier_for_read(png_modifier *pm, png_infopp ppi, png_uint_32 id,
/***************************** STANDARD PNG FILES *****************************/
/* Standard files - write and save standard files. */
-/* The standard files are constructed with rows which fit into a 1024 byte row
+/* There are two basic forms of standard images. Those which attempt to have
+ * all the possible pixel values (not possible for 16bpp images, but a range of
+ * values are produced) and those which have a range of image sizes. The former
+ * are used for testing transforms, in particular gamma correction and bit
+ * reduction and increase. The latter are reserved for testing the behavior of
+ * libpng with respect to 'odd' image sizes - particularly small images where
+ * rows become 1 byte and interlace passes disappear.
+ *
+ * The first, most useful, set are the 'transform' images, the second set of
+ * small images are the 'size' images.
+ *
+ * The transform files are constructed with rows which fit into a 1024 byte row
* buffer. This makes allocation easier below. Further regardless of the file
- * format every file has 128 pixels (giving 1024 bytes for 64bpp formats).
+ * format every row has 128 pixels (giving 1024 bytes for 64bpp formats).
*
* Files are stored with no gAMA or sBIT chunks, with a PLTE only when needed
* and with an ID derived from the colour type, bit depth and interlace type
- * as above (FILEID).
+ * as above (FILEID). The width (128) and height (variable) are not stored in
+ * the FILEID - instead the fields are set to 0, indicating a transform file.
+ *
+ * The size files ar constructed with rows a maximum of 128 bytes wide, allowing
+ * a maximum width of 16 pixels (for the 64bpp case.) They also have a maximum
+ * height of 16 rows. The width and height are stored in the FILEID and, being
+ * non-zero, indicate a size file.
*/
-/* The number of passes is related to the interlace type. There's no libpng API
- * to determine this so we need an inquiry function:
+/* The number of passes is related to the interlace type. There wass no libpng
+ * API to determine this prior to 1.5, so we need an inquiry function:
*/
static int
npasses_from_interlace_type(png_structp pp, int interlace_type)
@@ -1674,13 +1794,10 @@ npasses_from_interlace_type(png_structp pp, int interlace_type)
return 1;
case PNG_INTERLACE_ADAM7:
- return 7;
+ return PNG_INTERLACE_ADAM7_PASSES;
}
}
-#define STD_WIDTH 128U
-#define STD_ROWMAX (STD_WIDTH*8U)
-
static unsigned int
bit_size(png_structp pp, png_byte colour_type, png_byte bit_depth)
{
@@ -1700,19 +1817,30 @@ bit_size(png_structp pp, png_byte colour_type, png_byte bit_depth)
}
}
+#define TRANSFORM_WIDTH 128U
+#define TRANSFORM_ROWMAX (TRANSFORM_WIDTH*8U)
+#define SIZE_ROWMAX (16*8U) /* 16 pixels, max 8 bytes each - 128 bytes */
+#define STANDARD_ROWMAX TRANSFORM_ROWMAX /* The larger of the two */
+
+/* So the maximum image sizes are as follows. A 'transform' image may require
+ * more than 65535 bytes. The size images are a maximum of 2046 bytes.
+ */
+#define TRANSFORM_IMAGEMAX (TRANSFORM_ROWMAX * (png_uint_32)2048)
+#define SIZE_IMAGEMAX (SIZE_ROWMAX * 16U)
+
static size_t
-standard_rowsize(png_structp pp, png_byte colour_type, png_byte bit_depth)
+transform_rowsize(png_structp pp, png_byte colour_type, png_byte bit_depth)
{
- return (STD_WIDTH * bit_size(pp, colour_type, bit_depth)) / 8;
+ return (TRANSFORM_WIDTH * bit_size(pp, colour_type, bit_depth)) / 8;
}
-/* standard_wdith(pp, colour_type, bit_depth) current returns the same number
+/* transform_width(pp, colour_type, bit_depth) current returns the same number
* every time, so just use a macro:
*/
-#define standard_width(pp, colour_type, bit_depth) STD_WIDTH
+#define transform_width(pp, colour_type, bit_depth) TRANSFORM_WIDTH
static png_uint_32
-standard_height(png_structp pp, png_byte colour_type, png_byte bit_depth)
+transform_height(png_structp pp, png_byte colour_type, png_byte bit_depth)
{
switch (bit_size(pp, colour_type, bit_depth))
{
@@ -1740,12 +1868,45 @@ standard_height(png_structp pp, png_byte colour_type, png_byte bit_depth)
}
}
-/* So the maximum standard image size is: */
-#define STD_IMAGEMAX (STD_ROWMAX * 2048)
+/* The following can only be defined here, now we have the definitions
+ * of the transform image sizes.
+ */
+static png_uint_32
+standard_width(png_structp pp, png_uint_32 id)
+{
+ png_uint_32 width = WIDTH_FROM_ID(id);
+ UNUSED(pp);
+
+ if (width == 0)
+ width = transform_width(pp, COL_FROM_ID(id), DEPTH_FROM_ID(id));
+
+ return width;
+}
+
+static png_uint_32
+standard_height(png_structp pp, png_uint_32 id)
+{
+ png_uint_32 height = HEIGHT_FROM_ID(id);
+
+ if (height == 0)
+ height = transform_height(pp, COL_FROM_ID(id), DEPTH_FROM_ID(id));
+
+ return height;
+}
+
+static png_uint_32
+standard_rowsize(png_structp pp, png_uint_32 id)
+{
+ png_uint_32 width = standard_width(pp, id);
+
+ /* This won't overflow: */
+ width *= bit_size(pp, COL_FROM_ID(id), DEPTH_FROM_ID(id));
+ return (width + 7) / 8;
+}
static void
-standard_row(png_structp pp, png_byte buffer[STD_ROWMAX], png_byte colour_type,
- png_byte bit_depth, png_uint_32 y)
+transform_row(png_structp pp, png_byte buffer[TRANSFORM_ROWMAX],
+ png_byte colour_type, png_byte bit_depth, png_uint_32 y)
{
png_uint_32 v = y << 7;
png_uint_32 i = 0;
@@ -1856,8 +2017,14 @@ standard_row(png_structp pp, png_byte buffer[STD_ROWMAX], png_byte colour_type,
*/
#define DEPTH(bd) ((png_byte)(1U << (bd)))
+/* Make a standardized image given a an image colour type, bit depth and
+ * interlace type. The standard images have a very restricted range of
+ * rows and heights and are used for testing transforms rather than image
+ * layout details. See make_size_images below for a way to make images
+ * that test odd sizes along with the libpng interlace handling.
+ */
static void
-make_standard_image(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type,
+make_transform_image(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type,
png_byte PNG_CONST bit_depth, int interlace_type, png_const_charp name)
{
context(ps, fault);
@@ -1875,9 +2042,9 @@ make_standard_image(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type,
if (pp == NULL)
Throw ps;
- h = standard_height(pp, colour_type, bit_depth);
+ h = transform_height(pp, colour_type, bit_depth);
- png_set_IHDR(pp, pi, standard_width(pp, colour_type, bit_depth), h,
+ png_set_IHDR(pp, pi, transform_width(pp, colour_type, bit_depth), h,
bit_depth, colour_type, interlace_type,
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
@@ -1896,7 +2063,7 @@ make_standard_image(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type,
png_write_info(pp, pi);
if (png_get_rowbytes(pp, pi) !=
- standard_rowsize(pp, colour_type, bit_depth))
+ transform_rowsize(pp, colour_type, bit_depth))
png_error(pp, "row size incorrect");
else
@@ -1911,15 +2078,15 @@ make_standard_image(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type,
if (npasses != npasses_from_interlace_type(pp, interlace_type))
png_error(pp, "write: png_set_interlace_handling failed");
- for (pass=1; pass<=npasses; ++pass)
+ for (pass=0; pass<npasses; ++pass)
{
png_uint_32 y;
for (y=0; y<h; ++y)
{
- png_byte buffer[STD_ROWMAX];
+ png_byte buffer[TRANSFORM_ROWMAX];
- standard_row(pp, buffer, colour_type, bit_depth, y);
+ transform_row(pp, buffer, colour_type, bit_depth, y);
png_write_row(pp, buffer);
}
}
@@ -1928,7 +2095,8 @@ make_standard_image(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type,
png_write_end(pp, pi);
/* And store this under the appropriate id, then clean up. */
- store_storefile(ps, FILEID(colour_type, bit_depth, interlace_type));
+ store_storefile(ps, FILEID(colour_type, bit_depth, interlace_type,
+ 0, 0, 0));
store_write_reset(ps);
}
@@ -1956,15 +2124,16 @@ make_standard(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type, int bdlo,
{
char name[FILE_NAME_SIZE];
- standard_name(name, sizeof name, 0, colour_type, bdlo, interlace_type);
- make_standard_image(ps, colour_type, DEPTH(bdlo), interlace_type,
+ standard_name(name, sizeof name, 0, colour_type, bdlo, interlace_type,
+ 0, 0, 0);
+ make_transform_image(ps, colour_type, DEPTH(bdlo), interlace_type,
name);
}
}
}
static void
-make_standard_images(png_store *ps)
+make_transform_images(png_store *ps)
{
/* This is in case of errors. */
safecat(ps->test, sizeof ps->test, 0, "make standard images");
@@ -1978,6 +2147,270 @@ make_standard_images(png_store *ps)
make_standard(ps, 6, 3, WRITE_BDHI);
}
+/* 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<<PNG_PASS_COL_SHIFT(pass);
+
+ for (xout=0; xin<w; xin+=xstep)
+ {
+ pixel_copy(buffer, xout, imageRow, xin, pixel_size);
+ ++xout;
+ }
+}
+
+static void
+deinterlace_row(png_bytep buffer, png_const_bytep row,
+ unsigned int pixel_size, png_uint_32 w, int pass)
+{
+ /* The inverse of the above, 'row' is part of row 'y' of the output image,
+ * in 'buffer'. The image is 'w' wide and this is pass 'pass', distribute
+ * the pixels of row into buffer and return the number written (to allow
+ * this to be checked).
+ */
+ png_uint_32 xin, xout, xstep;
+
+ xout = PNG_PASS_START_COL(pass);
+ xstep = 1U<<PNG_PASS_COL_SHIFT(pass);
+
+ for (xin=0; xout<w; xout+=xstep)
+ {
+ pixel_copy(buffer, xout, row, xin, pixel_size);
+ ++xin;
+ }
+}
+
+/* Build a single row for the 'size' test images, this fills in only the
+ * first bit_width bits of the sample row.
+ */
+static void
+size_row(png_byte buffer[SIZE_ROWMAX], png_uint_32 bit_width, png_uint_32 y)
+{
+ /* height is in the range 1 to 16, so: */
+ y = ((y & 1) << 7) + ((y & 2) << 6) + ((y & 4) << 5) + ((y & 8) << 4);
+ /* the following ensures bits are set in small images: */
+ y ^= 0xA5;
+
+ while (bit_width >= 8)
+ *buffer++ = (png_byte)y++, bit_width -= 8;
+
+ /* There may be up to 7 remaining bits, these go in the most significant
+ * bits of the byte.
+ */
+ if (bit_width > 0)
+ {
+ png_uint_32 mask = (1U<<(8-bit_width))-1;
+ *buffer = (png_byte)((*buffer & mask) | (y & ~mask));
+ }
+}
+
+static void
+make_size_image(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type,
+ png_byte PNG_CONST bit_depth, int PNG_CONST interlace_type,
+ png_uint_32 PNG_CONST w, png_uint_32 PNG_CONST h,
+ int PNG_CONST do_interlace)
+{
+ char name[FILE_NAME_SIZE];
+ context(ps, fault);
+
+ /* Make a name and get an appropriate id: */
+ PNG_CONST png_uint_32 id = FILEID(colour_type, bit_depth, interlace_type,
+ w, h, do_interlace);
+
+ standard_name_from_id(name, sizeof name, 0, id);
+
+ Try
+ {
+ png_infop pi;
+ unsigned int pixel_size;
+ png_structp pp = set_store_for_write(ps, &pi, name);
+
+ /* In the event of a problem return control to the Catch statement below
+ * to do the clean up - it is not possible to 'return' directly from a Try
+ * block.
+ */
+ if (pp == NULL)
+ Throw ps;
+
+ png_set_IHDR(pp, pi, w, h, bit_depth, colour_type, interlace_type,
+ PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+
+ /* Same palette as make_transform_image - I don' think there is any
+ * benefit from using a different one (JB 20101211)
+ */
+ if (colour_type == 3) /* palette */
+ {
+ unsigned int i = 0;
+ png_color pal[256];
+
+ do
+ pal[i].red = pal[i].green = pal[i].blue = (png_byte)i;
+ while(++i < 256U);
+
+ png_set_PLTE(pp, pi, pal, 256);
+ }
+
+ png_write_info(pp, pi);
+
+ /* Calculate the bit size, divide by 8 to get the byte size - this won't
+ * overflow because we know the w values are all small enough even for
+ * a system where 'unsigned int' is only 16 bits.
+ */
+ pixel_size = bit_size(pp, colour_type, bit_depth);
+ if (png_get_rowbytes(pp, pi) != ((w * pixel_size) + 7) / 8)
+ png_error(pp, "row size incorrect");
+
+ else
+ {
+ int npasses = npasses_from_interlace_type(pp, interlace_type);
+ png_uint_32 y;
+ int pass;
+ png_byte image[16][SIZE_ROWMAX];
+
+ /* To help consistent error detection make the parts of this buffer
+ * that aren't set below all '1':
+ */
+ memset(image, 0xff, sizeof image);
+
+ if (!do_interlace && npasses != png_set_interlace_handling(pp))
+ png_error(pp, "write: png_set_interlace_handling failed");
+
+ /* Prepare the whole image first to avoid making it 7 times: */
+ for (y=0; y<h; ++y)
+ size_row(image[y], w * pixel_size, y);
+
+ for (pass=0; pass<npasses; ++pass)
+ {
+ /* The following two are for checking the macros: */
+ PNG_CONST png_uint_32 wPass = PNG_PASS_COLS(w, pass);
+
+ /* If do_interlace is set we don't call png_write_row for every
+ * row because some of them are empty. In fact, for a 1x1 image,
+ * most of them are empty!
+ */
+ for (y=0; y<h; ++y)
+ {
+ png_const_bytep row = image[y];
+ png_byte tempRow[SIZE_ROWMAX];
+
+ /* If do_interlace *and* the image is interlaced we
+ * need a reduced interlace row, this may be reduced
+ * to empty.
+ */
+ if (do_interlace && interlace_type == PNG_INTERLACE_ADAM7)
+ {
+ /* The row must not be written if it doesn't exist, notice
+ * that there are two conditions here, either the row isn't
+ * ever in the pass or the row would be but isn't wide
+ * enough to contribute any pixels. In fact the wPass test
+ * can be used to skip the whole y loop in this case.
+ */
+ if (PNG_ROW_IN_INTERLACE_PASS(y, pass) && wPass > 0)
+ {
+ /* Set to all 1's for error detection (libpng tends to
+ * set unset things to 0).
+ */
+ memset(tempRow, 0xff, sizeof tempRow);
+ interlace_row(tempRow, row, pixel_size, w, pass);
+ row = tempRow;
+ }
+ else
+ continue;
+ }
+
+ /* Only get to here if the row has some pixels in it. */
+ png_write_row(pp, row);
+ }
+ }
+ }
+
+ png_write_end(pp, pi);
+
+ /* And store this under the appropriate id, then clean up. */
+ store_storefile(ps, id);
+
+ store_write_reset(ps);
+ }
+
+ Catch(fault)
+ {
+ /* Use the png_store returned by the exception. This may help the compiler
+ * because 'ps' is not used in this branch of the setjmp. Note that fault
+ * and ps will always be the same value.
+ */
+ store_write_reset(fault);
+ }
+}
+
+static void
+make_size(png_store* PNG_CONST ps, png_byte PNG_CONST colour_type, int bdlo,
+ int PNG_CONST bdhi)
+{
+ for (; bdlo <= bdhi; ++bdlo)
+ {
+ png_uint_32 width;
+
+ for (width = 1; width <= 16; ++width)
+ {
+ png_uint_32 height;
+
+ for (height = 1; height <= 16; ++height)
+ {
+ /* The four combinations of DIY interlace and interlace or not -
+ * no interlace + DIY should be identical to no interlace with
+ * libpng doing it.
+ */
+ make_size_image(ps, colour_type, DEPTH(bdlo), PNG_INTERLACE_NONE,
+ width, height, 0);
+ make_size_image(ps, colour_type, DEPTH(bdlo), PNG_INTERLACE_NONE,
+ width, height, 1);
+ make_size_image(ps, colour_type, DEPTH(bdlo), PNG_INTERLACE_ADAM7,
+ width, height, 0);
+ make_size_image(ps, colour_type, DEPTH(bdlo), PNG_INTERLACE_ADAM7,
+ width, height, 1);
+ }
+ }
+ }
+}
+
+static void
+make_size_images(png_store *ps)
+{
+ /* This is in case of errors. */
+ safecat(ps->test, sizeof ps->test, 0, "make size images");
+
+ /* Arguments are colour_type, low bit depth, high bit depth
+ */
+ make_size(ps, 0, 0, WRITE_BDHI);
+ make_size(ps, 2, 3, WRITE_BDHI);
+ make_size(ps, 3, 0, 3 /*palette: max 8 bits*/);
+ make_size(ps, 4, 3, WRITE_BDHI);
+ make_size(ps, 6, 3, WRITE_BDHI);
+}
+
+/* Return a row based on image id and 'y' for checking: */
+static void
+standard_row(png_structp pp, png_byte std[STANDARD_ROWMAX], png_uint_32 id,
+ png_uint_32 y)
+{
+ if (WIDTH_FROM_ID(id) == 0)
+ transform_row(pp, std, COL_FROM_ID(id), DEPTH_FROM_ID(id), y);
+ else
+ size_row(std, WIDTH_FROM_ID(id) * bit_size(pp, COL_FROM_ID(id),
+ DEPTH_FROM_ID(id)), y);
+}
+
/* Tests - individual test cases */
/* Like 'make_standard' but errors are deliberately introduced into the calls
* to ensure that they get detected - it should not be possible to write an
@@ -2037,8 +2470,8 @@ make_error(png_store* ps, png_byte PNG_CONST colour_type, png_byte bit_depth,
if (pp == NULL)
Throw ps;
- png_set_IHDR(pp, pi, standard_width(pp, colour_type, bit_depth),
- standard_height(pp, colour_type, bit_depth), bit_depth, colour_type,
+ png_set_IHDR(pp, pi, transform_width(pp, colour_type, bit_depth),
+ transform_height(pp, colour_type, bit_depth), bit_depth, colour_type,
interlace_type, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
if (colour_type == 3) /* palette */
@@ -2088,27 +2521,27 @@ make_error(png_store* ps, png_byte PNG_CONST colour_type, png_byte bit_depth,
* undetected, errro has not created problems inside libpng.
*/
if (png_get_rowbytes(pp, pi) !=
- standard_rowsize(pp, colour_type, bit_depth))
+ transform_rowsize(pp, colour_type, bit_depth))
png_error(pp, "row size incorrect");
else
{
- png_uint_32 h = standard_height(pp, colour_type, bit_depth);
+ png_uint_32 h = transform_height(pp, colour_type, bit_depth);
int npasses = png_set_interlace_handling(pp);
int pass;
if (npasses != npasses_from_interlace_type(pp, interlace_type))
png_error(pp, "write: png_set_interlace_handling failed");
- for (pass=1; pass<=npasses; ++pass)
+ for (pass=0; pass<npasses; ++pass)
{
png_uint_32 y;
for (y=0; y<h; ++y)
{
- png_byte buffer[STD_ROWMAX];
+ png_byte buffer[TRANSFORM_ROWMAX];
- standard_row(pp, buffer, colour_type, bit_depth, y);
+ transform_row(pp, buffer, colour_type, bit_depth, y);
png_write_row(pp, buffer);
}
}
@@ -2140,7 +2573,8 @@ make_errors(png_modifier* PNG_CONST pm, png_byte PNG_CONST colour_type,
unsigned int test;
char name[FILE_NAME_SIZE];
- standard_name(name, sizeof name, 0, colour_type, bdlo, interlace_type);
+ standard_name(name, sizeof name, 0, colour_type, bdlo, interlace_type,
+ 0, 0, 0);
for (test=0; test<(sizeof error_test)/(sizeof error_test[0]); ++test)
{
@@ -2212,22 +2646,29 @@ typedef struct standard_display
png_uint_32 w; /* Width of image */
png_uint_32 h; /* Height of image */
int npasses; /* Number of interlaced passes */
- size_t cbRow; /* Bytes in a row of the output image. */
+ png_uint_32 pixel_size; /* Width of one pixel in bits */
+ png_uint_32 bit_width; /* Width of output row in bits */
+ size_t cbRow; /* Bytes in a row of the output image */
+ int do_interlace; /* Do interlacing internally */
} standard_display;
static void
-standard_display_init(standard_display *dp, png_store* ps, png_byte colour_type,
- png_byte bit_depth, int interlace_type)
+standard_display_init(standard_display *dp, png_store* ps, png_uint_32 id,
+ int do_interlace)
{
dp->ps = ps;
- dp->colour_type = colour_type;
- dp->bit_depth = bit_depth;
- dp->interlace_type = interlace_type;
- dp->id = FILEID(colour_type, bit_depth, interlace_type);
+ dp->colour_type = COL_FROM_ID(id);
+ dp->bit_depth = DEPTH_FROM_ID(id);
+ dp->interlace_type = INTERLACE_FROM_ID(id);
+ dp->id = id;
+ /* All the rest are filled in after the read_info: */
dp->w = 0;
dp->h = 0;
dp->npasses = 0;
+ dp->pixel_size = 0;
+ dp->bit_width = 0;
dp->cbRow = 0;
+ dp->do_interlace = do_interlace;
}
/* By passing a 'standard_display' the progressive callbacks can be used
@@ -2258,12 +2699,12 @@ standard_info_part1(standard_display *dp, png_structp pp, png_infop pi)
dp->w = png_get_image_width(pp, pi);
- if (dp->w != standard_width(pp, dp->colour_type, dp->bit_depth))
+ if (dp->w != standard_width(pp, dp->id))
png_error(pp, "validate: image width changed");
dp->h = png_get_image_height(pp, pi);
- if (dp->h != standard_height(pp, dp->colour_type, dp->bit_depth))
+ if (dp->h != standard_height(pp, dp->id))
png_error(pp, "validate: image height changed");
/* Important: this is validating the value *before* any transforms have been
@@ -2271,8 +2712,7 @@ standard_info_part1(standard_display *dp, png_structp pp, png_infop pi)
* no transforms, but it does for other tests where rowbytes may change after
* png_read_update_info.
*/
- if (png_get_rowbytes(pp, pi) !=
- standard_rowsize(pp, dp->colour_type, dp->bit_depth))
+ if (png_get_rowbytes(pp, pi) != standard_rowsize(pp, dp->id))
png_error(pp, "validate: row size changed");
if (dp->colour_type == 3) /* palette */
@@ -2301,11 +2741,10 @@ standard_info_part1(standard_display *dp, png_structp pp, png_infop pi)
/* Read the number of passes - expected to match the value used when
* creating the image (interlaced or not). This has the side effect of
- * turning on interlace handling.
+ * turning on interlace handling (if do_interlace is not set.)
*/
- dp->npasses = png_set_interlace_handling(pp);
-
- if (dp->npasses != npasses_from_interlace_type(pp, dp->interlace_type))
+ dp->npasses = npasses_from_interlace_type(pp, dp->interlace_type);
+ if (!dp->do_interlace && dp->npasses != png_set_interlace_handling(pp))
png_error(pp, "validate: file changed interlace type");
/* Caller calls png_read_update_info or png_start_read_image now, then calls
@@ -2322,8 +2761,15 @@ standard_info_part2(standard_display *dp, png_structp pp, png_infop pi,
int nImages)
{
/* Record cbRow now that it can be found. */
+ dp->pixel_size = bit_size(pp, png_get_color_type(pp, pi),
+ png_get_bit_depth(pp, pi));
+ dp->bit_width = png_get_image_width(pp, pi) * dp->pixel_size;
dp->cbRow = png_get_rowbytes(pp, pi);
+ /* Validate the rowbytes here again. */
+ if (dp->cbRow != (dp->bit_width+7)/8)
+ png_error(pp, "bad png_get_rowbytes calculation");
+
/* Then ensure there is enough space for the output image(s). */
store_ensure_image(dp->ps, pp, nImages * dp->cbRow * dp->h);
}
@@ -2362,20 +2808,43 @@ standard_info(png_structp pp, png_infop pi)
static void
progressive_row(png_structp pp, png_bytep new_row, png_uint_32 y, int pass)
{
- UNUSED(pass);
+ PNG_CONST standard_display *dp = png_get_progressive_ptr(pp);
/* When handling interlacing some rows will be absent in each pass, the
- * callback still gets called, but with a NULL pointer. We need our own
- * 'cbRow', but we can't call png_get_rowbytes because we got no info
- * structure.
+ * callback still gets called, but with a NULL pointer. This is checked
+ * in the 'else' clause below. We need our own 'cbRow', but we can't call
+ * png_get_rowbytes because we got no info structure.
*/
if (new_row != NULL)
{
- PNG_CONST standard_display *dp = png_get_progressive_ptr(pp);
+ png_bytep row;
+
+ /* In the case where the reader doesn't do the interlace it gives
+ * us the y in the sub-image:
+ */
+ if (dp->do_interlace && dp->interlace_type == PNG_INTERLACE_ADAM7)
+ y = PNG_ROW_FROM_PASS_ROW(y, pass);
+
+ /* Validate this just in case. */
+ if (y >= dp->h)
+ png_error(pp, "invalid y to progressive row callback");
+
+ row = dp->ps->image + y * dp->cbRow;
/* Combine the new row into the old: */
- png_progressive_combine_row(pp, dp->ps->image + y * dp->cbRow, new_row);
- }
+ if (dp->do_interlace)
+ {
+ if (dp->interlace_type == PNG_INTERLACE_ADAM7)
+ deinterlace_row(row, new_row, dp->pixel_size, dp->w, pass);
+ else
+ memcpy(row, new_row, dp->cbRow);
+ }
+ else
+ png_progressive_combine_row(pp, row, new_row);
+ } else if (dp->interlace_type == PNG_INTERLACE_ADAM7 &&
+ PNG_ROW_IN_INTERLACE_PASS(y, pass) &&
+ PNG_PASS_COLS(dp->w, pass) > 0)
+ png_error(pp, "missing row in progressive de-interlacing");
}
static void
@@ -2383,19 +2852,52 @@ sequential_row(standard_display *dp, png_structp pp, png_infop pi,
PNG_CONST png_bytep pImage, PNG_CONST png_bytep pDisplay)
{
PNG_CONST int npasses = dp->npasses;
- PNG_CONST png_uint_32 h = dp->h;
+ PNG_CONST int do_interlace = dp->do_interlace &&
+ dp->interlace_type == PNG_INTERLACE_ADAM7;
+ PNG_CONST png_uint_32 height = standard_height(pp, dp->id);
+ PNG_CONST png_uint_32 width = standard_width(pp, dp->id);
PNG_CONST size_t cbRow = dp->cbRow;
int pass;
- for (pass=1; pass <= npasses; ++pass)
+ for (pass=0; pass<npasses; ++pass)
{
png_uint_32 y;
+ png_uint_32 wPass = PNG_PASS_COLS(width, pass);
png_bytep pRow1 = pImage;
png_bytep pRow2 = pDisplay;
- for (y=0; y<h; ++y)
+ for (y=0; y<height; ++y)
{
- png_read_row(pp, pRow1, pRow2);
+ if (do_interlace)
+ {
+ /* wPass may be zero or this row may not be in this pass.
+ * png_read_row must not be called in either case.
+ */
+ if (wPass > 0 && PNG_ROW_IN_INTERLACE_PASS(y, pass))
+ {
+ /* Read the row into a pair of temporary buffers, then do the
+ * merge here into the output rows.
+ */
+ png_byte row[STANDARD_ROWMAX], display[STANDARD_ROWMAX];
+
+ /* The following aids (to some extent) error detection - we can
+ * see where png_read_row wrote. Use opposite values in row and
+ * display to make this easier.
+ */
+ memset(row, 0xff, sizeof row);
+ memset(display, 0, sizeof display);
+
+ png_read_row(pp, row, display);
+
+ if (pRow1 != NULL)
+ deinterlace_row(pRow1, row, dp->pixel_size, dp->w, pass);
+
+ if (pRow2 != NULL)
+ deinterlace_row(pRow2, display, dp->pixel_size, dp->w, pass);
+ }
+ }
+ else
+ png_read_row(pp, pRow1, pRow2);
if (pRow1 != NULL)
pRow1 += cbRow;
@@ -2415,22 +2917,34 @@ static void
standard_row_validate(standard_display *dp, png_structp pp, png_const_bytep row,
png_const_bytep display, png_uint_32 y)
{
- png_byte std[STD_ROWMAX];
- standard_row(pp, std, dp->colour_type, dp->bit_depth, y);
-
- /* At the end both the 'read' and 'display' arrays should end up identical.
- * In earlier passes 'read' will be narrow, containing only the columns that
- * were read, and display will be full width but populated with garbage where
- * pixels have not been filled in.
+ png_byte std[STANDARD_ROWMAX];
+
+ memset(std, 0xff, sizeof std);
+ standard_row(pp, std, dp->id, y);
+
+ /* At the end both the 'row' and 'display' arrays should end up identical.
+ * In earlier passes 'row' will be partially filled in, with only the pixels
+ * that have been read so far, but 'display' will have those pixels
+ * replicated to fill the unread pixels while reading an interlaced image.
+ * The side effect inside the libpng sequential reader is that the 'row'
+ * array retains the correct values for unwritten pixels within the row
+ * bytes, while the 'display' array gets bits off the end of the image (in
+ * the last byte) trashed. Unfortunately in the progressive reader the
+ * row bytes are always trashed, so we always do a pixel_cmp here even though
+ * a memcmp of all cbRow bytes will succeed for the sequential reader.
*/
- if (row != NULL && memcmp(std, row, dp->cbRow) != 0)
+ if (row != NULL && pixel_cmp(std, row, dp->bit_width) != 0)
{
char msg[64];
sprintf(msg, "PNG image row %d changed", y);
png_error(pp, msg);
}
- if (display != NULL && memcmp(std, display, dp->cbRow) != 0)
+ /* In this case use pixel_cmp because we need to compare a partial
+ * byte at the end of the row if the row is not an exact multiple
+ * of 8 bits wide.
+ */
+ if (display != NULL && pixel_cmp(std, display, dp->bit_width) != 0)
{
char msg[64];
sprintf(msg, "display row %d changed", y);
@@ -2474,8 +2988,8 @@ standard_end(png_structp pp, png_infop pi)
/* A single test run checking the standard image to ensure it is not damaged. */
static void
-standard_test(png_store* PNG_CONST psIn, png_byte PNG_CONST colour_typeIn,
- png_byte PNG_CONST bit_depthIn, int interlace_typeIn)
+standard_test(png_store* PNG_CONST psIn, png_uint_32 PNG_CONST id,
+ int do_interlace)
{
standard_display d;
context(psIn, fault);
@@ -2483,8 +2997,7 @@ standard_test(png_store* PNG_CONST psIn, png_byte PNG_CONST colour_typeIn,
/* Set up the display (stack frame) variables from the arguments to the
* function and initialize the locals that are filled in later.
*/
- standard_display_init(&d, psIn, colour_typeIn, bit_depthIn,
- interlace_typeIn);
+ standard_display_init(&d, psIn, id, do_interlace);
/* Everything is protected by a Try/Catch. The functions called also
* typically have local Try/Catch blocks.
@@ -2494,11 +3007,14 @@ standard_test(png_store* PNG_CONST psIn, png_byte PNG_CONST colour_typeIn,
png_structp pp;
png_infop pi;
- /* Get a png_struct for writing the image. This will throw an error if it
+ /* Get a png_struct for reading the image. This will throw an error if it
* fails, so we don't need to check the result.
*/
pp = set_store_for_read(d.ps, &pi, d.id,
- d.ps->progressive ? "progressive reader" : "sequential reader");
+ d.do_interlace ? (d.ps->progressive ?
+ "pngvalid progressive deinterlacer" :
+ "pngvalid sequential deinterlacer") : (d.ps->progressive ?
+ "progressive reader" : "sequential reader"));
/* Introduce the correct read function. */
if (d.ps->progressive)
@@ -2564,15 +3080,8 @@ test_standard(png_modifier* PNG_CONST pm, png_byte PNG_CONST colour_type,
for (interlace_type = PNG_INTERLACE_NONE;
interlace_type < PNG_INTERLACE_LAST; ++interlace_type)
{
- /* Test both sequential and standard readers here. */
- pm->this.progressive = !pm->this.progressive;
- standard_test(&pm->this, colour_type, DEPTH(bdlo), interlace_type);
-
- if (fail(pm))
- return 0;
-
- pm->this.progressive = !pm->this.progressive;
- standard_test(&pm->this, colour_type, DEPTH(bdlo), interlace_type);
+ standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo),
+ interlace_type, 0, 0, 0), 0/*do_interlace*/);
if (fail(pm))
return 0;
@@ -2605,6 +3114,104 @@ perform_standard_test(png_modifier *pm)
}
+/********************************** SIZE TESTS ********************************/
+static int
+test_size(png_modifier* PNG_CONST pm, png_byte PNG_CONST colour_type,
+ int bdlo, int PNG_CONST bdhi)
+{
+ /* Run the tests on each combination.
+ *
+ * NOTE: on my 32 bit x86 each of the following blocks takes
+ * a total of 3.5 seconds if done across every combo of bit depth
+ * width and height. This is a waste of time in practice, hence the
+ * hinc and winc stuff:
+ */
+ static PNG_CONST png_byte hinc[] = {1, 3, 11, 1, 5};
+ static PNG_CONST png_byte winc[] = {1, 9, 5, 7, 1};
+ for (; bdlo <= bdhi; ++bdlo)
+ {
+ png_uint_32 h, w;
+
+ for (h=1; h<=16; h+=hinc[bdlo]) for (w=1; w<=16; w+=winc[bdlo])
+ {
+ /* First test all the 'size' images against the sequential
+ * reader using libpng to deinterlace (where required.) This
+ * validates the write side of libpng. There are four possibilities
+ * to validate.
+ */
+ standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo),
+ PNG_INTERLACE_NONE, w, h, 0), 0/*do_interlace*/);
+
+ if (fail(pm))
+ return 0;
+
+ standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo),
+ PNG_INTERLACE_NONE, w, h, 1), 0/*do_interlace*/);
+
+ if (fail(pm))
+ return 0;
+
+ standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo),
+ PNG_INTERLACE_ADAM7, w, h, 0), 0/*do_interlace*/);
+
+ if (fail(pm))
+ return 0;
+
+ standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo),
+ PNG_INTERLACE_ADAM7, w, h, 1), 0/*do_interlace*/);
+
+ if (fail(pm))
+ return 0;
+
+ /* Now validate the interlaced read side - do_interlace true,
+ * in the progressive case this does actually make a difference
+ * to the code used in the non-interlaced case too.
+ */
+ standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo),
+ PNG_INTERLACE_NONE, w, h, 0), 1/*do_interlace*/);
+
+ if (fail(pm))
+ return 0;
+
+ standard_test(&pm->this, FILEID(colour_type, DEPTH(bdlo),
+ PNG_INTERLACE_ADAM7, w, h, 0), 1/*do_interlace*/);
+
+ if (fail(pm))
+ return 0;
+ }
+ }
+
+ return 1; /* keep going */
+}
+
+static void
+perform_size_test(png_modifier *pm)
+{
+ /* Test each colour type over the valid range of bit depths (expressed as
+ * log2(bit_depth) in turn, stop as soon as any error is detected.
+ */
+ if (!test_size(pm, 0, 0, READ_BDHI))
+ return;
+
+ if (!test_size(pm, 2, 3, READ_BDHI))
+ return;
+
+ /* For the moment don't do the palette test - it's a waste of time when
+ * compared to the greyscale test.
+ */
+#if 0
+ if (!test_size(pm, 3, 0, 3))
+ return;
+#endif
+
+ if (!test_size(pm, 4, 3, READ_BDHI))
+ return;
+
+ if (!test_size(pm, 6, 3, READ_BDHI))
+ return;
+}
+
+
/********************************* GAMMA TESTS ********************************/
/* Gamma test images. */
typedef struct gamma_modification
@@ -2772,14 +3379,12 @@ typedef struct gamma_display
} gamma_display;
static void
-gamma_display_init(gamma_display *dp, png_modifier *pm, png_byte colour_type,
- png_byte bit_depth, int interlace_type, double file_gamma,
- double screen_gamma, png_byte sbit, int threshold_test, int speed,
- int use_input_precision, int strip16)
+gamma_display_init(gamma_display *dp, png_modifier *pm, png_uint_32 id,
+ double file_gamma, double screen_gamma, png_byte sbit, int threshold_test,
+ int speed, int use_input_precision, int strip16)
{
/* Standard fields */
- standard_display_init(&dp->this, &pm->this, colour_type, bit_depth,
- interlace_type);
+ standard_display_init(&dp->this, &pm->this, id, 0/*do_interlace*/);
/* Parameter fields */
dp->pm = pm;
@@ -2900,9 +3505,9 @@ gamma_image_validate(gamma_display *dp, png_structp pp, png_infop pi,
for (y=0; y<h; ++y, pRow += cbRow)
{
unsigned int s, x;
- png_byte std[STD_ROWMAX];
+ png_byte std[STANDARD_ROWMAX];
- standard_row(pp, std, in_ct, in_bd, y);
+ transform_row(pp, std, in_ct, in_bd, y);
if (processing)
{
@@ -3116,9 +3721,9 @@ gamma_test(png_modifier *pmIn, PNG_CONST png_byte colour_typeIn,
gamma_display d;
context(&pmIn->this, fault);
- gamma_display_init(&d, pmIn, colour_typeIn, bit_depthIn, interlace_typeIn,
- file_gammaIn, screen_gammaIn, sbitIn, threshold_testIn, speedIn,
- use_input_precisionIn, strip16In);
+ gamma_display_init(&d, pmIn, FILEID(colour_typeIn, bit_depthIn,
+ interlace_typeIn, 0, 0, 0), file_gammaIn, screen_gammaIn, sbitIn,
+ threshold_testIn, speedIn, use_input_precisionIn, strip16In);
Try
{
@@ -3548,10 +4153,356 @@ perform_gamma_test(png_modifier *pm, int speed, int summary)
#endif
}
+/* INTERLACE MACRO VALIDATION */
+/* This is copied verbatim from the specification, it is simply the pass
+ * number in which each pixel in each 8x8 tile appears. The array must
+ * be indexed adam7[y][x] and notice that the pass numbers are based at
+ * 1, not 0 - the base libpng uses.
+ */
+static PNG_CONST
+png_byte adam7[8][8] =
+{
+ { 1,6,4,6,2,6,4,6 },
+ { 7,7,7,7,7,7,7,7 },
+ { 5,6,5,6,5,6,5,6 },
+ { 7,7,7,7,7,7,7,7 },
+ { 3,6,4,6,3,6,4,6 },
+ { 7,7,7,7,7,7,7,7 },
+ { 5,6,5,6,5,6,5,6 },
+ { 7,7,7,7,7,7,7,7 }
+};
+
+/* This routine validates all the interlace support macros in png.h for
+ * a variety of valid PNG widths and heights. It uses a number of similarly
+ * named internal routines that feed off the above array.
+ */
+static png_uint_32
+png_pass_start_row(int pass)
+{
+ int x, y;
+ ++pass;
+ for (y=0; y<8; ++y) for (x=0; x<8; ++x) if (adam7[y][x] == pass)
+ return y;
+ return 0xf;
+}
+
+static png_uint_32
+png_pass_start_col(int pass)
+{
+ int x, y;
+ ++pass;
+ for (x=0; x<8; ++x) for (y=0; y<8; ++y) if (adam7[y][x] == pass)
+ return x;
+ return 0xf;
+}
+
+static int
+png_pass_row_shift(int pass)
+{
+ int x, y, base=(-1), inc=8;
+ ++pass;
+ for (y=0; y<8; ++y) for (x=0; x<8; ++x) if (adam7[y][x] == pass)
+ {
+ if (base == (-1))
+ base = y;
+ else if (base == y)
+ {}
+ else if (inc == y-base)
+ base=y;
+ else if (inc == 8)
+ inc = y-base, base=y;
+ else if (inc != y-base)
+ return 0xff; /* error - more than one 'inc' value! */
+ }
+
+ if (base == (-1)) return 0xfe; /* error - no row in pass! */
+
+ /* The shift is always 1, 2 or 3 - no pass has all the rows! */
+ switch (inc)
+ {
+case 2: return 1;
+case 4: return 2;
+case 8: return 3;
+default: break;
+ }
+
+ /* error - unrecognized 'inc' */
+ return (inc << 8) + 0xfd;
+}
+
+static int
+png_pass_col_shift(int pass)
+{
+ int x, y, base=(-1), inc=8;
+ ++pass;
+ for (x=0; x<8; ++x) for (y=0; y<8; ++y) if (adam7[y][x] == pass)
+ {
+ if (base == (-1))
+ base = x;
+ else if (base == x)
+ {}
+ else if (inc == x-base)
+ base=x;
+ else if (inc == 8)
+ inc = x-base, base=x;
+ else if (inc != x-base)
+ return 0xff; /* error - more than one 'inc' value! */
+ }
+
+ if (base == (-1)) return 0xfe; /* error - no row in pass! */
+
+ /* The shift is always 1, 2 or 3 - no pass has all the rows! */
+ switch (inc)
+ {
+case 1: return 0; /* pass 7 has all the columns */
+case 2: return 1;
+case 4: return 2;
+case 8: return 3;
+default: break;
+ }
+
+ /* error - unrecognized 'inc' */
+ return (inc << 8) + 0xfd;
+}
+
+static png_uint_32
+png_row_from_pass_row(png_uint_32 yIn, int pass)
+{
+ /* By examination of the array: */
+ switch (pass)
+ {
+case 0: return yIn * 8;
+case 1: return yIn * 8;
+case 2: return yIn * 8 + 4;
+case 3: return yIn * 4;
+case 4: return yIn * 4 + 2;
+case 5: return yIn * 2;
+case 6: return yIn * 2 + 1;
+default: break;
+ }
+
+ return 0xff; /* bad pass number */
+}
+
+static png_uint_32
+png_col_from_pass_col(png_uint_32 xIn, int pass)
+{
+ /* By examination of the array: */
+ switch (pass)
+ {
+case 0: return xIn * 8;
+case 1: return xIn * 8 + 4;
+case 2: return xIn * 4;
+case 3: return xIn * 4 + 2;
+case 4: return xIn * 2;
+case 5: return xIn * 2 + 1;
+case 6: return xIn;
+default: break;
+ }
+
+ return 0xff; /* bad pass number */
+}
+
+static int
+png_row_in_interlace_pass(png_uint_32 y, int pass)
+{
+ /* Is row 'y' in pass 'pass'? */
+ int x;
+ y &= 7;
+ ++pass;
+ for (x=0; x<8; ++x) if (adam7[y][x] == pass)
+ return 1;
+
+ return 0;
+}
+
+static int
+png_col_in_interlace_pass(png_uint_32 x, int pass)
+{
+ /* Is column 'x' in pass 'pass'? */
+ int y;
+ x &= 7;
+ ++pass;
+ for (y=0; y<8; ++y) if (adam7[y][x] == pass)
+ return 1;
+
+ return 0;
+}
+
+static png_uint_32
+png_pass_rows(png_uint_32 height, int pass)
+{
+ png_uint_32 tiles = height>>3;
+ png_uint_32 rows = 0;
+ unsigned int x, y;
+
+ height &= 7;
+ ++pass;
+ for (y=0; y<8; ++y) for (x=0; x<8; ++x) if (adam7[y][x] == pass)
+ {
+ rows += tiles;
+ if (y < height) ++rows;
+ break; /* i.e. break the 'x', column, loop. */
+ }
+
+ return rows;
+}
+
+static png_uint_32
+png_pass_cols(png_uint_32 width, int pass)
+{
+ png_uint_32 tiles = width>>3;
+ png_uint_32 cols = 0;
+ unsigned int x, y;
+
+ width &= 7;
+ ++pass;
+ for (x=0; x<8; ++x) for (y=0; y<8; ++y) if (adam7[y][x] == pass)
+ {
+ cols += tiles;
+ if (x < width) ++cols;
+ break; /* i.e. break the 'y', row, loop. */
+ }
+
+ return cols;
+}
+
+static void
+perform_interlace_macro_validation(void)
+{
+ /* The macros to validate, first those that depend only on pass:
+ *
+ * PNG_PASS_START_ROW(pass)
+ * PNG_PASS_START_COL(pass)
+ * PNG_PASS_ROW_SHIFT(pass)
+ * PNG_PASS_COL_SHIFT(pass)
+ */
+ int pass;
+
+ for (pass=0; pass<7; ++pass)
+ {
+ png_uint_32 m, f, v;
+
+ m = PNG_PASS_START_ROW(pass);
+ f = png_pass_start_row(pass);
+ if (m != f)
+ {
+ fprintf(stderr, "PNG_PASS_START_ROW(%d) = %u != %x\n", pass, m, f);
+ exit(1);
+ }
+
+ m = PNG_PASS_START_COL(pass);
+ f = png_pass_start_col(pass);
+ if (m != f)
+ {
+ fprintf(stderr, "PNG_PASS_START_COL(%d) = %u != %x\n", pass, m, f);
+ exit(1);
+ }
+
+ m = PNG_PASS_ROW_SHIFT(pass);
+ f = png_pass_row_shift(pass);
+ if (m != f)
+ {
+ fprintf(stderr, "PNG_PASS_ROW_SHIFT(%d) = %u != %x\n", pass, m, f);
+ exit(1);
+ }
+
+ m = PNG_PASS_COL_SHIFT(pass);
+ f = png_pass_col_shift(pass);
+ if (m != f)
+ {
+ fprintf(stderr, "PNG_PASS_COL_SHIFT(%d) = %u != %x\n", pass, m, f);
+ exit(1);
+ }
+
+ /* Macros that depend on the image or sub-image height too:
+ *
+ * PNG_PASS_ROWS(height, pass)
+ * PNG_PASS_COLS(width, pass)
+ * PNG_ROW_FROM_PASS_ROW(yIn, pass)
+ * PNG_COL_FROM_PASS_COL(xIn, pass)
+ * PNG_ROW_IN_INTERLACE_PASS(y, pass)
+ * PNG_COL_IN_INTERLACE_PASS(x, pass)
+ */
+ for (v=0;;)
+ {
+ /* First the base 0 stuff: */
+ m = PNG_ROW_FROM_PASS_ROW(v, pass);
+ f = png_row_from_pass_row(v, pass);
+ if (m != f)
+ {
+ fprintf(stderr, "PNG_ROW_FROM_PASS_ROW(%u, %d) = %u != %x\n",
+ v, pass, m, f);
+ exit(1);
+ }
+
+ m = PNG_COL_FROM_PASS_COL(v, pass);
+ f = png_col_from_pass_col(v, pass);
+ if (m != f)
+ {
+ fprintf(stderr, "PNG_COL_FROM_PASS_COL(%u, %d) = %u != %x\n",
+ v, pass, m, f);
+ exit(1);
+ }
+
+ m = PNG_ROW_IN_INTERLACE_PASS(v, pass);
+ f = png_row_in_interlace_pass(v, pass);
+ if (m != f)
+ {
+ fprintf(stderr, "PNG_ROW_IN_INTERLACE_PASS(%u, %d) = %u != %x\n",
+ v, pass, m, f);
+ exit(1);
+ }
+
+ m = PNG_COL_IN_INTERLACE_PASS(v, pass);
+ f = png_col_in_interlace_pass(v, pass);
+ if (m != f)
+ {
+ fprintf(stderr, "PNG_COL_IN_INTERLACE_PASS(%u, %d) = %u != %x\n",
+ v, pass, m, f);
+ exit(1);
+ }
+
+ /* Then the base 1 stuff: */
+ ++v;
+ m = PNG_PASS_ROWS(v, pass);
+ f = png_pass_rows(v, pass);
+ if (m != f)
+ {
+ fprintf(stderr, "PNG_PASS_ROWS(%u, %d) = %u != %x\n",
+ v, pass, m, f);
+ exit(1);
+ }
+
+ m = PNG_PASS_COLS(v, pass);
+ f = png_pass_cols(v, pass);
+ if (m != f)
+ {
+ fprintf(stderr, "PNG_PASS_COLS(%u, %d) = %u != %x\n",
+ v, pass, m, f);
+ exit(1);
+ }
+
+ /* Move to the next v - the stepping algorithm starts skipping
+ * values above 1024.
+ */
+ if (v > 1024)
+ {
+ if (v == PNG_UINT_31_MAX)
+ break;
+
+ v = (v << 1) ^ v;
+ if (v >= PNG_UINT_31_MAX)
+ v = PNG_UINT_31_MAX-1;
+ }
+ }
+ }
+}
+
/* main program */
int main(int argc, PNG_CONST char **argv)
{
- volatile int summary = 1; /* Print the error summary at the end */
+ volatile int summary = 1; /* Print the error summary at the end */
/* Create the given output file on success: */
PNG_CONST char *volatile touch = NULL;
@@ -3572,7 +4523,7 @@ int main(int argc, PNG_CONST char **argv)
/* Preallocate the image buffer, because we know how big it needs to be,
* note that, for testing purposes, it is deliberately mis-aligned.
*/
- pm.this.image = malloc(2*STD_IMAGEMAX+1);
+ pm.this.image = malloc(2*TRANSFORM_IMAGEMAX+1);
if (pm.this.image != NULL)
{
@@ -3580,7 +4531,7 @@ int main(int argc, PNG_CONST char **argv)
* the array appropriately.
*/
++(pm.this.image);
- pm.this.cb_image = 2*STD_IMAGEMAX;
+ pm.this.cb_image = 2*TRANSFORM_IMAGEMAX;
}
/* Default to error on warning: */
@@ -3637,6 +4588,9 @@ int main(int argc, PNG_CONST char **argv)
pm.this.speed = 1, pm.ngammas = (sizeof gammas)/(sizeof gammas[0]),
pm.test_standard = 0;
+ else if (strcmp(*argv, "--size") == 0)
+ pm.test_size = 1;
+
else if (strcmp(*argv, "--nostandard") == 0)
pm.test_standard = 0;
@@ -3706,15 +4660,23 @@ int main(int argc, PNG_CONST char **argv)
Try
{
/* Make useful base images */
- make_standard_images(&pm.this);
+ make_transform_images(&pm.this);
/* Perform the standard and gamma tests. */
if (pm.test_standard)
{
+ perform_interlace_macro_validation();
perform_standard_test(&pm);
perform_error_test(&pm);
}
+ /* Various oddly sized images: */
+ if (pm.test_size)
+ {
+ make_size_images(&pm.this);
+ perform_size_test(&pm);
+ }
+
if (pm.ngammas > 0)
perform_gamma_test(&pm, pm.this.speed != 0,
summary && !pm.this.speed);
diff --git a/scripts/pngwin.def b/scripts/pngwin.def
index 0cbf2cc85..63c722657 100644
--- a/scripts/pngwin.def
+++ b/scripts/pngwin.def
@@ -230,7 +230,4 @@ EXPORTS
png_get_y_offset_inches_fixed @212
png_set_sCAL_fixed @213
png_get_sCAL_fixed @214
- png_get_num_passes @215
- png_get_num_rows @216
- png_set_background_fixed @217
- png_get_num_cols @218
+ png_set_background_fixed @215
diff --git a/scripts/symbols.def b/scripts/symbols.def
index 4b58e0b76..37dcc15e6 100644
--- a/scripts/symbols.def
+++ b/scripts/symbols.def
@@ -220,7 +220,4 @@ EXPORTS
png_get_y_offset_inches_fixed @212
png_set_sCAL_fixed @213
png_get_sCAL_fixed @214
- png_get_num_passes @215
- png_get_num_rows @216
- png_set_background_fixed @217
- png_get_num_cols @218
+ png_set_background_fixed @215
diff --git a/test-pngvalid-simple.sh b/test-pngvalid-simple.sh
index c62e11e7b..24ea0dc0c 100755
--- a/test-pngvalid-simple.sh
+++ b/test-pngvalid-simple.sh
@@ -7,8 +7,14 @@ err=0
echo >> pngtest-log.txt
echo "============ pngvalid-simple.sh ==============" >> pngtest-log.txt
echo "Running test-pngvalid-simple.sh"
+# The options to test are:
+#
+# --progressive-read, --interlace on the 'transform' images
+# --progressive-read on the 'size' images
+#
for opts in "" --progressive-read --interlace \
- "--progressive-read --interlace"
+ "--progressive-read --interlace" "--nostandard --size" \
+ "--nostandard --size --progressive-read"
do
if ./pngvalid --nogamma $opts >> pngtest-log.txt 2>&1
then