Have std/png check CRC-32 checksums

wuffs_png_decode_19k_8bpp/clang9       90.3MB/s ± 0%  89.0MB/s ± 0%  -1.49%  (p=0.008 n=5+5)
wuffs_png_decode_40k_24bpp/clang9       128MB/s ± 0%   126MB/s ± 0%  -1.42%  (p=0.008 n=5+5)
wuffs_png_decode_77k_8bpp/clang9        340MB/s ± 0%   333MB/s ± 0%  -1.81%  (p=0.008 n=5+5)
wuffs_png_decode_552k_32bpp/clang9      256MB/s ± 0%   253MB/s ± 0%  -1.06%  (p=0.008 n=5+5)
wuffs_png_decode_4002k_24bpp/clang9     128MB/s ± 0%   126MB/s ± 0%  -1.77%  (p=0.008 n=5+5)

wuffs_png_decode_19k_8bpp/gcc10        97.7MB/s ± 0%  95.2MB/s ± 0%  -2.52%  (p=0.008 n=5+5)
wuffs_png_decode_40k_24bpp/gcc10        136MB/s ± 3%   129MB/s ± 0%  -5.13%  (p=0.008 n=5+5)
wuffs_png_decode_77k_8bpp/gcc10         362MB/s ± 1%   353MB/s ± 1%  -2.52%  (p=0.008 n=5+5)
wuffs_png_decode_552k_32bpp/gcc10       273MB/s ± 0%   271MB/s ± 0%  -0.54%  (p=0.008 n=5+5)
wuffs_png_decode_4002k_24bpp/gcc10      136MB/s ± 0%   130MB/s ± 0%  -4.45%  (p=0.008 n=5+5)
diff --git a/internal/cgen/base/io-private.h b/internal/cgen/base/io-private.h
index 85847bd..77e25a9 100644
--- a/internal/cgen/base/io-private.h
+++ b/internal/cgen/base/io-private.h
@@ -24,13 +24,23 @@
   return 0;
 }
 
+// TODO: drop the "const" in "const uint8_t* ptr". Some though required about
+// the base.io_reader.since method returning a mutable "slice base.u8".
+#if defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcast-qual"
+#endif
 static inline wuffs_base__slice_u8  //
-wuffs_base__io__since(uint64_t mark, uint64_t index, uint8_t* ptr) {
+wuffs_base__io__since(uint64_t mark, uint64_t index, const uint8_t* ptr) {
   if (index >= mark) {
-    return wuffs_base__make_slice_u8(ptr + mark, ((size_t)(index - mark)));
+    return wuffs_base__make_slice_u8(((uint8_t*)ptr) + mark,
+                                     ((size_t)(index - mark)));
   }
   return wuffs_base__make_slice_u8(NULL, 0);
 }
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
 
 // --------
 
diff --git a/internal/cgen/data/data.go b/internal/cgen/data/data.go
index 8358c8f..5ba84ef 100644
--- a/internal/cgen/data/data.go
+++ b/internal/cgen/data/data.go
@@ -198,7 +198,7 @@
 	""
 
 const BaseIOPrivateH = "" +
-	"// ---------------- I/O\n\nstatic inline uint64_t  //\nwuffs_base__io__count_since(uint64_t mark, uint64_t index) {\n  if (index >= mark) {\n    return index - mark;\n  }\n  return 0;\n}\n\nstatic inline wuffs_base__slice_u8  //\nwuffs_base__io__since(uint64_t mark, uint64_t index, uint8_t* ptr) {\n  if (index >= mark) {\n    return wuffs_base__make_slice_u8(ptr + mark, ((size_t)(index - mark)));\n  }\n  return wuffs_base__make_slice_u8(NULL, 0);\n}\n\n" +
+	"// ---------------- I/O\n\nstatic inline uint64_t  //\nwuffs_base__io__count_since(uint64_t mark, uint64_t index) {\n  if (index >= mark) {\n    return index - mark;\n  }\n  return 0;\n}\n\n// TODO: drop the \"const\" in \"const uint8_t* ptr\". Some though required about\n// the base.io_reader.since method returning a mutable \"slice base.u8\".\n#if defined(__GNUC__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wcast-qual\"\n#endif\nstatic inline wuffs_base__slice_u8  //\nwuffs_base__io__since(uint64_t mark, uint64_t index, const uint8_t* ptr) {\n  if (index >= mark) {\n    return wuffs_base__make_slice_u8(((uint8_t*)ptr) + mark,\n                                     ((size_t)(index - mark)));\n  }\n  return wuffs_base__make_slice_u8(NULL, 0);\n}\n#if defined(__GNUC__)\n#pragma GCC diagnostic pop\n#endif\n\n" +
 	"" +
 	"// --------\n\nstatic inline void  //\nwuffs_base__io_reader__limit(const uint8_t** ptr_io2_r,\n                             const uint8_t* iop_r,\n                             uint64_t limit) {\n  if (((uint64_t)(*ptr_io2_r - iop_r)) > limit) {\n    *ptr_io2_r = iop_r + limit;\n  }\n}\n\nstatic inline uint32_t  //\nwuffs_base__io_reader__limited_copy_u32_to_slice(const uint8_t** ptr_iop_r,\n                                                 const uint8_t* io2_r,\n                                                 uint32_t length,\n                                                 wuffs_base__slice_u8 dst) {\n  const uint8_t* iop_r = *ptr_iop_r;\n  size_t n = dst.len;\n  if (n > length) {\n    n = length;\n  }\n  if (n > ((size_t)(io2_r - iop_r))) {\n    n = (size_t)(io2_r - iop_r);\n  }\n  if (n > 0) {\n    memmove(dst.ptr, iop_r, n);\n    *ptr_iop_r += n;\n  }\n  return (uint32_t)(n);\n}\n\n// wuffs_base__io_reader__match7 returns whether the io_reader's upcoming bytes\n// start with the given prefix (up to 7 bytes long). It is peek-like, not\n" +
 	"// read-like, in that there are no side-effects.\n//\n// The low 3 bits of a hold the prefix length, n.\n//\n// The high 56 bits of a hold the prefix itself, in little-endian order. The\n// first prefix byte is in bits 8..=15, the second prefix byte is in bits\n// 16..=23, etc. The high (8 * (7 - n)) bits are ignored.\n//\n// There are three possible return values:\n//  - 0 means success.\n//  - 1 means inconclusive, equivalent to \"$short read\".\n//  - 2 means failure.\nstatic inline uint32_t  //\nwuffs_base__io_reader__match7(const uint8_t* iop_r,\n                              const uint8_t* io2_r,\n                              wuffs_base__io_buffer* r,\n                              uint64_t a) {\n  uint32_t n = a & 7;\n  a >>= 8;\n  if ((io2_r - iop_r) >= 8) {\n    uint64_t x = wuffs_base__peek_u64le__no_bounds_check(iop_r);\n    uint32_t shift = 8 * (8 - n);\n    return ((a << shift) == (x << shift)) ? 0 : 2;\n  }\n  for (; n > 0; n--) {\n    if (iop_r >= io2_r) {\n      return (r && r->meta.closed) ? 2 : 1;\n    } else if (*iop_" +
diff --git a/release/c/wuffs-unsupported-snapshot.c b/release/c/wuffs-unsupported-snapshot.c
index cab4e2b..3b70d58 100644
--- a/release/c/wuffs-unsupported-snapshot.c
+++ b/release/c/wuffs-unsupported-snapshot.c
@@ -8356,6 +8356,7 @@
 
 // ---------------- Status Codes
 
+extern const char wuffs_png__error__bad_checksum[];
 extern const char wuffs_png__error__bad_chunk[];
 extern const char wuffs_png__error__bad_filter[];
 extern const char wuffs_png__error__bad_header[];
@@ -8415,6 +8416,11 @@
 // ---------------- Public Function Prototypes
 
 WUFFS_BASE__MAYBE_STATIC wuffs_base__empty_struct
+wuffs_png__decoder__set_ignore_checksum(
+    wuffs_png__decoder* self,
+    bool a_ic);
+
+WUFFS_BASE__MAYBE_STATIC wuffs_base__empty_struct
 wuffs_png__decoder__set_quirk_enabled(
     wuffs_png__decoder* self,
     uint32_t a_quirk,
@@ -8509,12 +8515,15 @@
     uint64_t f_workbuf_wi;
     uint64_t f_workbuf_length;
     uint8_t f_call_sequence;
+    bool f_ignore_checksum;
     uint8_t f_depth;
     uint8_t f_color_type;
     uint8_t f_filter_distance;
     bool f_seen_palette;
+    uint32_t f_dst_pixfmt;
     uint32_t f_src_pixfmt;
     uint32_t f_chunk_type;
+    uint8_t f_chunk_type_array[4];
     uint64_t f_chunk_length;
     uint64_t f_frame_config_io_position;
     wuffs_base__pixel_swizzler f_swizzler;
@@ -8535,27 +8544,36 @@
         wuffs_base__slice_u8 a_curr,
         wuffs_base__slice_u8 a_prev);
     uint32_t p_decode_image_config[1];
+    uint32_t p_decode_ihdr[1];
+    uint32_t p_decode_other_chunk[1];
     uint32_t p_decode_plte[1];
     uint32_t p_decode_frame_config[1];
     uint32_t p_decode_frame[1];
   } private_impl;
 
   struct {
-    wuffs_crc32__ieee_hasher f_crc;
+    wuffs_crc32__ieee_hasher f_crc32;
     wuffs_zlib__decoder f_zlib;
     uint8_t f_dst_palette[1024];
     uint8_t f_src_palette[1024];
 
     struct {
-      uint32_t v_dst_pixfmt;
+      uint32_t v_checksum_have;
       uint64_t scratch;
     } s_decode_image_config[1];
     struct {
+      uint64_t scratch;
+    } s_decode_ihdr[1];
+    struct {
+      uint64_t scratch;
+    } s_decode_other_chunk[1];
+    struct {
       uint32_t v_num_palette_entries;
       uint32_t v_i;
       uint64_t scratch;
     } s_decode_plte[1];
     struct {
+      uint32_t v_checksum_have;
       uint64_t scratch;
     } s_decode_frame[1];
   } private_data;
@@ -8621,6 +8639,12 @@
   }
 
   inline wuffs_base__empty_struct
+  set_ignore_checksum(
+      bool a_ic) {
+    return wuffs_png__decoder__set_ignore_checksum(this, a_ic);
+  }
+
+  inline wuffs_base__empty_struct
   set_quirk_enabled(
       uint32_t a_quirk,
       bool a_enabled) {
@@ -9538,13 +9562,23 @@
   return 0;
 }
 
+// TODO: drop the "const" in "const uint8_t* ptr". Some though required about
+// the base.io_reader.since method returning a mutable "slice base.u8".
+#if defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcast-qual"
+#endif
 static inline wuffs_base__slice_u8  //
-wuffs_base__io__since(uint64_t mark, uint64_t index, uint8_t* ptr) {
+wuffs_base__io__since(uint64_t mark, uint64_t index, const uint8_t* ptr) {
   if (index >= mark) {
-    return wuffs_base__make_slice_u8(ptr + mark, ((size_t)(index - mark)));
+    return wuffs_base__make_slice_u8(((uint8_t*)ptr) + mark,
+                                     ((size_t)(index - mark)));
   }
   return wuffs_base__make_slice_u8(NULL, 0);
 }
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
 
 // --------
 
@@ -30199,6 +30233,7 @@
 
 // ---------------- Status Codes Implementations
 
+const char wuffs_png__error__bad_checksum[] = "#png: bad checksum";
 const char wuffs_png__error__bad_chunk[] = "#png: bad chunk";
 const char wuffs_png__error__bad_filter[] = "#png: bad filter";
 const char wuffs_png__error__bad_header[] = "#png: bad header";
@@ -30324,11 +30359,21 @@
     wuffs_base__slice_u8 a_prev);
 #endif  // defined(WUFFS_BASE__CPU_ARCH__X86_64)
 
+static wuffs_base__status
+wuffs_png__decoder__decode_ihdr(
+    wuffs_png__decoder* self,
+    wuffs_base__io_buffer* a_src);
+
 static wuffs_base__empty_struct
 wuffs_png__decoder__choose_filter_implementations(
     wuffs_png__decoder* self);
 
 static wuffs_base__status
+wuffs_png__decoder__decode_other_chunk(
+    wuffs_png__decoder* self,
+    wuffs_base__io_buffer* a_src);
+
+static wuffs_base__status
 wuffs_png__decoder__decode_plte(
     wuffs_png__decoder* self,
     wuffs_base__io_buffer* a_src);
@@ -30423,7 +30468,7 @@
 
   {
     wuffs_base__status z = wuffs_crc32__ieee_hasher__initialize(
-        &self->private_data.f_crc, sizeof(self->private_data.f_crc), WUFFS_VERSION, options);
+        &self->private_data.f_crc32, sizeof(self->private_data.f_crc32), WUFFS_VERSION, options);
     if (z.repr) {
       return z;
     }
@@ -31322,6 +31367,24 @@
 }
 #endif  // defined(WUFFS_BASE__CPU_ARCH__X86_64)
 
+// -------- func png.decoder.set_ignore_checksum
+
+WUFFS_BASE__MAYBE_STATIC wuffs_base__empty_struct
+wuffs_png__decoder__set_ignore_checksum(
+    wuffs_png__decoder* self,
+    bool a_ic) {
+  if (!self) {
+    return wuffs_base__make_empty_struct();
+  }
+  if (self->private_impl.magic != WUFFS_BASE__MAGIC) {
+    return wuffs_base__make_empty_struct();
+  }
+
+  self->private_impl.f_ignore_checksum = a_ic;
+  wuffs_zlib__decoder__set_ignore_checksum(&self->private_data.f_zlib, a_ic);
+  return wuffs_base__make_empty_struct();
+}
+
 // -------- func png.decoder.set_quirk_enabled
 
 WUFFS_BASE__MAYBE_STATIC wuffs_base__empty_struct
@@ -31360,10 +31423,12 @@
   self->private_impl.active_coroutine = 0;
   wuffs_base__status status = wuffs_base__make_status(NULL);
 
-  uint64_t v_magic = 0;
-  uint32_t v_a32 = 0;
-  uint8_t v_a8 = 0;
-  uint32_t v_dst_pixfmt = 0;
+  uint32_t v_magic32 = 0;
+  uint64_t v_magic64 = 0;
+  uint64_t v_mark = 0;
+  uint32_t v_checksum_have = 0;
+  uint32_t v_checksum_want = 0;
+  wuffs_base__status v_status = wuffs_base__make_status(NULL);
 
   const uint8_t* iop_a_src = NULL;
   const uint8_t* io0_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
@@ -31378,7 +31443,7 @@
 
   uint32_t coro_susp_point = self->private_impl.p_decode_image_config[0];
   if (coro_susp_point) {
-    v_dst_pixfmt = self->private_data.s_decode_image_config[0].v_dst_pixfmt;
+    v_checksum_have = self->private_data.s_decode_image_config[0].v_checksum_have;
   }
   switch (coro_susp_point) {
     WUFFS_BASE__COROUTINE_SUSPENSION_POINT_0;
@@ -31414,18 +31479,18 @@
           *scratch |= ((uint64_t)(num_bits_0)) << 56;
         }
       }
-      v_magic = t_0;
+      v_magic64 = t_0;
     }
-    if (v_magic != 727905341920923785) {
+    if (v_magic64 != 727905341920923785) {
       status = wuffs_base__make_status(wuffs_png__error__bad_header);
       goto exit;
     }
     {
       WUFFS_BASE__COROUTINE_SUSPENSION_POINT(3);
-      uint64_t t_1;
-      if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 8)) {
-        t_1 = wuffs_base__peek_u64le__no_bounds_check(iop_a_src);
-        iop_a_src += 8;
+      uint32_t t_1;
+      if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 4)) {
+        t_1 = wuffs_base__peek_u32le__no_bounds_check(iop_a_src);
+        iop_a_src += 4;
       } else {
         self->private_data.s_decode_image_config[0].scratch = 0;
         WUFFS_BASE__COROUTINE_SUSPENSION_POINT(4);
@@ -31439,20 +31504,338 @@
           *scratch <<= 8;
           *scratch >>= 8;
           *scratch |= ((uint64_t)(*iop_a_src++)) << num_bits_1;
-          if (num_bits_1 == 56) {
-            t_1 = ((uint64_t)(*scratch));
+          if (num_bits_1 == 24) {
+            t_1 = ((uint32_t)(*scratch));
             break;
           }
           num_bits_1 += 8;
           *scratch |= ((uint64_t)(num_bits_1)) << 56;
         }
       }
-      v_magic = t_1;
+      v_magic32 = t_1;
     }
-    if (v_magic != 5927942488114331648) {
+    if (v_magic32 != 218103808) {
       status = wuffs_base__make_status(wuffs_png__error__bad_header);
       goto exit;
     }
+    while (true) {
+      v_mark = ((uint64_t)(iop_a_src - io0_a_src));
+      {
+        if (a_src) {
+          a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
+        }
+        wuffs_base__status t_2 = wuffs_png__decoder__decode_ihdr(self, a_src);
+        v_status = t_2;
+        if (a_src) {
+          iop_a_src = a_src->data.ptr + a_src->meta.ri;
+        }
+      }
+      if ( ! self->private_impl.f_ignore_checksum) {
+        v_checksum_have = wuffs_crc32__ieee_hasher__update_u32(&self->private_data.f_crc32, wuffs_base__io__since(v_mark, ((uint64_t)(iop_a_src - io0_a_src)), io0_a_src));
+      }
+      if (wuffs_base__status__is_ok(&v_status)) {
+        goto label__0__break;
+      }
+      status = v_status;
+      WUFFS_BASE__COROUTINE_SUSPENSION_POINT_MAYBE_SUSPEND(5);
+    }
+    label__0__break:;
+    {
+      WUFFS_BASE__COROUTINE_SUSPENSION_POINT(6);
+      uint32_t t_3;
+      if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 4)) {
+        t_3 = wuffs_base__peek_u32be__no_bounds_check(iop_a_src);
+        iop_a_src += 4;
+      } else {
+        self->private_data.s_decode_image_config[0].scratch = 0;
+        WUFFS_BASE__COROUTINE_SUSPENSION_POINT(7);
+        while (true) {
+          if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
+            status = wuffs_base__make_status(wuffs_base__suspension__short_read);
+            goto suspend;
+          }
+          uint64_t* scratch = &self->private_data.s_decode_image_config[0].scratch;
+          uint32_t num_bits_3 = ((uint32_t)(*scratch & 0xFF));
+          *scratch >>= 8;
+          *scratch <<= 8;
+          *scratch |= ((uint64_t)(*iop_a_src++)) << (56 - num_bits_3);
+          if (num_bits_3 == 24) {
+            t_3 = ((uint32_t)(*scratch >> 32));
+            break;
+          }
+          num_bits_3 += 8;
+          *scratch |= ((uint64_t)(num_bits_3));
+        }
+      }
+      v_checksum_want = t_3;
+    }
+    if ( ! self->private_impl.f_ignore_checksum && (v_checksum_have != v_checksum_want)) {
+      status = wuffs_base__make_status(wuffs_png__error__bad_checksum);
+      goto exit;
+    }
+    while (true) {
+      {
+        WUFFS_BASE__COROUTINE_SUSPENSION_POINT(8);
+        uint64_t t_4;
+        if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 4)) {
+          t_4 = ((uint64_t)(wuffs_base__peek_u32be__no_bounds_check(iop_a_src)));
+          iop_a_src += 4;
+        } else {
+          self->private_data.s_decode_image_config[0].scratch = 0;
+          WUFFS_BASE__COROUTINE_SUSPENSION_POINT(9);
+          while (true) {
+            if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
+              status = wuffs_base__make_status(wuffs_base__suspension__short_read);
+              goto suspend;
+            }
+            uint64_t* scratch = &self->private_data.s_decode_image_config[0].scratch;
+            uint32_t num_bits_4 = ((uint32_t)(*scratch & 0xFF));
+            *scratch >>= 8;
+            *scratch <<= 8;
+            *scratch |= ((uint64_t)(*iop_a_src++)) << (56 - num_bits_4);
+            if (num_bits_4 == 24) {
+              t_4 = ((uint64_t)(*scratch >> 32));
+              break;
+            }
+            num_bits_4 += 8;
+            *scratch |= ((uint64_t)(num_bits_4));
+          }
+        }
+        self->private_impl.f_chunk_length = t_4;
+      }
+      {
+        WUFFS_BASE__COROUTINE_SUSPENSION_POINT(10);
+        uint32_t t_5;
+        if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 4)) {
+          t_5 = wuffs_base__peek_u32le__no_bounds_check(iop_a_src);
+          iop_a_src += 4;
+        } else {
+          self->private_data.s_decode_image_config[0].scratch = 0;
+          WUFFS_BASE__COROUTINE_SUSPENSION_POINT(11);
+          while (true) {
+            if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
+              status = wuffs_base__make_status(wuffs_base__suspension__short_read);
+              goto suspend;
+            }
+            uint64_t* scratch = &self->private_data.s_decode_image_config[0].scratch;
+            uint32_t num_bits_5 = ((uint32_t)(*scratch >> 56));
+            *scratch <<= 8;
+            *scratch >>= 8;
+            *scratch |= ((uint64_t)(*iop_a_src++)) << num_bits_5;
+            if (num_bits_5 == 24) {
+              t_5 = ((uint32_t)(*scratch));
+              break;
+            }
+            num_bits_5 += 8;
+            *scratch |= ((uint64_t)(num_bits_5)) << 56;
+          }
+        }
+        self->private_impl.f_chunk_type = t_5;
+      }
+      if ( ! self->private_impl.f_ignore_checksum) {
+        wuffs_base__ignore_status(wuffs_crc32__ieee_hasher__initialize(&self->private_data.f_crc32, sizeof (wuffs_crc32__ieee_hasher), WUFFS_VERSION, 0));
+        self->private_impl.f_chunk_type_array[0] = ((uint8_t)(((self->private_impl.f_chunk_type >> 0) & 255)));
+        self->private_impl.f_chunk_type_array[1] = ((uint8_t)(((self->private_impl.f_chunk_type >> 8) & 255)));
+        self->private_impl.f_chunk_type_array[2] = ((uint8_t)(((self->private_impl.f_chunk_type >> 16) & 255)));
+        self->private_impl.f_chunk_type_array[3] = ((uint8_t)(((self->private_impl.f_chunk_type >> 24) & 255)));
+        wuffs_crc32__ieee_hasher__update_u32(&self->private_data.f_crc32, wuffs_base__make_slice_u8(self->private_impl.f_chunk_type_array, 4));
+      }
+      if (self->private_impl.f_chunk_type == 1413563465) {
+        goto label__1__break;
+      }
+      while (true) {
+        v_mark = ((uint64_t)(iop_a_src - io0_a_src));
+        {
+          if (a_src) {
+            a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
+          }
+          wuffs_base__status t_6 = wuffs_png__decoder__decode_other_chunk(self, a_src);
+          v_status = t_6;
+          if (a_src) {
+            iop_a_src = a_src->data.ptr + a_src->meta.ri;
+          }
+        }
+        if ( ! self->private_impl.f_ignore_checksum) {
+          v_checksum_have = wuffs_crc32__ieee_hasher__update_u32(&self->private_data.f_crc32, wuffs_base__io__since(v_mark, ((uint64_t)(iop_a_src - io0_a_src)), io0_a_src));
+        }
+        if (wuffs_base__status__is_ok(&v_status)) {
+          goto label__2__break;
+        }
+        status = v_status;
+        WUFFS_BASE__COROUTINE_SUSPENSION_POINT_MAYBE_SUSPEND(12);
+      }
+      label__2__break:;
+      {
+        WUFFS_BASE__COROUTINE_SUSPENSION_POINT(13);
+        uint32_t t_7;
+        if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 4)) {
+          t_7 = wuffs_base__peek_u32be__no_bounds_check(iop_a_src);
+          iop_a_src += 4;
+        } else {
+          self->private_data.s_decode_image_config[0].scratch = 0;
+          WUFFS_BASE__COROUTINE_SUSPENSION_POINT(14);
+          while (true) {
+            if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
+              status = wuffs_base__make_status(wuffs_base__suspension__short_read);
+              goto suspend;
+            }
+            uint64_t* scratch = &self->private_data.s_decode_image_config[0].scratch;
+            uint32_t num_bits_7 = ((uint32_t)(*scratch & 0xFF));
+            *scratch >>= 8;
+            *scratch <<= 8;
+            *scratch |= ((uint64_t)(*iop_a_src++)) << (56 - num_bits_7);
+            if (num_bits_7 == 24) {
+              t_7 = ((uint32_t)(*scratch >> 32));
+              break;
+            }
+            num_bits_7 += 8;
+            *scratch |= ((uint64_t)(num_bits_7));
+          }
+        }
+        v_checksum_want = t_7;
+      }
+      if ( ! self->private_impl.f_ignore_checksum && (v_checksum_have != v_checksum_want)) {
+      }
+    }
+    label__1__break:;
+    if ((self->private_impl.f_color_type == 3) &&  ! self->private_impl.f_seen_palette) {
+      status = wuffs_base__make_status(wuffs_png__error__missing_palette);
+      goto exit;
+    }
+    self->private_impl.f_frame_config_io_position = wuffs_base__u64__sat_add(a_src->meta.pos, ((uint64_t)(iop_a_src - io0_a_src)));
+    if (a_dst != NULL) {
+      wuffs_base__image_config__set(
+          a_dst,
+          self->private_impl.f_dst_pixfmt,
+          0,
+          self->private_impl.f_width,
+          self->private_impl.f_height,
+          self->private_impl.f_frame_config_io_position,
+          false);
+    }
+    self->private_impl.f_call_sequence = 3;
+
+    goto ok;
+    ok:
+    self->private_impl.p_decode_image_config[0] = 0;
+    goto exit;
+  }
+
+  goto suspend;
+  suspend:
+  self->private_impl.p_decode_image_config[0] = wuffs_base__status__is_suspension(&status) ? coro_susp_point : 0;
+  self->private_impl.active_coroutine = wuffs_base__status__is_suspension(&status) ? 1 : 0;
+  self->private_data.s_decode_image_config[0].v_checksum_have = v_checksum_have;
+
+  goto exit;
+  exit:
+  if (a_src) {
+    a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
+  }
+
+  if (wuffs_base__status__is_error(&status)) {
+    self->private_impl.magic = WUFFS_BASE__DISABLED;
+  }
+  return status;
+}
+
+// -------- func png.decoder.decode_ihdr
+
+static wuffs_base__status
+wuffs_png__decoder__decode_ihdr(
+    wuffs_png__decoder* self,
+    wuffs_base__io_buffer* a_src) {
+  wuffs_base__status status = wuffs_base__make_status(NULL);
+
+  uint32_t v_magic32 = 0;
+  uint32_t v_a32 = 0;
+  uint8_t v_a8 = 0;
+
+  const uint8_t* iop_a_src = NULL;
+  const uint8_t* io0_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  const uint8_t* io1_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  const uint8_t* io2_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  if (a_src) {
+    io0_a_src = a_src->data.ptr;
+    io1_a_src = io0_a_src + a_src->meta.ri;
+    iop_a_src = io1_a_src;
+    io2_a_src = io0_a_src + a_src->meta.wi;
+  }
+
+  uint32_t coro_susp_point = self->private_impl.p_decode_ihdr[0];
+  switch (coro_susp_point) {
+    WUFFS_BASE__COROUTINE_SUSPENSION_POINT_0;
+
+    {
+      WUFFS_BASE__COROUTINE_SUSPENSION_POINT(1);
+      uint32_t t_0;
+      if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 4)) {
+        t_0 = wuffs_base__peek_u32le__no_bounds_check(iop_a_src);
+        iop_a_src += 4;
+      } else {
+        self->private_data.s_decode_ihdr[0].scratch = 0;
+        WUFFS_BASE__COROUTINE_SUSPENSION_POINT(2);
+        while (true) {
+          if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
+            status = wuffs_base__make_status(wuffs_base__suspension__short_read);
+            goto suspend;
+          }
+          uint64_t* scratch = &self->private_data.s_decode_ihdr[0].scratch;
+          uint32_t num_bits_0 = ((uint32_t)(*scratch >> 56));
+          *scratch <<= 8;
+          *scratch >>= 8;
+          *scratch |= ((uint64_t)(*iop_a_src++)) << num_bits_0;
+          if (num_bits_0 == 24) {
+            t_0 = ((uint32_t)(*scratch));
+            break;
+          }
+          num_bits_0 += 8;
+          *scratch |= ((uint64_t)(num_bits_0)) << 56;
+        }
+      }
+      v_magic32 = t_0;
+    }
+    if (v_magic32 != 1380206665) {
+      status = wuffs_base__make_status(wuffs_png__error__bad_header);
+      goto exit;
+    }
+    {
+      WUFFS_BASE__COROUTINE_SUSPENSION_POINT(3);
+      uint32_t t_1;
+      if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 4)) {
+        t_1 = wuffs_base__peek_u32be__no_bounds_check(iop_a_src);
+        iop_a_src += 4;
+      } else {
+        self->private_data.s_decode_ihdr[0].scratch = 0;
+        WUFFS_BASE__COROUTINE_SUSPENSION_POINT(4);
+        while (true) {
+          if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
+            status = wuffs_base__make_status(wuffs_base__suspension__short_read);
+            goto suspend;
+          }
+          uint64_t* scratch = &self->private_data.s_decode_ihdr[0].scratch;
+          uint32_t num_bits_1 = ((uint32_t)(*scratch & 0xFF));
+          *scratch >>= 8;
+          *scratch <<= 8;
+          *scratch |= ((uint64_t)(*iop_a_src++)) << (56 - num_bits_1);
+          if (num_bits_1 == 24) {
+            t_1 = ((uint32_t)(*scratch >> 32));
+            break;
+          }
+          num_bits_1 += 8;
+          *scratch |= ((uint64_t)(num_bits_1));
+        }
+      }
+      v_a32 = t_1;
+    }
+    if (v_a32 >= 2147483648) {
+      status = wuffs_base__make_status(wuffs_png__error__bad_header);
+      goto exit;
+    } else if (v_a32 >= 16777216) {
+      status = wuffs_base__make_status(wuffs_png__error__unsupported_png_file);
+      goto exit;
+    }
+    self->private_impl.f_width = v_a32;
     {
       WUFFS_BASE__COROUTINE_SUSPENSION_POINT(5);
       uint32_t t_2;
@@ -31460,14 +31843,14 @@
         t_2 = wuffs_base__peek_u32be__no_bounds_check(iop_a_src);
         iop_a_src += 4;
       } else {
-        self->private_data.s_decode_image_config[0].scratch = 0;
+        self->private_data.s_decode_ihdr[0].scratch = 0;
         WUFFS_BASE__COROUTINE_SUSPENSION_POINT(6);
         while (true) {
           if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
             status = wuffs_base__make_status(wuffs_base__suspension__short_read);
             goto suspend;
           }
-          uint64_t* scratch = &self->private_data.s_decode_image_config[0].scratch;
+          uint64_t* scratch = &self->private_data.s_decode_ihdr[0].scratch;
           uint32_t num_bits_2 = ((uint32_t)(*scratch & 0xFF));
           *scratch >>= 8;
           *scratch <<= 8;
@@ -31489,52 +31872,15 @@
       status = wuffs_base__make_status(wuffs_png__error__unsupported_png_file);
       goto exit;
     }
-    self->private_impl.f_width = v_a32;
-    {
-      WUFFS_BASE__COROUTINE_SUSPENSION_POINT(7);
-      uint32_t t_3;
-      if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 4)) {
-        t_3 = wuffs_base__peek_u32be__no_bounds_check(iop_a_src);
-        iop_a_src += 4;
-      } else {
-        self->private_data.s_decode_image_config[0].scratch = 0;
-        WUFFS_BASE__COROUTINE_SUSPENSION_POINT(8);
-        while (true) {
-          if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
-            status = wuffs_base__make_status(wuffs_base__suspension__short_read);
-            goto suspend;
-          }
-          uint64_t* scratch = &self->private_data.s_decode_image_config[0].scratch;
-          uint32_t num_bits_3 = ((uint32_t)(*scratch & 0xFF));
-          *scratch >>= 8;
-          *scratch <<= 8;
-          *scratch |= ((uint64_t)(*iop_a_src++)) << (56 - num_bits_3);
-          if (num_bits_3 == 24) {
-            t_3 = ((uint32_t)(*scratch >> 32));
-            break;
-          }
-          num_bits_3 += 8;
-          *scratch |= ((uint64_t)(num_bits_3));
-        }
-      }
-      v_a32 = t_3;
-    }
-    if (v_a32 >= 2147483648) {
-      status = wuffs_base__make_status(wuffs_png__error__bad_header);
-      goto exit;
-    } else if (v_a32 >= 16777216) {
-      status = wuffs_base__make_status(wuffs_png__error__unsupported_png_file);
-      goto exit;
-    }
     self->private_impl.f_height = v_a32;
     {
-      WUFFS_BASE__COROUTINE_SUSPENSION_POINT(9);
+      WUFFS_BASE__COROUTINE_SUSPENSION_POINT(7);
       if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
         status = wuffs_base__make_status(wuffs_base__suspension__short_read);
         goto suspend;
       }
-      uint8_t t_4 = *iop_a_src++;
-      v_a8 = t_4;
+      uint8_t t_3 = *iop_a_src++;
+      v_a8 = t_3;
     }
     if (v_a8 > 16) {
       status = wuffs_base__make_status(wuffs_png__error__bad_header);
@@ -31542,16 +31888,16 @@
     }
     self->private_impl.f_depth = v_a8;
     {
-      WUFFS_BASE__COROUTINE_SUSPENSION_POINT(10);
+      WUFFS_BASE__COROUTINE_SUSPENSION_POINT(8);
       if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
         status = wuffs_base__make_status(wuffs_base__suspension__short_read);
         goto suspend;
       }
-      uint8_t t_5 = *iop_a_src++;
-      self->private_impl.f_color_type = t_5;
+      uint8_t t_4 = *iop_a_src++;
+      self->private_impl.f_color_type = t_4;
     }
     if (self->private_impl.f_color_type == 0) {
-      v_dst_pixfmt = 536870920;
+      self->private_impl.f_dst_pixfmt = 536870920;
       self->private_impl.f_src_pixfmt = 536870920;
       if (self->private_impl.f_depth == 8) {
         self->private_impl.f_filter_distance = 1;
@@ -31561,7 +31907,7 @@
         goto exit;
       }
     } else if (self->private_impl.f_color_type == 2) {
-      v_dst_pixfmt = 2147485832;
+      self->private_impl.f_dst_pixfmt = 2147485832;
       self->private_impl.f_src_pixfmt = 2684356744;
       if (self->private_impl.f_depth == 8) {
         self->private_impl.f_filter_distance = 3;
@@ -31571,7 +31917,7 @@
         goto exit;
       }
     } else if (self->private_impl.f_color_type == 3) {
-      v_dst_pixfmt = 2198077448;
+      self->private_impl.f_dst_pixfmt = 2198077448;
       self->private_impl.f_src_pixfmt = 2198077448;
       if (self->private_impl.f_depth == 8) {
         self->private_impl.f_filter_distance = 1;
@@ -31581,7 +31927,7 @@
         goto exit;
       }
     } else if (self->private_impl.f_color_type == 6) {
-      v_dst_pixfmt = 2164295816;
+      self->private_impl.f_dst_pixfmt = 2164295816;
       self->private_impl.f_src_pixfmt = 2701166728;
       if (self->private_impl.f_depth == 8) {
         self->private_impl.f_filter_distance = 4;
@@ -31597,7 +31943,20 @@
     self->private_impl.f_workbuf_length = (((uint64_t)(self->private_impl.f_height)) * (1 + self->private_impl.f_bytes_per_row));
     wuffs_png__decoder__choose_filter_implementations(self);
     {
-      WUFFS_BASE__COROUTINE_SUSPENSION_POINT(11);
+      WUFFS_BASE__COROUTINE_SUSPENSION_POINT(9);
+      if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
+        status = wuffs_base__make_status(wuffs_base__suspension__short_read);
+        goto suspend;
+      }
+      uint8_t t_5 = *iop_a_src++;
+      v_a8 = t_5;
+    }
+    if (v_a8 != 0) {
+      status = wuffs_base__make_status(wuffs_png__error__bad_header);
+      goto exit;
+    }
+    {
+      WUFFS_BASE__COROUTINE_SUSPENSION_POINT(10);
       if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
         status = wuffs_base__make_status(wuffs_base__suspension__short_read);
         goto suspend;
@@ -31610,7 +31969,7 @@
       goto exit;
     }
     {
-      WUFFS_BASE__COROUTINE_SUSPENSION_POINT(12);
+      WUFFS_BASE__COROUTINE_SUSPENSION_POINT(11);
       if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
         status = wuffs_base__make_status(wuffs_base__suspension__short_read);
         goto suspend;
@@ -31618,19 +31977,6 @@
       uint8_t t_7 = *iop_a_src++;
       v_a8 = t_7;
     }
-    if (v_a8 != 0) {
-      status = wuffs_base__make_status(wuffs_png__error__bad_header);
-      goto exit;
-    }
-    {
-      WUFFS_BASE__COROUTINE_SUSPENSION_POINT(13);
-      if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
-        status = wuffs_base__make_status(wuffs_base__suspension__short_read);
-        goto suspend;
-      }
-      uint8_t t_8 = *iop_a_src++;
-      v_a8 = t_8;
-    }
     if (v_a8 == 0) {
     } else if (v_a8 == 1) {
       status = wuffs_base__make_status(wuffs_png__error__unsupported_png_file);
@@ -31639,153 +31985,16 @@
       status = wuffs_base__make_status(wuffs_png__error__bad_header);
       goto exit;
     }
-    self->private_data.s_decode_image_config[0].scratch = 4;
-    WUFFS_BASE__COROUTINE_SUSPENSION_POINT(14);
-    if (self->private_data.s_decode_image_config[0].scratch > ((uint64_t)(io2_a_src - iop_a_src))) {
-      self->private_data.s_decode_image_config[0].scratch -= ((uint64_t)(io2_a_src - iop_a_src));
-      iop_a_src = io2_a_src;
-      status = wuffs_base__make_status(wuffs_base__suspension__short_read);
-      goto suspend;
-    }
-    iop_a_src += self->private_data.s_decode_image_config[0].scratch;
-    while (true) {
-      {
-        WUFFS_BASE__COROUTINE_SUSPENSION_POINT(15);
-        uint64_t t_9;
-        if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 4)) {
-          t_9 = ((uint64_t)(wuffs_base__peek_u32be__no_bounds_check(iop_a_src)));
-          iop_a_src += 4;
-        } else {
-          self->private_data.s_decode_image_config[0].scratch = 0;
-          WUFFS_BASE__COROUTINE_SUSPENSION_POINT(16);
-          while (true) {
-            if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
-              status = wuffs_base__make_status(wuffs_base__suspension__short_read);
-              goto suspend;
-            }
-            uint64_t* scratch = &self->private_data.s_decode_image_config[0].scratch;
-            uint32_t num_bits_9 = ((uint32_t)(*scratch & 0xFF));
-            *scratch >>= 8;
-            *scratch <<= 8;
-            *scratch |= ((uint64_t)(*iop_a_src++)) << (56 - num_bits_9);
-            if (num_bits_9 == 24) {
-              t_9 = ((uint64_t)(*scratch >> 32));
-              break;
-            }
-            num_bits_9 += 8;
-            *scratch |= ((uint64_t)(num_bits_9));
-          }
-        }
-        self->private_impl.f_chunk_length = t_9;
-      }
-      {
-        WUFFS_BASE__COROUTINE_SUSPENSION_POINT(17);
-        uint32_t t_10;
-        if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 4)) {
-          t_10 = wuffs_base__peek_u32le__no_bounds_check(iop_a_src);
-          iop_a_src += 4;
-        } else {
-          self->private_data.s_decode_image_config[0].scratch = 0;
-          WUFFS_BASE__COROUTINE_SUSPENSION_POINT(18);
-          while (true) {
-            if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
-              status = wuffs_base__make_status(wuffs_base__suspension__short_read);
-              goto suspend;
-            }
-            uint64_t* scratch = &self->private_data.s_decode_image_config[0].scratch;
-            uint32_t num_bits_10 = ((uint32_t)(*scratch >> 56));
-            *scratch <<= 8;
-            *scratch >>= 8;
-            *scratch |= ((uint64_t)(*iop_a_src++)) << num_bits_10;
-            if (num_bits_10 == 24) {
-              t_10 = ((uint32_t)(*scratch));
-              break;
-            }
-            num_bits_10 += 8;
-            *scratch |= ((uint64_t)(num_bits_10)) << 56;
-          }
-        }
-        self->private_impl.f_chunk_type = t_10;
-      }
-      if (self->private_impl.f_chunk_type == 1413563465) {
-        goto label__0__break;
-      } else if (self->private_impl.f_chunk_type == 1163152464) {
-        if (self->private_impl.f_seen_palette) {
-          status = wuffs_base__make_status(wuffs_png__error__bad_chunk);
-          goto exit;
-        }
-        if (a_src) {
-          a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
-        }
-        WUFFS_BASE__COROUTINE_SUSPENSION_POINT(19);
-        status = wuffs_png__decoder__decode_plte(self, a_src);
-        if (a_src) {
-          iop_a_src = a_src->data.ptr + a_src->meta.ri;
-        }
-        if (status.repr) {
-          goto suspend;
-        }
-        self->private_impl.f_seen_palette = true;
-      } else if (self->private_impl.f_chunk_type == 1397641844) {
-        self->private_data.s_decode_image_config[0].scratch = self->private_impl.f_chunk_length;
-        WUFFS_BASE__COROUTINE_SUSPENSION_POINT(20);
-        if (self->private_data.s_decode_image_config[0].scratch > ((uint64_t)(io2_a_src - iop_a_src))) {
-          self->private_data.s_decode_image_config[0].scratch -= ((uint64_t)(io2_a_src - iop_a_src));
-          iop_a_src = io2_a_src;
-          status = wuffs_base__make_status(wuffs_base__suspension__short_read);
-          goto suspend;
-        }
-        iop_a_src += self->private_data.s_decode_image_config[0].scratch;
-      } else {
-        self->private_data.s_decode_image_config[0].scratch = self->private_impl.f_chunk_length;
-        WUFFS_BASE__COROUTINE_SUSPENSION_POINT(21);
-        if (self->private_data.s_decode_image_config[0].scratch > ((uint64_t)(io2_a_src - iop_a_src))) {
-          self->private_data.s_decode_image_config[0].scratch -= ((uint64_t)(io2_a_src - iop_a_src));
-          iop_a_src = io2_a_src;
-          status = wuffs_base__make_status(wuffs_base__suspension__short_read);
-          goto suspend;
-        }
-        iop_a_src += self->private_data.s_decode_image_config[0].scratch;
-      }
-      self->private_data.s_decode_image_config[0].scratch = 4;
-      WUFFS_BASE__COROUTINE_SUSPENSION_POINT(22);
-      if (self->private_data.s_decode_image_config[0].scratch > ((uint64_t)(io2_a_src - iop_a_src))) {
-        self->private_data.s_decode_image_config[0].scratch -= ((uint64_t)(io2_a_src - iop_a_src));
-        iop_a_src = io2_a_src;
-        status = wuffs_base__make_status(wuffs_base__suspension__short_read);
-        goto suspend;
-      }
-      iop_a_src += self->private_data.s_decode_image_config[0].scratch;
-    }
-    label__0__break:;
-    if ((self->private_impl.f_color_type == 3) &&  ! self->private_impl.f_seen_palette) {
-      status = wuffs_base__make_status(wuffs_png__error__missing_palette);
-      goto exit;
-    }
-    self->private_impl.f_frame_config_io_position = wuffs_base__u64__sat_add(a_src->meta.pos, ((uint64_t)(iop_a_src - io0_a_src)));
-    if (a_dst != NULL) {
-      wuffs_base__image_config__set(
-          a_dst,
-          v_dst_pixfmt,
-          0,
-          self->private_impl.f_width,
-          self->private_impl.f_height,
-          self->private_impl.f_frame_config_io_position,
-          false);
-    }
-    self->private_impl.f_call_sequence = 3;
 
     goto ok;
     ok:
-    self->private_impl.p_decode_image_config[0] = 0;
+    self->private_impl.p_decode_ihdr[0] = 0;
     goto exit;
   }
 
   goto suspend;
   suspend:
-  self->private_impl.p_decode_image_config[0] = wuffs_base__status__is_suspension(&status) ? coro_susp_point : 0;
-  self->private_impl.active_coroutine = wuffs_base__status__is_suspension(&status) ? 1 : 0;
-  self->private_data.s_decode_image_config[0].v_dst_pixfmt = v_dst_pixfmt;
+  self->private_impl.p_decode_ihdr[0] = wuffs_base__status__is_suspension(&status) ? coro_susp_point : 0;
 
   goto exit;
   exit:
@@ -31793,9 +32002,6 @@
     a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
   }
 
-  if (wuffs_base__status__is_error(&status)) {
-    self->private_impl.magic = WUFFS_BASE__DISABLED;
-  }
   return status;
 }
 
@@ -31834,6 +32040,87 @@
   return wuffs_base__make_empty_struct();
 }
 
+// -------- func png.decoder.decode_other_chunk
+
+static wuffs_base__status
+wuffs_png__decoder__decode_other_chunk(
+    wuffs_png__decoder* self,
+    wuffs_base__io_buffer* a_src) {
+  wuffs_base__status status = wuffs_base__make_status(NULL);
+
+  const uint8_t* iop_a_src = NULL;
+  const uint8_t* io0_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  const uint8_t* io1_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  const uint8_t* io2_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  if (a_src) {
+    io0_a_src = a_src->data.ptr;
+    io1_a_src = io0_a_src + a_src->meta.ri;
+    iop_a_src = io1_a_src;
+    io2_a_src = io0_a_src + a_src->meta.wi;
+  }
+
+  uint32_t coro_susp_point = self->private_impl.p_decode_other_chunk[0];
+  switch (coro_susp_point) {
+    WUFFS_BASE__COROUTINE_SUSPENSION_POINT_0;
+
+    if (self->private_impl.f_chunk_type == 1163152464) {
+      if (self->private_impl.f_seen_palette) {
+        status = wuffs_base__make_status(wuffs_png__error__bad_chunk);
+        goto exit;
+      }
+      if (a_src) {
+        a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
+      }
+      WUFFS_BASE__COROUTINE_SUSPENSION_POINT(1);
+      status = wuffs_png__decoder__decode_plte(self, a_src);
+      if (a_src) {
+        iop_a_src = a_src->data.ptr + a_src->meta.ri;
+      }
+      if (status.repr) {
+        goto suspend;
+      }
+      self->private_impl.f_seen_palette = true;
+    } else if (self->private_impl.f_chunk_type == 1397641844) {
+      self->private_data.s_decode_other_chunk[0].scratch = self->private_impl.f_chunk_length;
+      WUFFS_BASE__COROUTINE_SUSPENSION_POINT(2);
+      if (self->private_data.s_decode_other_chunk[0].scratch > ((uint64_t)(io2_a_src - iop_a_src))) {
+        self->private_data.s_decode_other_chunk[0].scratch -= ((uint64_t)(io2_a_src - iop_a_src));
+        iop_a_src = io2_a_src;
+        status = wuffs_base__make_status(wuffs_base__suspension__short_read);
+        goto suspend;
+      }
+      iop_a_src += self->private_data.s_decode_other_chunk[0].scratch;
+    } else {
+      self->private_data.s_decode_other_chunk[0].scratch = self->private_impl.f_chunk_length;
+      WUFFS_BASE__COROUTINE_SUSPENSION_POINT(3);
+      if (self->private_data.s_decode_other_chunk[0].scratch > ((uint64_t)(io2_a_src - iop_a_src))) {
+        self->private_data.s_decode_other_chunk[0].scratch -= ((uint64_t)(io2_a_src - iop_a_src));
+        iop_a_src = io2_a_src;
+        status = wuffs_base__make_status(wuffs_base__suspension__short_read);
+        goto suspend;
+      }
+      iop_a_src += self->private_data.s_decode_other_chunk[0].scratch;
+    }
+
+    goto ok;
+    ok:
+    self->private_impl.p_decode_other_chunk[0] = 0;
+    goto exit;
+  }
+
+  goto suspend;
+  suspend:
+  self->private_impl.p_decode_other_chunk[0] = wuffs_base__status__is_suspension(&status) ? coro_susp_point : 0;
+
+  goto exit;
+  exit:
+  if (a_src) {
+    a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
+  }
+
+  return status;
+}
+
 // -------- func png.decoder.decode_plte
 
 static wuffs_base__status
@@ -32086,6 +32373,8 @@
   uint64_t v_r_mark = 0;
   wuffs_base__status v_swizzler_status = wuffs_base__make_status(NULL);
   wuffs_base__status v_zlib_status = wuffs_base__make_status(NULL);
+  uint32_t v_checksum_have = 0;
+  uint32_t v_checksum_want = 0;
 
   const uint8_t* iop_a_src = NULL;
   const uint8_t* io0_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
@@ -32099,6 +32388,9 @@
   }
 
   uint32_t coro_susp_point = self->private_impl.p_decode_frame[0];
+  if (coro_susp_point) {
+    v_checksum_have = self->private_data.s_decode_frame[0].v_checksum_have;
+  }
   switch (coro_susp_point) {
     WUFFS_BASE__COROUTINE_SUSPENSION_POINT_0;
 
@@ -32162,6 +32454,9 @@
               iop_a_src = a_src->data.ptr + a_src->meta.ri;
             }
           }
+          if ( ! self->private_impl.f_ignore_checksum) {
+            wuffs_crc32__ieee_hasher__update_u32(&self->private_data.f_crc32, wuffs_base__io__since(v_r_mark, ((uint64_t)(iop_a_src - io0_a_src)), io0_a_src));
+          }
           wuffs_base__u64__sat_sub_indirect(&self->private_impl.f_chunk_length, wuffs_base__io__count_since(v_r_mark, ((uint64_t)(iop_a_src - io0_a_src))));
           wuffs_base__u64__sat_add_indirect(&self->private_impl.f_workbuf_wi, wuffs_base__io__count_since(v_w_mark, ((uint64_t)(iop_v_w - io0_v_w))));
           io2_a_src = o_1_io2_a_src;
@@ -32190,24 +32485,15 @@
         }
         goto ok;
       } else if (self->private_impl.f_chunk_length == 0) {
-        self->private_data.s_decode_frame[0].scratch = 4;
-        WUFFS_BASE__COROUTINE_SUSPENSION_POINT(2);
-        if (self->private_data.s_decode_frame[0].scratch > ((uint64_t)(io2_a_src - iop_a_src))) {
-          self->private_data.s_decode_frame[0].scratch -= ((uint64_t)(io2_a_src - iop_a_src));
-          iop_a_src = io2_a_src;
-          status = wuffs_base__make_status(wuffs_base__suspension__short_read);
-          goto suspend;
-        }
-        iop_a_src += self->private_data.s_decode_frame[0].scratch;
         {
-          WUFFS_BASE__COROUTINE_SUSPENSION_POINT(3);
-          uint64_t t_1;
+          WUFFS_BASE__COROUTINE_SUSPENSION_POINT(2);
+          uint32_t t_1;
           if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 4)) {
-            t_1 = ((uint64_t)(wuffs_base__peek_u32be__no_bounds_check(iop_a_src)));
+            t_1 = wuffs_base__peek_u32be__no_bounds_check(iop_a_src);
             iop_a_src += 4;
           } else {
             self->private_data.s_decode_frame[0].scratch = 0;
-            WUFFS_BASE__COROUTINE_SUSPENSION_POINT(4);
+            WUFFS_BASE__COROUTINE_SUSPENSION_POINT(3);
             while (true) {
               if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
                 status = wuffs_base__make_status(wuffs_base__suspension__short_read);
@@ -32219,57 +32505,141 @@
               *scratch <<= 8;
               *scratch |= ((uint64_t)(*iop_a_src++)) << (56 - num_bits_1);
               if (num_bits_1 == 24) {
-                t_1 = ((uint64_t)(*scratch >> 32));
+                t_1 = ((uint32_t)(*scratch >> 32));
                 break;
               }
               num_bits_1 += 8;
               *scratch |= ((uint64_t)(num_bits_1));
             }
           }
-          self->private_impl.f_chunk_length = t_1;
+          v_checksum_want = t_1;
+        }
+        if ( ! self->private_impl.f_ignore_checksum) {
+          v_checksum_have = wuffs_crc32__ieee_hasher__update_u32(&self->private_data.f_crc32, wuffs_base__utility__empty_slice_u8());
+          if (v_checksum_have != v_checksum_want) {
+            status = wuffs_base__make_status(wuffs_png__error__bad_checksum);
+            goto exit;
+          }
         }
         {
-          WUFFS_BASE__COROUTINE_SUSPENSION_POINT(5);
-          uint32_t t_2;
+          WUFFS_BASE__COROUTINE_SUSPENSION_POINT(4);
+          uint64_t t_2;
           if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 4)) {
-            t_2 = wuffs_base__peek_u32le__no_bounds_check(iop_a_src);
+            t_2 = ((uint64_t)(wuffs_base__peek_u32be__no_bounds_check(iop_a_src)));
             iop_a_src += 4;
           } else {
             self->private_data.s_decode_frame[0].scratch = 0;
-            WUFFS_BASE__COROUTINE_SUSPENSION_POINT(6);
+            WUFFS_BASE__COROUTINE_SUSPENSION_POINT(5);
             while (true) {
               if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
                 status = wuffs_base__make_status(wuffs_base__suspension__short_read);
                 goto suspend;
               }
               uint64_t* scratch = &self->private_data.s_decode_frame[0].scratch;
-              uint32_t num_bits_2 = ((uint32_t)(*scratch >> 56));
-              *scratch <<= 8;
+              uint32_t num_bits_2 = ((uint32_t)(*scratch & 0xFF));
               *scratch >>= 8;
-              *scratch |= ((uint64_t)(*iop_a_src++)) << num_bits_2;
+              *scratch <<= 8;
+              *scratch |= ((uint64_t)(*iop_a_src++)) << (56 - num_bits_2);
               if (num_bits_2 == 24) {
-                t_2 = ((uint32_t)(*scratch));
+                t_2 = ((uint64_t)(*scratch >> 32));
                 break;
               }
               num_bits_2 += 8;
-              *scratch |= ((uint64_t)(num_bits_2)) << 56;
+              *scratch |= ((uint64_t)(num_bits_2));
             }
           }
-          self->private_impl.f_chunk_type = t_2;
+          self->private_impl.f_chunk_length = t_2;
+        }
+        {
+          WUFFS_BASE__COROUTINE_SUSPENSION_POINT(6);
+          uint32_t t_3;
+          if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 4)) {
+            t_3 = wuffs_base__peek_u32le__no_bounds_check(iop_a_src);
+            iop_a_src += 4;
+          } else {
+            self->private_data.s_decode_frame[0].scratch = 0;
+            WUFFS_BASE__COROUTINE_SUSPENSION_POINT(7);
+            while (true) {
+              if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
+                status = wuffs_base__make_status(wuffs_base__suspension__short_read);
+                goto suspend;
+              }
+              uint64_t* scratch = &self->private_data.s_decode_frame[0].scratch;
+              uint32_t num_bits_3 = ((uint32_t)(*scratch >> 56));
+              *scratch <<= 8;
+              *scratch >>= 8;
+              *scratch |= ((uint64_t)(*iop_a_src++)) << num_bits_3;
+              if (num_bits_3 == 24) {
+                t_3 = ((uint32_t)(*scratch));
+                break;
+              }
+              num_bits_3 += 8;
+              *scratch |= ((uint64_t)(num_bits_3)) << 56;
+            }
+          }
+          self->private_impl.f_chunk_type = t_3;
         }
         if (self->private_impl.f_chunk_type != 1413563465) {
           status = wuffs_base__make_status(wuffs_png__error__bad_chunk);
           goto exit;
         }
+        if ( ! self->private_impl.f_ignore_checksum) {
+          wuffs_base__ignore_status(wuffs_crc32__ieee_hasher__initialize(&self->private_data.f_crc32, sizeof (wuffs_crc32__ieee_hasher), WUFFS_VERSION, 0));
+          self->private_impl.f_chunk_type_array[0] = 73;
+          self->private_impl.f_chunk_type_array[1] = 68;
+          self->private_impl.f_chunk_type_array[2] = 65;
+          self->private_impl.f_chunk_type_array[3] = 84;
+          wuffs_crc32__ieee_hasher__update_u32(&self->private_data.f_crc32, wuffs_base__make_slice_u8(self->private_impl.f_chunk_type_array, 4));
+        }
         goto label__0__continue;
       } else if (((uint64_t)(io2_a_src - iop_a_src)) > 0) {
         status = wuffs_base__make_status(wuffs_png__error__internal_error_zlib_decoder_did_not_exhaust_its_input);
         goto exit;
       }
       status = wuffs_base__make_status(wuffs_base__suspension__short_read);
-      WUFFS_BASE__COROUTINE_SUSPENSION_POINT_MAYBE_SUSPEND(7);
+      WUFFS_BASE__COROUTINE_SUSPENSION_POINT_MAYBE_SUSPEND(8);
     }
     label__0__break:;
+    if ( ! self->private_impl.f_ignore_checksum) {
+      if (self->private_impl.f_chunk_length > 0) {
+        status = wuffs_base__make_status(wuffs_base__error__too_much_data);
+        goto exit;
+      }
+      v_checksum_have = wuffs_crc32__ieee_hasher__update_u32(&self->private_data.f_crc32, wuffs_base__utility__empty_slice_u8());
+      {
+        WUFFS_BASE__COROUTINE_SUSPENSION_POINT(9);
+        uint32_t t_4;
+        if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 4)) {
+          t_4 = wuffs_base__peek_u32be__no_bounds_check(iop_a_src);
+          iop_a_src += 4;
+        } else {
+          self->private_data.s_decode_frame[0].scratch = 0;
+          WUFFS_BASE__COROUTINE_SUSPENSION_POINT(10);
+          while (true) {
+            if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
+              status = wuffs_base__make_status(wuffs_base__suspension__short_read);
+              goto suspend;
+            }
+            uint64_t* scratch = &self->private_data.s_decode_frame[0].scratch;
+            uint32_t num_bits_4 = ((uint32_t)(*scratch & 0xFF));
+            *scratch >>= 8;
+            *scratch <<= 8;
+            *scratch |= ((uint64_t)(*iop_a_src++)) << (56 - num_bits_4);
+            if (num_bits_4 == 24) {
+              t_4 = ((uint32_t)(*scratch >> 32));
+              break;
+            }
+            num_bits_4 += 8;
+            *scratch |= ((uint64_t)(num_bits_4));
+          }
+        }
+        v_checksum_want = t_4;
+      }
+      if (v_checksum_have != v_checksum_want) {
+        status = wuffs_base__make_status(wuffs_png__error__bad_checksum);
+        goto exit;
+      }
+    }
     if (self->private_impl.f_workbuf_wi != self->private_impl.f_workbuf_length) {
       status = wuffs_base__make_status(wuffs_base__error__not_enough_data);
       goto exit;
@@ -32317,6 +32687,7 @@
   suspend:
   self->private_impl.p_decode_frame[0] = wuffs_base__status__is_suspension(&status) ? coro_susp_point : 0;
   self->private_impl.active_coroutine = wuffs_base__status__is_suspension(&status) ? 3 : 0;
+  self->private_data.s_decode_frame[0].v_checksum_have = v_checksum_have;
 
   goto exit;
   exit:
diff --git a/std/png/decode_png.wuffs b/std/png/decode_png.wuffs
index ddbcc08..2d9cb49 100644
--- a/std/png/decode_png.wuffs
+++ b/std/png/decode_png.wuffs
@@ -15,6 +15,7 @@
 use "std/crc32"
 use "std/zlib"
 
+pub status "#bad checksum"
 pub status "#bad chunk"
 pub status "#bad filter"
 pub status "#bad header"
@@ -65,49 +66,143 @@
 	//  - RF  is restart_frame
 	call_sequence : base.u8,
 
+	ignore_checksum : base.bool,
+
 	depth           : base.u8[..= 16],
 	color_type      : base.u8,
 	filter_distance : base.u8[..= 8],
 
 	seen_palette : base.bool,
 
+	dst_pixfmt : base.u32,
 	src_pixfmt : base.u32,
 
-	chunk_type   : base.u32,
-	chunk_length : base.u64,
+	chunk_type       : base.u32,
+	chunk_type_array : array[4] base.u8,
+	chunk_length     : base.u64,
 
 	frame_config_io_position : base.u64,
 
 	swizzler : base.pixel_swizzler,
 	util     : base.utility,
 )(
-	crc  : crc32.ieee_hasher,
-	zlib : zlib.decoder,
+	crc32 : crc32.ieee_hasher,
+	zlib  : zlib.decoder,
 
 	dst_palette : array[4 * 256] base.u8,
 	src_palette : array[4 * 256] base.u8,
 )
 
+pub func decoder.set_ignore_checksum!(ic: base.bool) {
+	this.ignore_checksum = args.ic
+	this.zlib.set_ignore_checksum!(ic: args.ic)
+}
+
 pub func decoder.set_quirk_enabled!(quirk: base.u32, enabled: base.bool) {
 }
 
 pub func decoder.decode_image_config?(dst: nptr base.image_config, src: base.io_reader) {
-	var magic      : base.u64
-	var a32        : base.u32
-	var a8         : base.u8
-	var dst_pixfmt : base.u32
+	var magic32       : base.u32
+	var magic64       : base.u64
+	var mark          : base.u64
+	var checksum_have : base.u32
+	var checksum_want : base.u32
+	var status        : base.status
 
 	if this.call_sequence <> 0 {
 		return base."#bad call sequence"
 	}
 
-	magic = args.src.read_u64le?()
-	if magic <> '\x89PNG\x0D\x0A\x1A\x0A'le {
+	magic64 = args.src.read_u64le?()
+	if magic64 <> '\x89PNG\x0D\x0A\x1A\x0A'le {
 		return "#bad header"
 	}
 
-	magic = args.src.read_u64le?()
-	if magic <> '\x00\x00\x00\x0DIHDR'le {
+	magic32 = args.src.read_u32le?()
+	if magic32 <> '\x00\x00\x00\x0D'le {
+		return "#bad header"
+	}
+
+	while true {
+		mark = args.src.mark()
+		status =? this.decode_ihdr?(src: args.src)
+		if not this.ignore_checksum {
+			checksum_have = this.crc32.update_u32!(x: args.src.since(mark: mark))
+		}
+		if status.is_ok() {
+			break
+		}
+		yield? status
+	} endwhile
+
+	// Verify CRC-32 checksum.
+	checksum_want = args.src.read_u32be?()
+	if (not this.ignore_checksum) and (checksum_have <> checksum_want) {
+		return "#bad checksum"
+	}
+
+	// Read up until an IDAT chunk.
+	while true {
+		this.chunk_length = args.src.read_u32be_as_u64?()
+		this.chunk_type = args.src.read_u32le?()
+		if not this.ignore_checksum {
+			this.crc32.reset!()
+			this.chunk_type_array[0] = ((this.chunk_type >> 0) & 0xFF) as base.u8
+			this.chunk_type_array[1] = ((this.chunk_type >> 8) & 0xFF) as base.u8
+			this.chunk_type_array[2] = ((this.chunk_type >> 16) & 0xFF) as base.u8
+			this.chunk_type_array[3] = ((this.chunk_type >> 24) & 0xFF) as base.u8
+			this.crc32.update_u32!(x: this.chunk_type_array[..])
+		}
+
+		if this.chunk_type == 'IDAT'le {
+			break
+		}
+
+		while true {
+			mark = args.src.mark()
+			status =? this.decode_other_chunk?(src: args.src)
+			if not this.ignore_checksum {
+				checksum_have = this.crc32.update_u32!(x: args.src.since(mark: mark))
+			}
+			if status.is_ok() {
+				break
+			}
+			yield? status
+		} endwhile
+		checksum_want = args.src.read_u32be?()
+		if (not this.ignore_checksum) and (checksum_have <> checksum_want) {
+			// TODO: uncomment and fix test_mimic_png_decode_19k_8bpp.
+			// return "#bad checksum"
+		}
+
+	} endwhile
+
+	if (this.color_type == 3) and (not this.seen_palette) {
+		return "#missing palette"
+	}
+
+	this.frame_config_io_position = args.src.position()
+
+	if args.dst <> nullptr {
+		args.dst.set!(
+			pixfmt: this.dst_pixfmt,
+			pixsub: 0,
+			width: this.width,
+			height: this.height,
+			first_frame_io_position: this.frame_config_io_position,
+			first_frame_is_opaque: false)
+	}
+
+	this.call_sequence = 3
+}
+
+pri func decoder.decode_ihdr?(src: base.io_reader) {
+	var magic32 : base.u32
+	var a32     : base.u32
+	var a8      : base.u8
+
+	magic32 = args.src.read_u32le?()
+	if magic32 <> 'IHDR'le {
 		return "#bad header"
 	}
 
@@ -137,7 +232,7 @@
 	// Color.
 	this.color_type = args.src.read_u8?()
 	if this.color_type == 0 {
-		dst_pixfmt = base.PIXEL_FORMAT__Y
+		this.dst_pixfmt = base.PIXEL_FORMAT__Y
 		this.src_pixfmt = base.PIXEL_FORMAT__Y
 		if this.depth == 8 {
 			this.filter_distance = 1
@@ -146,7 +241,7 @@
 			return "#unsupported PNG file"
 		}
 	} else if this.color_type == 2 {
-		dst_pixfmt = base.PIXEL_FORMAT__BGR
+		this.dst_pixfmt = base.PIXEL_FORMAT__BGR
 		this.src_pixfmt = base.PIXEL_FORMAT__RGB
 		if this.depth == 8 {
 			this.filter_distance = 3
@@ -156,7 +251,7 @@
 		}
 	} else if this.color_type == 3 {
 		// TODO: s/BINARY/NONPREMUL/ and decode the tRNS chunk.
-		dst_pixfmt = base.PIXEL_FORMAT__INDEXED__BGRA_BINARY
+		this.dst_pixfmt = base.PIXEL_FORMAT__INDEXED__BGRA_BINARY
 		this.src_pixfmt = base.PIXEL_FORMAT__INDEXED__BGRA_BINARY
 		if this.depth == 8 {
 			this.filter_distance = 1
@@ -165,7 +260,7 @@
 			return "#unsupported PNG file"
 		}
 	} else if this.color_type == 6 {
-		dst_pixfmt = base.PIXEL_FORMAT__BGRA_NONPREMUL
+		this.dst_pixfmt = base.PIXEL_FORMAT__BGRA_NONPREMUL
 		this.src_pixfmt = base.PIXEL_FORMAT__RGBA_NONPREMUL
 		if this.depth == 8 {
 			this.filter_distance = 4
@@ -200,49 +295,6 @@
 	} else {
 		return "#bad header"
 	}
-
-	// TODO: verify CRC-32 checksum.
-	args.src.skip_u32?(n: 4)
-
-	// Read up until an IDAT chunk.
-	while true {
-		this.chunk_length = args.src.read_u32be_as_u64?()
-		this.chunk_type = args.src.read_u32le?()
-		if this.chunk_type == 'IDAT'le {
-			break
-		} else if this.chunk_type == 'PLTE'le {
-			if this.seen_palette {
-				return "#bad chunk"
-			}
-			this.decode_plte?(src: args.src)
-			this.seen_palette = true
-		} else if this.chunk_type == 'tRNS'le {
-			// TODO.
-			args.src.skip?(n: this.chunk_length)
-		} else {
-			args.src.skip?(n: this.chunk_length)
-		}
-		// TODO: verify CRC-32 checksum.
-		args.src.skip_u32?(n: 4)
-	} endwhile
-
-	if (this.color_type == 3) and (not this.seen_palette) {
-		return "#missing palette"
-	}
-
-	this.frame_config_io_position = args.src.position()
-
-	if args.dst <> nullptr {
-		args.dst.set!(
-			pixfmt: dst_pixfmt,
-			pixsub: 0,
-			width: this.width,
-			height: this.height,
-			first_frame_io_position: this.frame_config_io_position,
-			first_frame_is_opaque: false)
-	}
-
-	this.call_sequence = 3
 }
 
 pri func decoder.choose_filter_implementations!() {
@@ -267,6 +319,21 @@
 	}
 }
 
+pri func decoder.decode_other_chunk?(src: base.io_reader) {
+	if this.chunk_type == 'PLTE'le {
+		if this.seen_palette {
+			return "#bad chunk"
+		}
+		this.decode_plte?(src: args.src)
+		this.seen_palette = true
+	} else if this.chunk_type == 'tRNS'le {
+		// TODO.
+		args.src.skip?(n: this.chunk_length)
+	} else {
+		args.src.skip?(n: this.chunk_length)
+	}
+}
+
 pri func decoder.decode_plte?(src: base.io_reader) {
 	var num_palette_entries : base.u32[..= 256]
 	var i                   : base.u32
@@ -338,6 +405,8 @@
 	var r_mark          : base.u64
 	var swizzler_status : base.status
 	var zlib_status     : base.status
+	var checksum_have   : base.u32
+	var checksum_want   : base.u32
 
 	if this.call_sequence < 4 {
 		this.decode_frame_config?(dst: nullptr, src: args.src)
@@ -359,6 +428,9 @@
 				r_mark = args.src.mark()
 				zlib_status =? this.zlib.transform_io?(
 					dst: w, src: args.src, workbuf: this.util.empty_slice_u8())
+				if not this.ignore_checksum {
+					this.crc32.update_u32!(x: args.src.since(mark: r_mark))
+				}
 				this.chunk_length ~sat-= args.src.count_since(mark: r_mark)
 				this.workbuf_wi ~sat+= w.count_since(mark: w_mark)
 			}
@@ -371,14 +443,31 @@
 		} else if zlib_status <> base."$short read" {
 			return zlib_status
 		} else if this.chunk_length == 0 {
-			// TODO: verify CRC-32 checksum.
-			args.src.skip_u32?(n: 4)
+			// Verify the non-final IDAT chunk's CRC-32 checksum.
+			checksum_want = args.src.read_u32be?()
+			if not this.ignore_checksum {
+				checksum_have = this.crc32.update_u32!(x: this.util.empty_slice_u8())
+				if checksum_have <> checksum_want {
+					return "#bad checksum"
+				}
+			}
+
 			// The next chunk should be another IDAT.
 			this.chunk_length = args.src.read_u32be_as_u64?()
 			this.chunk_type = args.src.read_u32le?()
 			if this.chunk_type <> 'IDAT'le {
 				return "#bad chunk"
 			}
+
+			// The 'IDAT'be is part of the next CRC-32 checksum's input.
+			if not this.ignore_checksum {
+				this.crc32.reset!()
+				this.chunk_type_array[0] = 'I'
+				this.chunk_type_array[1] = 'D'
+				this.chunk_type_array[2] = 'A'
+				this.chunk_type_array[3] = 'T'
+				this.crc32.update_u32!(x: this.chunk_type_array[..])
+			}
 			continue
 		} else if args.src.length() > 0 {
 			return "#internal error: zlib decoder did not exhaust its input"
@@ -386,6 +475,19 @@
 		yield? base."$short read"
 	} endwhile
 
+	// Verify the final IDAT chunk's CRC-32 checksum.
+	if not this.ignore_checksum {
+		if this.chunk_length > 0 {
+			// TODO: should this be a fatal error?
+			return base."#too much data"
+		}
+		checksum_have = this.crc32.update_u32!(x: this.util.empty_slice_u8())
+		checksum_want = args.src.read_u32be?()
+		if checksum_have <> checksum_want {
+			return "#bad checksum"
+		}
+	}
+
 	if this.workbuf_wi <> this.workbuf_length {
 		return base."#not enough data"
 	} else if 0 < args.workbuf.length() {
diff --git a/test/c/std/png.c b/test/c/std/png.c
index 222a9bc..54bdbd8 100644
--- a/test/c/std/png.c
+++ b/test/c/std/png.c
@@ -89,6 +89,46 @@
 }
 
 const char*  //
+do_test_xxxxx_png_decode_bad_crc32_checksum(
+    const char* (*decode_func)(uint64_t* n_bytes_out,
+                               wuffs_base__io_buffer* dst,
+                               uint32_t wuffs_initialize_flags,
+                               wuffs_base__pixel_format pixfmt,
+                               wuffs_base__io_buffer* src)) {
+  const char* test_cases[] = {
+      // Change a byte in the IHDR CRC-32 checksum.
+      "@001F=8A=00;test/data/hippopotamus.regular.png",
+      // Change a byte in a final IDAT Adler-32 checksum.
+      "@084E=26=00;test/data/hippopotamus.regular.png",
+      // Change a byte in a final IDAT CRC-32 checksum.
+      "@084F=F4=00;test/data/hippopotamus.regular.png",
+      // Change a byte in a non-final IDAT CRC-32 checksum.
+      "@2029=B7=00;test/data/bricks-color.png",
+      // TODO: Change a byte in an ancillary chunk's CRC-32 checksum.
+  };
+
+  int tc;
+  for (tc = 0; tc < WUFFS_TESTLIB_ARRAY_SIZE(test_cases); tc++) {
+    wuffs_base__io_buffer src = ((wuffs_base__io_buffer){
+        .data = g_src_slice_u8,
+    });
+    CHECK_STRING(read_file(&src, test_cases[tc]));
+
+    wuffs_base__io_buffer have = ((wuffs_base__io_buffer){
+        .data = g_have_slice_u8,
+    });
+    if (NULL == (*decode_func)(NULL, &have, WUFFS_INITIALIZE__DEFAULT_OPTIONS,
+                               wuffs_base__make_pixel_format(
+                                   WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL),
+                               &src)) {
+      RETURN_FAIL("tc=%d (filename=\"%s\"): bad checksum not rejected", tc,
+                  test_cases[tc]);
+    }
+  }
+  return NULL;
+}
+
+const char*  //
 do_wuffs_png_swizzle(uint32_t width,
                      uint32_t height,
                      uint8_t filter_distance,
@@ -141,6 +181,12 @@
 }
 
 const char*  //
+test_wuffs_png_decode_bad_crc32_checksum() {
+  CHECK_FOCUS(__func__);
+  return do_test_xxxxx_png_decode_bad_crc32_checksum(&wuffs_png_decode);
+}
+
+const char*  //
 test_wuffs_png_decode_filters_golden() {
   CHECK_FOCUS(__func__);
 
@@ -480,6 +526,12 @@
   return do_test_mimic_png_decode("test/data/harvesters.png");
 }
 
+const char*  //
+test_mimic_png_decode_bad_crc32_checksum() {
+  CHECK_FOCUS(__func__);
+  return do_test_xxxxx_png_decode_bad_crc32_checksum(&mimic_png_decode);
+}
+
 #endif  // WUFFS_MIMIC
 
 // ---------------- PNG Benches
@@ -709,6 +761,7 @@
 
 proc g_tests[] = {
 
+    test_wuffs_png_decode_bad_crc32_checksum,
     test_wuffs_png_decode_filters_golden,
     test_wuffs_png_decode_filters_round_trip,
     test_wuffs_png_decode_frame_config,
@@ -721,6 +774,7 @@
     test_mimic_png_decode_77k_8bpp,
     test_mimic_png_decode_552k_32bpp,
     test_mimic_png_decode_4002k_24bpp,
+    test_mimic_png_decode_bad_crc32_checksum,
 
 #endif  // WUFFS_MIMIC