diff options
-rw-r--r-- | png.h | 90 | ||||
-rw-r--r-- | pngconf.h | 4 | ||||
-rw-r--r-- | pngread.c | 179 | ||||
-rw-r--r-- | pngrio.c | 25 | ||||
-rw-r--r-- | pngrutil.c | 45 |
5 files changed, 338 insertions, 5 deletions
@@ -765,6 +765,50 @@ typedef png_unknown_chunk FAR * png_unknown_chunkp; typedef png_unknown_chunk FAR * FAR * png_unknown_chunkpp; #endif +#ifdef PNG_INDEX_SUPPORTED +/* png_line_index_struct records an index point, where we impose an index point + * to be located at the beginning of a line for simplifying the implementation. + */ +typedef struct png_line_index_struct +{ + // state of the lz decoder + z_streamp z_state; + + // the IDAT header position of the chunk, which the index point is in + png_uint_32 stream_idat_position; + + // we intend to record the offset of the index point in the chunk, + // but we record the number of remaining bytes in the chunk after the + // index point. That's because PNG processes a chunk this way. + png_uint_32 bytes_left_in_idat; + + // decompressed data of the previous row + png_bytep prev_row; +} png_line_index; +typedef png_line_index FAR * png_line_indexp; + +typedef struct png_index_struct +{ + // A temporary variable used when we build the index. The variable records + // the IDAT header position of the last chunk read in so far. + png_uint_32 stream_idat_position; + + // line index information about each passes + + // the number of index points in each pass + png_uint_32 size[7]; + + // the line span of two index points of each pass + png_uint_32 step[7]; + + // the index points of each pass + png_line_indexp *pass_line_index[7]; +} png_index; +typedef png_index FAR * png_indexp; + +#define INDEX_SAMPLE_SIZE 254 +#endif + /* png_info is a structure that holds the information in a PNG file so * that the application can find out the characteristics of the image. * If you are reading the file, this structure will tell you what is @@ -1171,6 +1215,9 @@ typedef png_struct FAR * png_structp; typedef void (PNGAPI *png_error_ptr) PNGARG((png_structp, png_const_charp)); typedef void (PNGAPI *png_rw_ptr) PNGARG((png_structp, png_bytep, png_size_t)); +#ifdef PNG_INDEX_SUPPORTED +typedef void (PNGAPI *png_seek_ptr) PNGARG((png_structp, png_uint_32)); +#endif typedef void (PNGAPI *png_flush_ptr) PNGARG((png_structp)); typedef void (PNGAPI *png_read_status_ptr) PNGARG((png_structp, png_uint_32, int)); @@ -1243,6 +1290,9 @@ struct png_struct_def png_voidp error_ptr PNG_DEPSTRUCT; /* user supplied struct for error functions */ png_rw_ptr write_data_fn PNG_DEPSTRUCT; /* function for writing output data */ png_rw_ptr read_data_fn PNG_DEPSTRUCT; /* function for reading input data */ +#ifdef PNG_INDEX_SUPPORTED + png_seek_ptr seek_data_fn PNG_DEPSTRUCT; /* function for seeking input data */ +#endif png_voidp io_ptr PNG_DEPSTRUCT; /* ptr to application struct for I/O functions */ #ifdef PNG_READ_USER_TRANSFORM_SUPPORTED @@ -1533,14 +1583,17 @@ struct png_struct_def png_unknown_chunk unknown_chunk PNG_DEPSTRUCT; #endif +#ifdef PNG_INDEX_SUPPORTED + png_indexp index PNG_DEPSTRUCT; + png_uint_32 total_data_read; +#endif + /* New members added in libpng-1.2.26 */ png_uint_32 old_big_row_buf_size PNG_DEPSTRUCT; png_uint_32 old_prev_row_size PNG_DEPSTRUCT; /* New member added in libpng-1.2.30 */ png_charp chunkdata PNG_DEPSTRUCT; /* buffer for reading chunk data */ - - }; @@ -1843,6 +1896,14 @@ extern PNG_EXPORT(void,png_read_row) PNGARG((png_structp png_ptr, png_bytep display_row)); #endif +#ifdef PNG_INDEX_SUPPORTED +/* Build image index for partial image decoding. */ +extern PNG_EXPORT(void,png_build_index) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(void,png_configure_decoder) + PNGARG((png_structp png_ptr, int *row_offset, int pass)); +#endif + + #ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED /* Read the whole image into memory at once. */ extern PNG_EXPORT(void,png_read_image) PNGARG((png_structp png_ptr, @@ -2059,6 +2120,14 @@ extern PNG_EXPORT(void,png_set_write_fn) PNGARG((png_structp png_ptr, extern PNG_EXPORT(void,png_set_read_fn) PNGARG((png_structp png_ptr, png_voidp io_ptr, png_rw_ptr read_data_fn)); +#ifdef PNG_INDEX_SUPPORTED +/* Set the data seek function with a user supplied one. + * REQUIRED by partial image decode. + */ +extern PNG_EXPORT(void,png_set_seek_fn) PNGARG((png_structp png_ptr, + png_seek_ptr seek_data_fn)); +#endif + /* Return the user pointer associated with the I/O functions */ extern PNG_EXPORT(png_voidp,png_get_io_ptr) PNGARG((png_structp png_ptr)); @@ -3189,6 +3258,11 @@ PNG_EXTERN void png_write_data PNGARG((png_structp png_ptr, png_bytep data, PNG_EXTERN void png_read_data PNGARG((png_structp png_ptr, png_bytep data, png_size_t length)) PNG_PRIVATE; +#ifdef PNG_INDEX_SUPPORTED +PNG_EXTERN void png_seek_data PNGARG((png_structp png_ptr, + png_uint_32 length)) PNG_PRIVATE; +#endif + /* Read bytes into buf, and update png_ptr->crc */ PNG_EXTERN void png_crc_read PNGARG((png_structp png_ptr, png_bytep buf, png_size_t length)) PNG_PRIVATE; @@ -3205,6 +3279,13 @@ PNG_EXTERN void png_decompress_chunk PNGARG((png_structp png_ptr, PNG_EXTERN int png_crc_finish PNGARG((png_structp png_ptr, png_uint_32 skip) PNG_PRIVATE); +#ifdef PNG_INDEX_SUPPORTED +/* Read "skip" bytes, read the file crc, and (optionally) verify png_ptr->crc */ +PNG_EXTERN int png_opt_crc_finish PNGARG((png_structp png_ptr, + png_uint_32 skip, int check_crc) + PNG_PRIVATE); +#endif + /* Read the CRC from the file and compare it to the libpng calculated CRC */ PNG_EXTERN int png_crc_error PNGARG((png_structp png_ptr)) PNG_PRIVATE; @@ -3411,6 +3492,11 @@ PNG_EXTERN void png_write_filtered_row PNGARG((png_structp png_ptr, /* Finish a row while reading, dealing with interlacing passes, etc. */ PNG_EXTERN void png_read_finish_row PNGARG((png_structp png_ptr)); +#ifdef PNG_INDEX_SUPPORTED +/* Update the decoder status to the given pass */ +PNG_EXTERN void png_set_interlaced_pass PNGARG((png_structp png_ptr, int pass)); +#endif + /* Initialize the row buffers, etc. */ PNG_EXTERN void png_read_start_row PNGARG((png_structp png_ptr)) PNG_PRIVATE; /* Optional call to update the users info structure */ @@ -22,6 +22,10 @@ #define PNG_1_2_X +#ifndef PNG_NO_INDEX_SUPPORTED +# define PNG_INDEX_SUPPORTED +#endif + /* * PNG_USER_CONFIG has to be defined on the compiler command line. This * includes the resource compiler for Windows DLL configurations. @@ -192,6 +192,10 @@ png_create_read_struct_2(png_const_charp user_png_ver, png_voidp error_ptr, #endif #endif /* PNG_SETJMP_SUPPORTED */ +#ifdef PNG_INDEX_SUPPORTED + png_ptr->index = NULL; +#endif + return (png_ptr); } @@ -567,6 +571,11 @@ png_read_update_info(png_structp png_ptr, png_infop info_ptr) if (png_ptr == NULL) return; +#ifdef PNG_INDEX_SUPPORTED + if (png_ptr->index) { + png_read_start_row(png_ptr); + } +#endif if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) png_read_start_row(png_ptr); else @@ -736,7 +745,14 @@ png_read_row(png_structp png_ptr, png_bytep row, png_bytep dsp_row) { while (!png_ptr->idat_size) { - png_crc_finish(png_ptr, 0); +#ifdef PNG_INDEX_SUPPORTED + if (png_ptr->index) { + png_opt_crc_finish(png_ptr, 0, 0); + png_ptr->index->stream_idat_position = png_ptr->total_data_read; + } else +#endif + png_crc_finish(png_ptr, 0); + png_ptr->idat_size = png_read_chunk_header(png_ptr); if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) @@ -761,7 +777,10 @@ png_read_row(png_structp png_ptr, png_bytep row, png_bytep dsp_row) break; } if (ret != Z_OK) - png_error(png_ptr, png_ptr->zstream.msg ? png_ptr->zstream.msg : +#ifdef PNG_INDEX_SUPPORTED + if (png_ptr->index && png_ptr->row_number != png_ptr->height - 1) +#endif + png_error(png_ptr, png_ptr->zstream.msg ? png_ptr->zstream.msg : "Decompression error"); } while (png_ptr->zstream.avail_out); @@ -893,6 +912,144 @@ png_read_rows(png_structp png_ptr, png_bytepp row, } #endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ +#ifdef PNG_INDEX_SUPPORTED +#define IDAT_HEADER_SIZE 8 + +/* Set the png read position to a new position based on idat_position and + * offset. + */ +void +png_set_read_offset(png_structp png_ptr, + png_uint_32 idat_position, png_uint_32 bytes_left) +{ + png_seek_data(png_ptr, idat_position); + png_ptr->idat_size = png_read_chunk_header(png_ptr); + + // We need to add back IDAT_HEADER_SIZE because in zlib's perspective, + // IDAT_HEADER in PNG is already stripped out. + png_seek_data(png_ptr, idat_position + IDAT_HEADER_SIZE + png_ptr->idat_size - bytes_left); + png_ptr->idat_size = bytes_left; +} + +/* Configure png decoder to decode the pass starting from *row. + * The requested row may be adjusted to align with an indexing row. + * The actual row for the decoder to start its decoding will be returned in + * *row. + */ +void PNGAPI +png_configure_decoder(png_structp png_ptr, int *row, int pass) +{ + png_indexp index = png_ptr->index; + int n = *row / index->step[pass]; + png_line_indexp line_index = index->pass_line_index[pass][n]; + + // Adjust row to an indexing row. + *row = n * index->step[pass]; + png_ptr->row_number = *row; + +#ifdef PNG_READ_INTERLACING_SUPPORTED + if (png_ptr->interlaced) + png_set_interlaced_pass(png_ptr, pass); +#endif + + long row_byte_length = + PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->iwidth) + 1; + + inflateEnd(&png_ptr->zstream); + inflateCopy(&png_ptr->zstream, line_index->z_state); + + // Set the png read position to line_index. + png_set_read_offset(png_ptr, line_index->stream_idat_position, + line_index->bytes_left_in_idat); + png_memcpy_check(png_ptr, + png_ptr->prev_row, line_index->prev_row, row_byte_length); + png_ptr->zstream.avail_in = 0; +} + +/* Build the line index and store the index in png_ptr->index. + */ +void PNGAPI +png_build_index(png_structp png_ptr) +{ + // number of rows in a 8x8 block for each interlaced pass. + int number_rows_in_pass[7] = {1, 1, 1, 2, 2, 4, 4}; + + int ret; + png_uint_32 i, j; + png_bytep rp; + int p, pass_number = 1; + +#ifdef PNG_READ_INTERLACING_SUPPORTED + pass_number = png_set_interlace_handling(png_ptr); +#endif + + if (png_ptr == NULL) + return; + + png_read_start_row(png_ptr); + +#ifdef PNG_READ_INTERLACING_SUPPORTED + if (!png_ptr->interlaced) +#endif + { + number_rows_in_pass[0] = 8; + } + + rp = png_malloc(png_ptr, png_ptr->rowbytes); + + png_indexp index = png_malloc(png_ptr, sizeof(png_index)); + png_ptr->index = index; + + index->stream_idat_position = png_ptr->total_data_read - IDAT_HEADER_SIZE; + + // Set the default size of index in each pass to 0, + // so that we can free index correctly in png_destroy_read_struct. + for (p = 0; p < 7; p++) + index->size[p] = 0; + + for (p = 0; p < pass_number; p++) + { + // We adjust the index step in each pass to make sure each pass + // has roughly the same size of index. + // This way, we won't consume to much memory in recording index. + index->step[p] = INDEX_SAMPLE_SIZE * (8 / number_rows_in_pass[p]); + index->size[p] = + (png_ptr->height + index->step[p] - 1) / index->step[p]; + index->pass_line_index[p] = + png_malloc(png_ptr, index->size[p] * sizeof(png_line_indexp)); + + // Get the row_byte_length seen by the filter. This value may be + // different from the row_byte_length of a bitmap in the case of + // color palette mode. + int row_byte_length = + PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->iwidth) + 1; + + // Now, we record index for each indexing row. + for (i = 0; i < index->size[p]; i++) + { + png_line_indexp line_index = png_malloc(png_ptr, sizeof(png_line_index)); + index->pass_line_index[p][i] = line_index; + + line_index->z_state = png_malloc(png_ptr, sizeof(z_stream)); + inflateCopy(line_index->z_state, &png_ptr->zstream); + line_index->prev_row = png_malloc(png_ptr, row_byte_length); + png_memcpy_check(png_ptr, + line_index->prev_row, png_ptr->prev_row, row_byte_length); + line_index->stream_idat_position = index->stream_idat_position; + line_index->bytes_left_in_idat = png_ptr->idat_size + png_ptr->zstream.avail_in; + + // Skip the "step" number of rows to the next indexing row. + for (j = 0; j < index->step[p] && + i * index->step[p] + j < png_ptr->height; j++) + { + png_read_row(png_ptr, rp, png_bytep_NULL); + } + } + } + png_free(png_ptr, rp); +} +#endif + #ifdef PNG_SEQUENTIAL_READ_SUPPORTED /* Read the entire image. If the image has an alpha channel or a tRNS * chunk, and you have called png_handle_alpha()[*], you will need to @@ -1184,6 +1341,24 @@ png_destroy_read_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr, if (png_ptr != NULL) { +#ifdef PNG_INDEX_SUPPORTED + if (png_ptr->index) { + unsigned int i, p; + png_indexp index = png_ptr->index; + for (p = 0; p < 7; p++) { + for (i = 0; i < index->size[p]; i++) { + inflateEnd(index->pass_line_index[p][i]->z_state); + png_free(png_ptr, index->pass_line_index[p][i]->z_state); + png_free(png_ptr, index->pass_line_index[p][i]->prev_row); + png_free(png_ptr, index->pass_line_index[p][i]); + } + if (index->size[p] != 0) { + png_free(png_ptr, index->pass_line_index[p]); + } + } + png_free(png_ptr, index); + } +#endif #ifdef PNG_USER_MEM_SUPPORTED png_destroy_struct_2((png_voidp)png_ptr, (png_free_ptr)free_fn, (png_voidp)mem_ptr); @@ -38,7 +38,22 @@ png_read_data(png_structp png_ptr, png_bytep data, png_size_t length) (*(png_ptr->read_data_fn))(png_ptr, data, length); else png_error(png_ptr, "Call to NULL read function"); + +#ifdef PNG_INDEX_SUPPORTED + png_ptr->total_data_read += length; +#endif +} + +#ifdef PNG_INDEX_SUPPORTED +void /* PRIVATE */ +png_seek_data(png_structp png_ptr, png_uint_32 offset) +{ + if (png_ptr->seek_data_fn != NULL) + (*(png_ptr->seek_data_fn))(png_ptr, offset); + else + png_error(png_ptr, "Call to NULL seek function"); } +#endif #ifdef PNG_STDIO_SUPPORTED /* This is the function that does the actual reading of data. If you are @@ -177,4 +192,14 @@ png_set_read_fn(png_structp png_ptr, png_voidp io_ptr, png_ptr->output_flush_fn = NULL; #endif } + +#ifdef PNG_INDEX_SUPPORTED +void PNGAPI +png_set_seek_fn(png_structp png_ptr, png_seek_ptr seek_data_fn) +{ + if (png_ptr == NULL) + return; + png_ptr->seek_data_fn = seek_data_fn; +} +#endif #endif /* PNG_READ_SUPPORTED */ @@ -144,13 +144,14 @@ png_crc_read(png_structp png_ptr, png_bytep buf, png_size_t length) png_calculate_crc(png_ptr, buf, length); } +#ifdef PNG_INDEX_SUPPORTED /* Optionally skip data and then check the CRC. Depending on whether we * are reading a ancillary or critical chunk, and how the program has set * things up, we may calculate the CRC on the data and print a message. * Returns '1' if there was a CRC error, '0' otherwise. */ int /* PRIVATE */ -png_crc_finish(png_structp png_ptr, png_uint_32 skip) +png_opt_crc_finish(png_structp png_ptr, png_uint_32 skip, int check_crc) { png_size_t i; png_size_t istop = png_ptr->zbuf_size; @@ -166,6 +167,10 @@ png_crc_finish(png_structp png_ptr, png_uint_32 skip) if (png_crc_error(png_ptr)) { + if (!check_crc) { + png_chunk_warning(png_ptr, "CRC error"); + return (1); + } if (((png_ptr->chunk_name[0] & 0x20) && /* Ancillary */ !(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN)) || (!(png_ptr->chunk_name[0] & 0x20) && /* Critical */ @@ -182,6 +187,18 @@ png_crc_finish(png_structp png_ptr, png_uint_32 skip) return (0); } +#endif + +/* Optionally skip data and then check the CRC. Depending on whether we + * are reading a ancillary or critical chunk, and how the program has set + * things up, we may calculate the CRC on the data and print a message. + * Returns '1' if there was a CRC error, '0' otherwise. + */ +int /* PRIVATE */ +png_crc_finish(png_structp png_ptr, png_uint_32 skip) +{ + return png_opt_crc_finish(png_ptr, skip, 1); +} /* Compare the CRC stored in the PNG file with that calculated by libpng from * the data it has read thus far. @@ -3045,6 +3062,32 @@ png_read_filter_row(png_structp png_ptr, png_row_infop row_info, png_bytep row, } } +#ifdef PNG_INDEX_SUPPORTED +void /* PRIVATE */ +png_set_interlaced_pass(png_structp png_ptr, int pass) +{ + /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* Start of interlace block */ + PNG_CONST int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* Offset to next interlace block */ + PNG_CONST int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* Start of interlace block in the y direction */ + PNG_CONST int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* Offset to next interlace block in the y direction */ + PNG_CONST int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; + + png_ptr->pass = pass; + png_ptr->iwidth = (png_ptr->width + + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; +} +#endif + #ifdef PNG_SEQUENTIAL_READ_SUPPORTED void /* PRIVATE */ png_read_finish_row(png_structp png_ptr) |