Let std/png report metadata after the first frame

Fixes #58
diff --git a/internal/cgen/auxiliary/image.cc b/internal/cgen/auxiliary/image.cc
index 1c58016..f46ea9b 100644
--- a/internal/cgen/auxiliary/image.cc
+++ b/internal/cgen/auxiliary/image.cc
@@ -310,6 +310,7 @@
   wuffs_base__image_config image_config = wuffs_base__null_image_config();
   sync_io::DynIOBuffer raw_metadata_buf(max_incl_metadata_length);
   uint64_t start_pos = io_buf.reader_position();
+  bool interested_in_metadata_after_the_frame = false;
   bool redirected = false;
   int32_t fourcc = 0;
 redirect:
@@ -384,6 +385,7 @@
         image_decoder->set_report_metadata(WUFFS_BASE__FOURCC__CHRM, true);
       }
       if (flags & DecodeImageArgFlags::REPORT_METADATA_EXIF) {
+        interested_in_metadata_after_the_frame = true;
         image_decoder->set_report_metadata(WUFFS_BASE__FOURCC__EXIF, true);
       }
       if (flags & DecodeImageArgFlags::REPORT_METADATA_GAMA) {
@@ -393,12 +395,14 @@
         image_decoder->set_report_metadata(WUFFS_BASE__FOURCC__ICCP, true);
       }
       if (flags & DecodeImageArgFlags::REPORT_METADATA_KVP) {
+        interested_in_metadata_after_the_frame = true;
         image_decoder->set_report_metadata(WUFFS_BASE__FOURCC__KVP, true);
       }
       if (flags & DecodeImageArgFlags::REPORT_METADATA_SRGB) {
         image_decoder->set_report_metadata(WUFFS_BASE__FOURCC__SRGB, true);
       }
       if (flags & DecodeImageArgFlags::REPORT_METADATA_XMP) {
+        interested_in_metadata_after_the_frame = true;
         image_decoder->set_report_metadata(WUFFS_BASE__FOURCC__XMP, true);
       }
     }
@@ -433,7 +437,9 @@
       }
     }
   } while (false);
-  raw_metadata_buf.drop();
+  if (!interested_in_metadata_after_the_frame) {
+    raw_metadata_buf.drop();
+  }
 
   // Select the pixel format.
   uint32_t w = image_config.pixcfg.width();
@@ -494,6 +500,12 @@
         image_decoder->decode_frame_config(&frame_config, &io_buf);
     if (id_dfc_status.repr == nullptr) {
       break;
+    } else if (id_dfc_status.repr == wuffs_base__note__metadata_reported) {
+      std::string error_message = DecodeImageHandleMetadata(
+          image_decoder, callbacks, input, io_buf, raw_metadata_buf);
+      if (!error_message.empty()) {
+        return DecodeImageResult(std::move(error_message));
+      }
     } else if (id_dfc_status.repr != wuffs_base__suspension__short_read) {
       return DecodeImageResult(id_dfc_status.message());
     } else if (io_buf.meta.closed) {
@@ -535,6 +547,35 @@
       }
     }
   }
+
+  // Decode any metadata after the frame.
+  if (interested_in_metadata_after_the_frame) {
+    while (true) {
+      wuffs_base__status id_dfc_status =
+          image_decoder->decode_frame_config(NULL, &io_buf);
+      if (id_dfc_status.repr == wuffs_base__note__end_of_data) {
+        break;
+      } else if (id_dfc_status.repr == nullptr) {
+        continue;
+      } else if (id_dfc_status.repr == wuffs_base__note__metadata_reported) {
+        std::string error_message = DecodeImageHandleMetadata(
+            image_decoder, callbacks, input, io_buf, raw_metadata_buf);
+        if (!error_message.empty()) {
+          return DecodeImageResult(std::move(error_message));
+        }
+      } else if (id_dfc_status.repr != wuffs_base__suspension__short_read) {
+        return DecodeImageResult(id_dfc_status.message());
+      } else if (io_buf.meta.closed) {
+        return DecodeImageResult(DecodeImage_UnexpectedEndOfFile);
+      } else {
+        std::string error_message = input.CopyIn(&io_buf);
+        if (!error_message.empty()) {
+          return DecodeImageResult(std::move(error_message));
+        }
+      }
+    }
+  }
+
   return DecodeImageResult(std::move(alloc_pixbuf_result.mem_owner),
                            pixel_buffer, std::move(message));
 }
diff --git a/release/c/wuffs-unsupported-snapshot.c b/release/c/wuffs-unsupported-snapshot.c
index a34439e..2f0b4ee 100644
--- a/release/c/wuffs-unsupported-snapshot.c
+++ b/release/c/wuffs-unsupported-snapshot.c
@@ -37915,7 +37915,8 @@
 static wuffs_base__status
 wuffs_png__decoder__decode_other_chunk(
     wuffs_png__decoder* self,
-    wuffs_base__io_buffer* a_src);
+    wuffs_base__io_buffer* a_src,
+    bool a_framy);
 
 static wuffs_base__status
 wuffs_png__decoder__decode_actl(
@@ -39772,7 +39773,7 @@
           if (a_src) {
             a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
           }
-          wuffs_base__status t_4 = wuffs_png__decoder__decode_other_chunk(self, a_src);
+          wuffs_base__status t_4 = wuffs_png__decoder__decode_other_chunk(self, a_src, false);
           v_status = t_4;
           if (a_src) {
             iop_a_src = a_src->data.ptr + a_src->meta.ri;
@@ -40234,7 +40235,8 @@
 static wuffs_base__status
 wuffs_png__decoder__decode_other_chunk(
     wuffs_png__decoder* self,
-    wuffs_base__io_buffer* a_src) {
+    wuffs_base__io_buffer* a_src,
+    bool a_framy) {
   wuffs_base__status status = wuffs_base__make_status(NULL);
 
   const uint8_t* iop_a_src = NULL;
@@ -40252,7 +40254,7 @@
   switch (coro_susp_point) {
     WUFFS_BASE__COROUTINE_SUSPENSION_POINT_0;
 
-    if (self->private_impl.f_chunk_type == 1163152464) {
+    if ((self->private_impl.f_chunk_type == 1163152464) &&  ! a_framy) {
       if (self->private_impl.f_seen_plte) {
         status = wuffs_base__make_status(wuffs_png__error__bad_chunk);
         goto exit;
@@ -40279,43 +40281,8 @@
         status = wuffs_base__make_status(wuffs_png__error__bad_chunk);
         goto exit;
       }
-    } else if (self->private_impl.f_chunk_type == 1280598881) {
-      if (self->private_impl.f_seen_actl) {
-        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(2);
-      status = wuffs_png__decoder__decode_actl(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_actl = true;
-    } else if (self->private_impl.f_chunk_type == 1297238115) {
-      if (self->private_impl.f_report_metadata_chrm) {
-        if (self->private_impl.f_seen_chrm) {
-          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(3);
-        status = wuffs_png__decoder__decode_chrm(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_chrm = true;
-      }
-    } else if (self->private_impl.f_chunk_type == 1716082789) {
+    }
+    if (self->private_impl.f_chunk_type == 1716082789) {
       if (self->private_impl.f_report_metadata_exif) {
         if (self->private_impl.f_seen_exif) {
           status = wuffs_base__make_status(wuffs_png__error__bad_chunk);
@@ -40324,7 +40291,7 @@
         if (a_src) {
           a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
         }
-        WUFFS_BASE__COROUTINE_SUSPENSION_POINT(4);
+        WUFFS_BASE__COROUTINE_SUSPENSION_POINT(2);
         status = wuffs_png__decoder__decode_exif(self, a_src);
         if (a_src) {
           iop_a_src = a_src->data.ptr + a_src->meta.ri;
@@ -40334,97 +40301,6 @@
         }
         self->private_impl.f_seen_exif = true;
       }
-    } else if (self->private_impl.f_chunk_type == 1280598886) {
-      if (self->private_impl.f_seen_fctl) {
-        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(5);
-      status = wuffs_png__decoder__decode_fctl(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_fctl = true;
-    } else if (self->private_impl.f_chunk_type == 1095582055) {
-      if (self->private_impl.f_report_metadata_gama) {
-        if (self->private_impl.f_seen_gama) {
-          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(6);
-        status = wuffs_png__decoder__decode_gama(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_gama = true;
-      }
-    } else if (self->private_impl.f_chunk_type == 1346585449) {
-      if (self->private_impl.f_report_metadata_iccp) {
-        if (self->private_impl.f_seen_iccp) {
-          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(7);
-        status = wuffs_png__decoder__decode_iccp(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_iccp = true;
-      }
-    } else if (self->private_impl.f_chunk_type == 1111970419) {
-      if (self->private_impl.f_report_metadata_srgb) {
-        if (self->private_impl.f_seen_srgb) {
-          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(8);
-        status = wuffs_png__decoder__decode_srgb(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_srgb = true;
-      }
-    } else if (self->private_impl.f_chunk_type == 1397641844) {
-      if (self->private_impl.f_seen_trns || (self->private_impl.f_color_type > 3) || ((self->private_impl.f_color_type == 3) &&  ! self->private_impl.f_seen_plte)) {
-        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(9);
-      status = wuffs_png__decoder__decode_trns(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_trns = true;
     } else if ((self->private_impl.f_chunk_type == 1951945833) || (self->private_impl.f_chunk_type == 1951942004) || (self->private_impl.f_chunk_type == 1951945850)) {
       if (self->private_impl.f_report_metadata_kvp) {
         self->private_impl.f_metadata_flavor = 4;
@@ -40433,6 +40309,135 @@
         self->private_impl.f_metadata_y = 0;
         self->private_impl.f_metadata_z = 0;
       }
+    } else if ( ! a_framy) {
+      if (self->private_impl.f_chunk_type == 1280598881) {
+        if (self->private_impl.f_seen_actl) {
+          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(3);
+        status = wuffs_png__decoder__decode_actl(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_actl = true;
+      } else if (self->private_impl.f_chunk_type == 1297238115) {
+        if (self->private_impl.f_report_metadata_chrm) {
+          if (self->private_impl.f_seen_chrm) {
+            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(4);
+          status = wuffs_png__decoder__decode_chrm(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_chrm = true;
+        }
+      } else if (self->private_impl.f_chunk_type == 1280598886) {
+        if (self->private_impl.f_seen_fctl) {
+          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(5);
+        status = wuffs_png__decoder__decode_fctl(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_fctl = true;
+      } else if (self->private_impl.f_chunk_type == 1095582055) {
+        if (self->private_impl.f_report_metadata_gama) {
+          if (self->private_impl.f_seen_gama) {
+            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(6);
+          status = wuffs_png__decoder__decode_gama(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_gama = true;
+        }
+      } else if (self->private_impl.f_chunk_type == 1346585449) {
+        if (self->private_impl.f_report_metadata_iccp) {
+          if (self->private_impl.f_seen_iccp) {
+            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(7);
+          status = wuffs_png__decoder__decode_iccp(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_iccp = true;
+        }
+      } else if (self->private_impl.f_chunk_type == 1111970419) {
+        if (self->private_impl.f_report_metadata_srgb) {
+          if (self->private_impl.f_seen_srgb) {
+            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(8);
+          status = wuffs_png__decoder__decode_srgb(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_srgb = true;
+        }
+      } else if (self->private_impl.f_chunk_type == 1397641844) {
+        if (self->private_impl.f_seen_trns || (self->private_impl.f_color_type > 3) || ((self->private_impl.f_color_type == 3) &&  ! self->private_impl.f_seen_plte)) {
+          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(9);
+        status = wuffs_png__decoder__decode_trns(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_trns = true;
+      }
     }
     if (self->private_impl.f_metadata_fourcc == 0) {
       self->private_data.s_decode_other_chunk[0].scratch = self->private_impl.f_chunk_length;
@@ -41800,6 +41805,8 @@
   self->private_impl.active_coroutine = 0;
   wuffs_base__status status = wuffs_base__make_status(NULL);
 
+  uint32_t v_checksum_have = 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;
@@ -41848,14 +41855,15 @@
       if (status.repr) {
         goto suspend;
       }
-      if (self->private_impl.f_call_sequence >= 96) {
-        status = wuffs_base__make_status(wuffs_base__note__end_of_data);
-        goto ok;
-      }
     } else {
       status = wuffs_base__make_status(wuffs_base__note__end_of_data);
       goto ok;
     }
+    if (self->private_impl.f_metadata_fourcc != 0) {
+      self->private_impl.f_call_sequence = 48;
+      status = wuffs_base__make_status(wuffs_base__note__metadata_reported);
+      goto ok;
+    }
     if (self->private_impl.f_num_decoded_frame_configs_value == 0) {
       self->private_impl.f_frame_rect_x0 = self->private_impl.f_first_rect_x0;
       self->private_impl.f_frame_rect_y0 = self->private_impl.f_first_rect_y0;
@@ -41925,7 +41933,48 @@
           }
           self->private_impl.f_chunk_type = t_1;
         }
-        if (self->private_impl.f_chunk_type == 1413571686) {
+        if (self->private_impl.f_chunk_type == 1145980233) {
+          if (self->private_impl.f_chunk_length != 0) {
+            status = wuffs_base__make_status(wuffs_png__error__bad_chunk);
+            goto exit;
+          }
+          {
+            WUFFS_BASE__COROUTINE_SUSPENSION_POINT(7);
+            uint32_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);
+              iop_a_src += 4;
+            } else {
+              self->private_data.s_decode_frame_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_frame_config[0].scratch;
+                uint32_t num_bits_2 = ((uint32_t)(*scratch >> 56));
+                *scratch <<= 8;
+                *scratch >>= 8;
+                *scratch |= ((uint64_t)(*iop_a_src++)) << num_bits_2;
+                if (num_bits_2 == 24) {
+                  t_2 = ((uint32_t)(*scratch));
+                  break;
+                }
+                num_bits_2 += 8;
+                *scratch |= ((uint64_t)(num_bits_2)) << 56;
+              }
+            }
+            v_checksum_have = t_2;
+          }
+          if ( ! self->private_impl.f_ignore_checksum && (v_checksum_have != 2187346606)) {
+            status = wuffs_base__make_status(wuffs_png__error__bad_checksum);
+            goto exit;
+          }
+          self->private_impl.f_call_sequence = 96;
+          status = wuffs_base__make_status(wuffs_base__note__end_of_data);
+          goto ok;
+        } else if (self->private_impl.f_chunk_type == 1413571686) {
           status = wuffs_base__make_status(wuffs_png__error__bad_chunk);
           goto exit;
         } else if (self->private_impl.f_chunk_type == 1280598886) {
@@ -41933,7 +41982,7 @@
           if (a_src) {
             a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
           }
-          WUFFS_BASE__COROUTINE_SUSPENSION_POINT(7);
+          WUFFS_BASE__COROUTINE_SUSPENSION_POINT(9);
           status = wuffs_png__decoder__decode_fctl(self, a_src);
           if (a_src) {
             iop_a_src = a_src->data.ptr + a_src->meta.ri;
@@ -41942,7 +41991,7 @@
             goto suspend;
           }
           self->private_data.s_decode_frame_config[0].scratch = 4;
-          WUFFS_BASE__COROUTINE_SUSPENSION_POINT(8);
+          WUFFS_BASE__COROUTINE_SUSPENSION_POINT(10);
           if (self->private_data.s_decode_frame_config[0].scratch > ((uint64_t)(io2_a_src - iop_a_src))) {
             self->private_data.s_decode_frame_config[0].scratch -= ((uint64_t)(io2_a_src - iop_a_src));
             iop_a_src = io2_a_src;
@@ -41952,8 +42001,24 @@
           iop_a_src += self->private_data.s_decode_frame_config[0].scratch;
           goto label__0__break;
         }
-        self->private_data.s_decode_frame_config[0].scratch = (((uint64_t)(self->private_impl.f_chunk_length)) + 4);
-        WUFFS_BASE__COROUTINE_SUSPENSION_POINT(9);
+        if (a_src) {
+          a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
+        }
+        WUFFS_BASE__COROUTINE_SUSPENSION_POINT(11);
+        status = wuffs_png__decoder__decode_other_chunk(self, a_src, true);
+        if (a_src) {
+          iop_a_src = a_src->data.ptr + a_src->meta.ri;
+        }
+        if (status.repr) {
+          goto suspend;
+        }
+        if (self->private_impl.f_metadata_fourcc != 0) {
+          self->private_impl.f_call_sequence = 48;
+          status = wuffs_base__make_status(wuffs_base__note__metadata_reported);
+          goto ok;
+        }
+        self->private_data.s_decode_frame_config[0].scratch = 4;
+        WUFFS_BASE__COROUTINE_SUSPENSION_POINT(12);
         if (self->private_data.s_decode_frame_config[0].scratch > ((uint64_t)(io2_a_src - iop_a_src))) {
           self->private_data.s_decode_frame_config[0].scratch -= ((uint64_t)(io2_a_src - iop_a_src));
           iop_a_src = io2_a_src;
@@ -42138,11 +42203,7 @@
     }
     label__0__break:;
     wuffs_base__u32__sat_add_indirect(&self->private_impl.f_num_decoded_frames_value, 1);
-    if (self->private_impl.f_num_decoded_frames_value < self->private_impl.f_num_animation_frames_value) {
-      self->private_impl.f_call_sequence = 32;
-    } else {
-      self->private_impl.f_call_sequence = 96;
-    }
+    self->private_impl.f_call_sequence = 32;
 
     ok:
     self->private_impl.p_skip_frame[0] = 0;
@@ -42411,11 +42472,7 @@
     }
     label__2__break:;
     wuffs_base__u32__sat_add_indirect(&self->private_impl.f_num_decoded_frames_value, 1);
-    if (self->private_impl.f_num_decoded_frames_value < self->private_impl.f_num_animation_frames_value) {
-      self->private_impl.f_call_sequence = 32;
-    } else {
-      self->private_impl.f_call_sequence = 96;
-    }
+    self->private_impl.f_call_sequence = 32;
 
     ok:
     self->private_impl.p_decode_frame[0] = 0;
@@ -46746,6 +46803,7 @@
   wuffs_base__image_config image_config = wuffs_base__null_image_config();
   sync_io::DynIOBuffer raw_metadata_buf(max_incl_metadata_length);
   uint64_t start_pos = io_buf.reader_position();
+  bool interested_in_metadata_after_the_frame = false;
   bool redirected = false;
   int32_t fourcc = 0;
 redirect:
@@ -46820,6 +46878,7 @@
         image_decoder->set_report_metadata(WUFFS_BASE__FOURCC__CHRM, true);
       }
       if (flags & DecodeImageArgFlags::REPORT_METADATA_EXIF) {
+        interested_in_metadata_after_the_frame = true;
         image_decoder->set_report_metadata(WUFFS_BASE__FOURCC__EXIF, true);
       }
       if (flags & DecodeImageArgFlags::REPORT_METADATA_GAMA) {
@@ -46829,12 +46888,14 @@
         image_decoder->set_report_metadata(WUFFS_BASE__FOURCC__ICCP, true);
       }
       if (flags & DecodeImageArgFlags::REPORT_METADATA_KVP) {
+        interested_in_metadata_after_the_frame = true;
         image_decoder->set_report_metadata(WUFFS_BASE__FOURCC__KVP, true);
       }
       if (flags & DecodeImageArgFlags::REPORT_METADATA_SRGB) {
         image_decoder->set_report_metadata(WUFFS_BASE__FOURCC__SRGB, true);
       }
       if (flags & DecodeImageArgFlags::REPORT_METADATA_XMP) {
+        interested_in_metadata_after_the_frame = true;
         image_decoder->set_report_metadata(WUFFS_BASE__FOURCC__XMP, true);
       }
     }
@@ -46869,7 +46930,9 @@
       }
     }
   } while (false);
-  raw_metadata_buf.drop();
+  if (!interested_in_metadata_after_the_frame) {
+    raw_metadata_buf.drop();
+  }
 
   // Select the pixel format.
   uint32_t w = image_config.pixcfg.width();
@@ -46930,6 +46993,12 @@
         image_decoder->decode_frame_config(&frame_config, &io_buf);
     if (id_dfc_status.repr == nullptr) {
       break;
+    } else if (id_dfc_status.repr == wuffs_base__note__metadata_reported) {
+      std::string error_message = DecodeImageHandleMetadata(
+          image_decoder, callbacks, input, io_buf, raw_metadata_buf);
+      if (!error_message.empty()) {
+        return DecodeImageResult(std::move(error_message));
+      }
     } else if (id_dfc_status.repr != wuffs_base__suspension__short_read) {
       return DecodeImageResult(id_dfc_status.message());
     } else if (io_buf.meta.closed) {
@@ -46971,6 +47040,35 @@
       }
     }
   }
+
+  // Decode any metadata after the frame.
+  if (interested_in_metadata_after_the_frame) {
+    while (true) {
+      wuffs_base__status id_dfc_status =
+          image_decoder->decode_frame_config(NULL, &io_buf);
+      if (id_dfc_status.repr == wuffs_base__note__end_of_data) {
+        break;
+      } else if (id_dfc_status.repr == nullptr) {
+        continue;
+      } else if (id_dfc_status.repr == wuffs_base__note__metadata_reported) {
+        std::string error_message = DecodeImageHandleMetadata(
+            image_decoder, callbacks, input, io_buf, raw_metadata_buf);
+        if (!error_message.empty()) {
+          return DecodeImageResult(std::move(error_message));
+        }
+      } else if (id_dfc_status.repr != wuffs_base__suspension__short_read) {
+        return DecodeImageResult(id_dfc_status.message());
+      } else if (io_buf.meta.closed) {
+        return DecodeImageResult(DecodeImage_UnexpectedEndOfFile);
+      } else {
+        std::string error_message = input.CopyIn(&io_buf);
+        if (!error_message.empty()) {
+          return DecodeImageResult(std::move(error_message));
+        }
+      }
+    }
+  }
+
   return DecodeImageResult(std::move(alloc_pixbuf_result.mem_owner),
                            pixel_buffer, std::move(message));
 }
diff --git a/std/png/decode_png.wuffs b/std/png/decode_png.wuffs
index 62e0e62..a1e37a0 100644
--- a/std/png/decode_png.wuffs
+++ b/std/png/decode_png.wuffs
@@ -24,8 +24,8 @@
 	// pass_bytes_per_row doesn't include the 1 byte for the per-row filter.
 	pass_bytes_per_row : base.u64[..= 0x07FF_FFF8],
 
-	workbuf_wi             : base.u64,
-	workbuf_hist_pos_base  : base.u64,
+	workbuf_wi            : base.u64,
+	workbuf_hist_pos_base : base.u64,
 
 	// The inclusive upper bound, DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE, is
 	// derived from the width and height upper bounds at up to 8 bytes per
@@ -264,7 +264,7 @@
 
 		while true {
 			mark = args.src.mark()
-			status =? this.decode_other_chunk?(src: args.src)
+			status =? this.decode_other_chunk?(src: args.src, framy: false)
 			if (not this.ignore_checksum) and ((this.chunk_type & ANCILLARY_BIT) == 0) {
 				checksum_have = this.crc32.update_u32!(x: args.src.since(mark: mark))
 			}
@@ -510,8 +510,11 @@
 	}
 }
 
-pri func decoder.decode_other_chunk?(src: base.io_reader) {
-	if this.chunk_type == 'PLTE'le {
+// framy is:
+//  - false when coming from decode_image_config.
+//  - true  when coming from decode_frame_config.
+pri func decoder.decode_other_chunk?(src: base.io_reader, framy: base.bool) {
+	if (this.chunk_type == 'PLTE'le) and (not args.framy) {
 		if this.seen_plte {
 			return "#bad chunk"
 		} else if this.color_type == 3 {
@@ -530,24 +533,9 @@
 		if this.chunk_type <> 'IDAT'le {
 			return "#bad chunk"
 		}
+	}
 
-	} else if this.chunk_type == 'acTL'le {
-		if this.seen_actl {
-			return "#bad chunk"
-		}
-		this.decode_actl?(src: args.src)
-		this.seen_actl = true
-
-	} else if this.chunk_type == 'cHRM'le {
-		if this.report_metadata_chrm {
-			if this.seen_chrm {
-				return "#bad chunk"
-			}
-			this.decode_chrm?(src: args.src)
-			this.seen_chrm = true
-		}
-
-	} else if this.chunk_type == 'eXIf'le {
+	if this.chunk_type == 'eXIf'le {
 		if this.report_metadata_exif {
 			if this.seen_exif {
 				return "#bad chunk"
@@ -556,48 +544,6 @@
 			this.seen_exif = true
 		}
 
-	} else if this.chunk_type == 'fcTL'le {
-		if this.seen_fctl {
-			return "#bad chunk"
-		}
-		this.decode_fctl?(src: args.src)
-		this.seen_fctl = true
-
-	} else if this.chunk_type == 'gAMA'le {
-		if this.report_metadata_gama {
-			if this.seen_gama {
-				return "#bad chunk"
-			}
-			this.decode_gama?(src: args.src)
-			this.seen_gama = true
-		}
-
-	} else if this.chunk_type == 'iCCP'le {
-		if this.report_metadata_iccp {
-			if this.seen_iccp {
-				return "#bad chunk"
-			}
-			this.decode_iccp?(src: args.src)
-			this.seen_iccp = true
-		}
-
-	} else if this.chunk_type == 'sRGB'le {
-		if this.report_metadata_srgb {
-			if this.seen_srgb {
-				return "#bad chunk"
-			}
-			this.decode_srgb?(src: args.src)
-			this.seen_srgb = true
-		}
-
-	} else if this.chunk_type == 'tRNS'le {
-		if this.seen_trns or (this.color_type > 3) or
-			((this.color_type == 3) and (not this.seen_plte)) {
-			return "#bad chunk"
-		}
-		this.decode_trns?(src: args.src)
-		this.seen_trns = true
-
 	} else if (this.chunk_type == 'iTXt'le) or
 		(this.chunk_type == 'tEXt'le) or
 		(this.chunk_type == 'zTXt'le) {
@@ -608,6 +554,66 @@
 			this.metadata_y = 0
 			this.metadata_z = 0
 		}
+
+	} else if not args.framy {
+		if this.chunk_type == 'acTL'le {
+			if this.seen_actl {
+				return "#bad chunk"
+			}
+			this.decode_actl?(src: args.src)
+			this.seen_actl = true
+
+		} else if this.chunk_type == 'cHRM'le {
+			if this.report_metadata_chrm {
+				if this.seen_chrm {
+					return "#bad chunk"
+				}
+				this.decode_chrm?(src: args.src)
+				this.seen_chrm = true
+			}
+
+		} else if this.chunk_type == 'fcTL'le {
+			if this.seen_fctl {
+				return "#bad chunk"
+			}
+			this.decode_fctl?(src: args.src)
+			this.seen_fctl = true
+
+		} else if this.chunk_type == 'gAMA'le {
+			if this.report_metadata_gama {
+				if this.seen_gama {
+					return "#bad chunk"
+				}
+				this.decode_gama?(src: args.src)
+				this.seen_gama = true
+			}
+
+		} else if this.chunk_type == 'iCCP'le {
+			if this.report_metadata_iccp {
+				if this.seen_iccp {
+					return "#bad chunk"
+				}
+				this.decode_iccp?(src: args.src)
+				this.seen_iccp = true
+			}
+
+		} else if this.chunk_type == 'sRGB'le {
+			if this.report_metadata_srgb {
+				if this.seen_srgb {
+					return "#bad chunk"
+				}
+				this.decode_srgb?(src: args.src)
+				this.seen_srgb = true
+			}
+
+		} else if this.chunk_type == 'tRNS'le {
+			if this.seen_trns or (this.color_type > 3) or
+				((this.color_type == 3) and (not this.seen_plte)) {
+				return "#bad chunk"
+			}
+			this.decode_trns?(src: args.src)
+			this.seen_trns = true
+		}
 	}
 
 	if this.metadata_fourcc == 0 {
@@ -928,6 +934,8 @@
 }
 
 pub func decoder.decode_frame_config?(dst: nptr base.frame_config, src: base.io_reader) {
+	var checksum_have : base.u32
+
 	if (this.call_sequence & 0x10) <> 0 {
 		return base."#bad call sequence"
 	} else if this.call_sequence == 0x20 {
@@ -940,13 +948,15 @@
 		}
 	} else if this.call_sequence == 0x40 {
 		this.skip_frame?(src: args.src)
-		if this.call_sequence >= 0x60 {
-			return base."@end of data"
-		}
 	} else {
 		return base."@end of data"
 	}
 
+	if this.metadata_fourcc <> 0 {
+		this.call_sequence = 0x30
+		return base."@metadata reported"
+	}
+
 	if this.num_decoded_frame_configs_value == 0 {
 		this.frame_rect_x0 = this.first_rect_x0
 		this.frame_rect_y0 = this.first_rect_y0
@@ -958,11 +968,21 @@
 		this.frame_overwrite_instead_of_blend = this.first_overwrite_instead_of_blend
 
 	} else {
-		// Decode the next fcTL chunk.
+		// Decode the next IEND or fcTL chunk.
 		while true {
 			this.chunk_length = args.src.read_u32be?()
 			this.chunk_type = args.src.read_u32le?()
-			if this.chunk_type == 'fdAT'le {
+			if this.chunk_type == 'IEND'le {
+				if this.chunk_length <> 0 {
+					return "#bad chunk"
+				}
+				checksum_have = args.src.read_u32le?()
+				if (not this.ignore_checksum) and (checksum_have <> 0x8260_42AE) {
+					return "#bad checksum"
+				}
+				this.call_sequence = 0x60
+				return base."@end of data"
+			} else if this.chunk_type == 'fdAT'le {
 				return "#bad chunk"
 			} else if this.chunk_type == 'fcTL'le {
 				this.frame_config_io_position = args.src.position() ~mod- 8
@@ -970,7 +990,13 @@
 				args.src.skip?(n: 4)  // Skip the checksum.
 				break
 			}
-			args.src.skip?(n: (this.chunk_length as base.u64) + 4)  // +4 for the checksum.
+
+			this.decode_other_chunk?(src: args.src, framy: true)
+			if this.metadata_fourcc <> 0 {
+				this.call_sequence = 0x30
+				return base."@metadata reported"
+			}
+			args.src.skip_u32?(n: 4)  // Skip the checksum.
 			this.chunk_length = 0
 		} endwhile
 	}
@@ -1061,11 +1087,7 @@
 	} endwhile
 
 	this.num_decoded_frames_value ~sat+= 1
-	if this.num_decoded_frames_value < this.num_animation_frames_value {
-		this.call_sequence = 0x20
-	} else {
-		this.call_sequence = 0x60
-	}
+	this.call_sequence = 0x20
 }
 
 pub func decoder.decode_frame?(dst: ptr base.pixel_buffer, src: base.io_reader, blend: base.pixel_blend, workbuf: slice base.u8, opts: nptr base.decode_frame_options) {
@@ -1207,11 +1229,7 @@
 	} endwhile
 
 	this.num_decoded_frames_value ~sat+= 1
-	if this.num_decoded_frames_value < this.num_animation_frames_value {
-		this.call_sequence = 0x20
-	} else {
-		this.call_sequence = 0x60
-	}
+	this.call_sequence = 0x20
 }
 
 pri func decoder.decode_pass?(src: base.io_reader, workbuf: slice base.u8) {
diff --git a/test/c/std/png.c b/test/c/std/png.c
index c77cb90..23eaafb 100644
--- a/test/c/std/png.c
+++ b/test/c/std/png.c
@@ -810,6 +810,8 @@
       "U-значение",  //
       "Z-Këy",       //
       "Z-значение",  //
+      "After",       //
+      "Frame",       //
   };
 
   wuffs_png__decoder dec;
@@ -819,14 +821,15 @@
                    WUFFS_INITIALIZE__LEAVE_INTERNAL_BUFFERS_UNINITIALIZED));
   wuffs_png__decoder__set_report_metadata(&dec, WUFFS_BASE__FOURCC__KVP, true);
 
-  wuffs_base__image_config ic = ((wuffs_base__image_config){});
   wuffs_base__more_information minfo = wuffs_base__empty_more_information();
 
   size_t i = 0;
-  for (;; i++) {
+  while (true) {
     wuffs_base__status status =
-        wuffs_png__decoder__decode_image_config(&dec, &ic, &src);
+        wuffs_png__decoder__decode_frame_config(&dec, NULL, &src);
     if (wuffs_base__status__is_ok(&status)) {
+      continue;
+    } else if (status.repr == wuffs_base__note__end_of_data) {
       break;
     } else if (status.repr != wuffs_base__note__metadata_reported) {
       RETURN_FAIL("decode_image_config i=%zu: %s", i, status.repr);
@@ -865,6 +868,7 @@
       }
     }
     CHECK_STRING(check_io_buffers_equal("", &have, &want));
+    i++;
   }
 
   if (i != WUFFS_TESTLIB_ARRAY_SIZE(wants)) {
diff --git a/test/data/artificial-png/key-value-pairs.png b/test/data/artificial-png/key-value-pairs.png
index 906fb0e..4b04cfc 100644
--- a/test/data/artificial-png/key-value-pairs.png
+++ b/test/data/artificial-png/key-value-pairs.png
Binary files differ
diff --git a/test/data/artificial-png/key-value-pairs.png.make-artificial.txt b/test/data/artificial-png/key-value-pairs.png.make-artificial.txt
index 95b35fc..7090747 100644
--- a/test/data/artificial-png/key-value-pairs.png.make-artificial.txt
+++ b/test/data/artificial-png/key-value-pairs.png.make-artificial.txt
@@ -95,5 +95,15 @@
 	}
 }
 
+# Basic key-value pair, after the frame (after all of the IDATs).
+tEXt {
+	raw {
+		# "After\x00".
+		0x41 0x66 0x74 0x65 0x72 0x00
+		# "Frame".
+		0x46 0x72 0x61 0x6D 0x65
+	}
+}
+
 IEND {
 }