Add the feature of tile-based decoding to PNG.

There is a relevant patch to the change in zlib:
https://android-git.corp.google.com/g/#change,63173

Change-Id: I617b8740a50af0d75df9f49fa6b88dd2decb36dd
diff --git a/png.h b/png.h
index cc1915d..a3c21b9 100644
--- a/png.h
+++ b/png.h
@@ -765,6 +765,50 @@
 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 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 @@
    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 @@
    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 @@
    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_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_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 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 @@
 /* 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 */
diff --git a/pngconf.h b/pngconf.h
index defc16d..27c9c32 100644
--- a/pngconf.h
+++ b/pngconf.h
@@ -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.
diff --git a/pngread.c b/pngread.c
index 6207624..fc4788b 100644
--- a/pngread.c
+++ b/pngread.c
@@ -192,6 +192,10 @@
 #endif
 #endif /* PNG_SETJMP_SUPPORTED */
 
+#ifdef PNG_INDEX_SUPPORTED
+   png_ptr->index = NULL;
+#endif
+
    return (png_ptr);
 }
 
@@ -567,6 +571,11 @@
  
    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 @@
       {
          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 @@
          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 @@
 }
 #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 @@
 
    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);
diff --git a/pngrio.c b/pngrio.c
index 6978682..4d1220a 100644
--- a/pngrio.c
+++ b/pngrio.c
@@ -38,8 +38,23 @@
       (*(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
  * not reading from a standard C stream, you should create a replacement
@@ -177,4 +192,14 @@
    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 */
diff --git a/pngrutil.c b/pngrutil.c
index 1e2db31..7796887 100644
--- a/pngrutil.c
+++ b/pngrutil.c
@@ -144,13 +144,14 @@
    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 @@
 
    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 @@
 
    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 @@
    }
 }
 
+#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)