std/jpeg: decode SOF, producing the image config

Updates #42
diff --git a/release/c/wuffs-unsupported-snapshot.c b/release/c/wuffs-unsupported-snapshot.c
index a9a3fa3..ac29d4c 100644
--- a/release/c/wuffs-unsupported-snapshot.c
+++ b/release/c/wuffs-unsupported-snapshot.c
@@ -8573,8 +8573,22 @@
 
 // ---------------- Status Codes
 
+extern const char wuffs_jpeg__error__bad_dht_marker[];
+extern const char wuffs_jpeg__error__bad_dqt_marker[];
+extern const char wuffs_jpeg__error__bad_dri_marker[];
+extern const char wuffs_jpeg__error__bad_sof_marker[];
+extern const char wuffs_jpeg__error__bad_sos_marker[];
 extern const char wuffs_jpeg__error__bad_header[];
+extern const char wuffs_jpeg__error__bad_marker[];
 extern const char wuffs_jpeg__error__truncated_input[];
+extern const char wuffs_jpeg__error__unsupported_arithmetic_coding[];
+extern const char wuffs_jpeg__error__unsupported_hierarchical_coding[];
+extern const char wuffs_jpeg__error__unsupported_lossless_coding[];
+extern const char wuffs_jpeg__error__unsupported_implicit_height[];
+extern const char wuffs_jpeg__error__unsupported_marker[];
+extern const char wuffs_jpeg__error__unsupported_precision[];
+extern const char wuffs_jpeg__error__unsupported_precision_12_bits[];
+extern const char wuffs_jpeg__error__unsupported_precision_16_bits[];
 
 // ---------------- Public Consts
 
@@ -8728,11 +8742,24 @@
     uint32_t f_width;
     uint32_t f_height;
     uint8_t f_call_sequence;
+    uint8_t f_sof_marker;
+    uint32_t f_num_components;
+    uint8_t f_components_c[4];
+    uint8_t f_components_h[4];
+    uint8_t f_components_v[4];
+    uint8_t f_components_tq[4];
+    uint32_t f_payload_length;
+    uint32_t f_restart_interval;
     uint64_t f_frame_config_io_position;
+    bool f_seen_dqt[4];
+    uint8_t f_quant_tables[4][64];
     wuffs_base__pixel_swizzler f_swizzler;
 
     uint32_t p_decode_image_config[1];
     uint32_t p_do_decode_image_config[1];
+    uint32_t p_decode_dqt[1];
+    uint32_t p_decode_dri[1];
+    uint32_t p_decode_sof[1];
     uint32_t p_decode_frame_config[1];
     uint32_t p_do_decode_frame_config[1];
     uint32_t p_decode_frame[1];
@@ -8743,6 +8770,21 @@
     uint8_t f_dst_palette[1024];
 
     struct {
+      uint8_t v_marker;
+      uint64_t scratch;
+    } s_do_decode_image_config[1];
+    struct {
+      uint8_t v_q;
+      uint32_t v_i;
+    } s_decode_dqt[1];
+    struct {
+      uint64_t scratch;
+    } s_decode_dri[1];
+    struct {
+      uint32_t v_i;
+      uint64_t scratch;
+    } s_decode_sof[1];
+    struct {
       uint8_t v_src[4];
     } s_do_decode_frame[1];
   } private_data;
@@ -35265,11 +35307,37 @@
 
 // ---------------- Status Codes Implementations
 
+const char wuffs_jpeg__error__bad_dht_marker[] = "#jpeg: bad DHT marker";
+const char wuffs_jpeg__error__bad_dqt_marker[] = "#jpeg: bad DQT marker";
+const char wuffs_jpeg__error__bad_dri_marker[] = "#jpeg: bad DRI marker";
+const char wuffs_jpeg__error__bad_sof_marker[] = "#jpeg: bad SOF marker";
+const char wuffs_jpeg__error__bad_sos_marker[] = "#jpeg: bad SOS marker";
 const char wuffs_jpeg__error__bad_header[] = "#jpeg: bad header";
+const char wuffs_jpeg__error__bad_marker[] = "#jpeg: bad marker";
 const char wuffs_jpeg__error__truncated_input[] = "#jpeg: truncated input";
+const char wuffs_jpeg__error__unsupported_arithmetic_coding[] = "#jpeg: unsupported arithmetic coding";
+const char wuffs_jpeg__error__unsupported_hierarchical_coding[] = "#jpeg: unsupported hierarchical coding";
+const char wuffs_jpeg__error__unsupported_lossless_coding[] = "#jpeg: unsupported lossless coding";
+const char wuffs_jpeg__error__unsupported_implicit_height[] = "#jpeg: unsupported implicit height";
+const char wuffs_jpeg__error__unsupported_marker[] = "#jpeg: unsupported marker";
+const char wuffs_jpeg__error__unsupported_precision[] = "#jpeg: unsupported precision";
+const char wuffs_jpeg__error__unsupported_precision_12_bits[] = "#jpeg: unsupported precision (12 bits)";
+const char wuffs_jpeg__error__unsupported_precision_16_bits[] = "#jpeg: unsupported precision (16 bits)";
 
 // ---------------- Private Consts
 
+static const uint8_t
+WUFFS_JPEG__UNZIG[64] WUFFS_BASE__POTENTIALLY_UNUSED = {
+  0, 1, 8, 16, 9, 2, 3, 10,
+  17, 24, 32, 25, 18, 11, 4, 5,
+  12, 19, 26, 33, 40, 48, 41, 34,
+  27, 20, 13, 6, 7, 14, 21, 28,
+  35, 42, 49, 56, 57, 50, 43, 36,
+  29, 22, 15, 23, 30, 37, 44, 51,
+  58, 59, 52, 45, 38, 31, 39, 46,
+  53, 60, 61, 54, 47, 55, 62, 63,
+};
+
 // ---------------- Private Initializer Prototypes
 
 // ---------------- Private Function Prototypes
@@ -35281,6 +35349,21 @@
     wuffs_base__io_buffer* a_src);
 
 static wuffs_base__status
+wuffs_jpeg__decoder__decode_dqt(
+    wuffs_jpeg__decoder* self,
+    wuffs_base__io_buffer* a_src);
+
+static wuffs_base__status
+wuffs_jpeg__decoder__decode_dri(
+    wuffs_jpeg__decoder* self,
+    wuffs_base__io_buffer* a_src);
+
+static wuffs_base__status
+wuffs_jpeg__decoder__decode_sof(
+    wuffs_jpeg__decoder* self,
+    wuffs_base__io_buffer* a_src);
+
+static wuffs_base__status
 wuffs_jpeg__decoder__do_decode_frame_config(
     wuffs_jpeg__decoder* self,
     wuffs_base__frame_config* a_dst,
@@ -35497,6 +35580,7 @@
   wuffs_base__status status = wuffs_base__make_status(NULL);
 
   uint8_t v_c = 0;
+  uint8_t v_marker = 0;
 
   const uint8_t* iop_a_src = NULL;
   const uint8_t* io0_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
@@ -35510,6 +35594,9 @@
   }
 
   uint32_t coro_susp_point = self->private_impl.p_do_decode_image_config[0];
+  if (coro_susp_point) {
+    v_marker = self->private_data.s_do_decode_image_config[0].v_marker;
+  }
   switch (coro_susp_point) {
     WUFFS_BASE__COROUTINE_SUSPENSION_POINT_0;
 
@@ -35543,8 +35630,173 @@
       status = wuffs_base__make_status(wuffs_jpeg__error__bad_header);
       goto exit;
     }
-    self->private_impl.f_width = 1;
-    self->private_impl.f_height = 1;
+    label__0__continue:;
+    while (true) {
+      while (true) {
+        {
+          WUFFS_BASE__COROUTINE_SUSPENSION_POINT(3);
+          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_2 = *iop_a_src++;
+          v_c = t_2;
+        }
+        if (v_c == 255) {
+          goto label__1__break;
+        }
+      }
+      label__1__break:;
+      while (true) {
+        {
+          WUFFS_BASE__COROUTINE_SUSPENSION_POINT(4);
+          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_3 = *iop_a_src++;
+          v_c = t_3;
+        }
+        if (v_c != 255) {
+          v_marker = v_c;
+          goto label__2__break;
+        }
+      }
+      label__2__break:;
+      if (v_marker == 0) {
+        goto label__0__continue;
+      } else if ((208 <= v_marker) && (v_marker <= 215)) {
+        goto label__0__continue;
+      }
+      {
+        WUFFS_BASE__COROUTINE_SUSPENSION_POINT(5);
+        uint32_t t_4;
+        if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 2)) {
+          t_4 = ((uint32_t)(wuffs_base__peek_u16be__no_bounds_check(iop_a_src)));
+          iop_a_src += 2;
+        } else {
+          self->private_data.s_do_decode_image_config[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_do_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 == 8) {
+              t_4 = ((uint32_t)(*scratch >> 48));
+              break;
+            }
+            num_bits_4 += 8;
+            *scratch |= ((uint64_t)(num_bits_4));
+          }
+        }
+        self->private_impl.f_payload_length = t_4;
+      }
+      if (self->private_impl.f_payload_length < 2) {
+        status = wuffs_base__make_status(wuffs_jpeg__error__bad_marker);
+        goto exit;
+      }
+      self->private_impl.f_payload_length -= 2;
+      if (v_marker < 192) {
+        status = wuffs_base__make_status(wuffs_jpeg__error__unsupported_marker);
+        goto exit;
+      } else if (v_marker < 208) {
+        if (v_marker <= 194) {
+          if (self->private_impl.f_sof_marker != 0) {
+            status = wuffs_base__make_status(wuffs_jpeg__error__bad_sof_marker);
+            goto exit;
+          }
+          self->private_impl.f_sof_marker = v_marker;
+          if (a_src) {
+            a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
+          }
+          WUFFS_BASE__COROUTINE_SUSPENSION_POINT(7);
+          status = wuffs_jpeg__decoder__decode_sof(self, a_src);
+          if (a_src) {
+            iop_a_src = a_src->data.ptr + a_src->meta.ri;
+          }
+          if (status.repr) {
+            goto suspend;
+          }
+          goto label__0__break;
+        } else if (v_marker == 195) {
+          status = wuffs_base__make_status(wuffs_jpeg__error__unsupported_lossless_coding);
+          goto exit;
+        } else if (v_marker == 196) {
+          status = wuffs_base__make_status(wuffs_jpeg__error__bad_dht_marker);
+          goto exit;
+        } else if ((197 <= v_marker) && (v_marker <= 199)) {
+          status = wuffs_base__make_status(wuffs_jpeg__error__unsupported_hierarchical_coding);
+          goto exit;
+        } else if (v_marker == 200) {
+          status = wuffs_base__make_status(wuffs_jpeg__error__unsupported_marker);
+          goto exit;
+        } else {
+          status = wuffs_base__make_status(wuffs_jpeg__error__unsupported_arithmetic_coding);
+          goto exit;
+        }
+      } else if (v_marker < 224) {
+        if (v_marker < 218) {
+          status = wuffs_base__make_status(wuffs_jpeg__error__bad_marker);
+          goto exit;
+        } else if (v_marker == 218) {
+          status = wuffs_base__make_status(wuffs_jpeg__error__bad_sos_marker);
+          goto exit;
+        } else if (v_marker == 219) {
+          if (a_src) {
+            a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
+          }
+          WUFFS_BASE__COROUTINE_SUSPENSION_POINT(8);
+          status = wuffs_jpeg__decoder__decode_dqt(self, a_src);
+          if (a_src) {
+            iop_a_src = a_src->data.ptr + a_src->meta.ri;
+          }
+          if (status.repr) {
+            goto suspend;
+          }
+          goto label__0__continue;
+        } else if (v_marker == 221) {
+          if (a_src) {
+            a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
+          }
+          WUFFS_BASE__COROUTINE_SUSPENSION_POINT(9);
+          status = wuffs_jpeg__decoder__decode_dri(self, a_src);
+          if (a_src) {
+            iop_a_src = a_src->data.ptr + a_src->meta.ri;
+          }
+          if (status.repr) {
+            goto suspend;
+          }
+          goto label__0__continue;
+        } else {
+          status = wuffs_base__make_status(wuffs_jpeg__error__unsupported_marker);
+          goto exit;
+        }
+      } else if (v_marker < 240) {
+      } else {
+        if (v_marker == 254) {
+        } else {
+          status = wuffs_base__make_status(wuffs_jpeg__error__unsupported_marker);
+          goto exit;
+        }
+      }
+      self->private_data.s_do_decode_image_config[0].scratch = self->private_impl.f_payload_length;
+      WUFFS_BASE__COROUTINE_SUSPENSION_POINT(10);
+      if (self->private_data.s_do_decode_image_config[0].scratch > ((uint64_t)(io2_a_src - iop_a_src))) {
+        self->private_data.s_do_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_do_decode_image_config[0].scratch;
+      self->private_impl.f_payload_length = 0;
+    }
+    label__0__break:;
     self->private_impl.f_frame_config_io_position = wuffs_base__u64__sat_add((a_src ? a_src->meta.pos : 0), ((uint64_t)(iop_a_src - io0_a_src)));
     if (a_dst != NULL) {
       wuffs_base__image_config__set(
@@ -35567,6 +35819,394 @@
   goto suspend;
   suspend:
   self->private_impl.p_do_decode_image_config[0] = wuffs_base__status__is_suspension(&status) ? coro_susp_point : 0;
+  self->private_data.s_do_decode_image_config[0].v_marker = v_marker;
+
+  goto exit;
+  exit:
+  if (a_src && a_src->data.ptr) {
+    a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
+  }
+
+  return status;
+}
+
+// -------- func jpeg.decoder.decode_dqt
+
+static wuffs_base__status
+wuffs_jpeg__decoder__decode_dqt(
+    wuffs_jpeg__decoder* self,
+    wuffs_base__io_buffer* a_src) {
+  wuffs_base__status status = wuffs_base__make_status(NULL);
+
+  uint8_t v_c = 0;
+  uint8_t v_q = 0;
+  uint32_t v_i = 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 && a_src->data.ptr) {
+    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_dqt[0];
+  if (coro_susp_point) {
+    v_q = self->private_data.s_decode_dqt[0].v_q;
+    v_i = self->private_data.s_decode_dqt[0].v_i;
+  }
+  switch (coro_susp_point) {
+    WUFFS_BASE__COROUTINE_SUSPENSION_POINT_0;
+
+    while (self->private_impl.f_payload_length > 0) {
+      self->private_impl.f_payload_length -= 1;
+      {
+        WUFFS_BASE__COROUTINE_SUSPENSION_POINT(1);
+        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_0 = *iop_a_src++;
+        v_c = t_0;
+      }
+      if ((v_c & 15) > 3) {
+        status = wuffs_base__make_status(wuffs_jpeg__error__bad_dqt_marker);
+        goto exit;
+      }
+      v_q = (v_c & 15);
+      if ((v_c >> 4) == 1) {
+        status = wuffs_base__make_status(wuffs_jpeg__error__unsupported_precision);
+        goto exit;
+      } else if (((v_c >> 4) > 1) || (self->private_impl.f_payload_length < 64)) {
+        status = wuffs_base__make_status(wuffs_jpeg__error__bad_dqt_marker);
+        goto exit;
+      }
+      self->private_impl.f_payload_length -= 64;
+      v_i = 0;
+      while (v_i < 64) {
+        {
+          WUFFS_BASE__COROUTINE_SUSPENSION_POINT(2);
+          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_1 = *iop_a_src++;
+          self->private_impl.f_quant_tables[v_q][WUFFS_JPEG__UNZIG[v_i]] = t_1;
+        }
+        v_i += 1;
+      }
+      self->private_impl.f_seen_dqt[v_q] = true;
+    }
+
+    goto ok;
+    ok:
+    self->private_impl.p_decode_dqt[0] = 0;
+    goto exit;
+  }
+
+  goto suspend;
+  suspend:
+  self->private_impl.p_decode_dqt[0] = wuffs_base__status__is_suspension(&status) ? coro_susp_point : 0;
+  self->private_data.s_decode_dqt[0].v_q = v_q;
+  self->private_data.s_decode_dqt[0].v_i = v_i;
+
+  goto exit;
+  exit:
+  if (a_src && a_src->data.ptr) {
+    a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
+  }
+
+  return status;
+}
+
+// -------- func jpeg.decoder.decode_dri
+
+static wuffs_base__status
+wuffs_jpeg__decoder__decode_dri(
+    wuffs_jpeg__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 && a_src->data.ptr) {
+    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_dri[0];
+  switch (coro_susp_point) {
+    WUFFS_BASE__COROUTINE_SUSPENSION_POINT_0;
+
+    if (self->private_impl.f_payload_length != 2) {
+      status = wuffs_base__make_status(wuffs_jpeg__error__bad_dri_marker);
+      goto exit;
+    }
+    self->private_impl.f_payload_length = 0;
+    {
+      WUFFS_BASE__COROUTINE_SUSPENSION_POINT(1);
+      uint32_t t_0;
+      if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 2)) {
+        t_0 = ((uint32_t)(wuffs_base__peek_u16be__no_bounds_check(iop_a_src)));
+        iop_a_src += 2;
+      } else {
+        self->private_data.s_decode_dri[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_dri[0].scratch;
+          uint32_t num_bits_0 = ((uint32_t)(*scratch & 0xFF));
+          *scratch >>= 8;
+          *scratch <<= 8;
+          *scratch |= ((uint64_t)(*iop_a_src++)) << (56 - num_bits_0);
+          if (num_bits_0 == 8) {
+            t_0 = ((uint32_t)(*scratch >> 48));
+            break;
+          }
+          num_bits_0 += 8;
+          *scratch |= ((uint64_t)(num_bits_0));
+        }
+      }
+      self->private_impl.f_restart_interval = t_0;
+    }
+
+    goto ok;
+    ok:
+    self->private_impl.p_decode_dri[0] = 0;
+    goto exit;
+  }
+
+  goto suspend;
+  suspend:
+  self->private_impl.p_decode_dri[0] = wuffs_base__status__is_suspension(&status) ? coro_susp_point : 0;
+
+  goto exit;
+  exit:
+  if (a_src && a_src->data.ptr) {
+    a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr));
+  }
+
+  return status;
+}
+
+// -------- func jpeg.decoder.decode_sof
+
+static wuffs_base__status
+wuffs_jpeg__decoder__decode_sof(
+    wuffs_jpeg__decoder* self,
+    wuffs_base__io_buffer* a_src) {
+  wuffs_base__status status = wuffs_base__make_status(NULL);
+
+  uint8_t v_c = 0;
+  uint8_t v_comp_h = 0;
+  uint8_t v_comp_v = 0;
+  uint32_t v_i = 0;
+  uint32_t v_j = 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 && a_src->data.ptr) {
+    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_sof[0];
+  if (coro_susp_point) {
+    v_i = self->private_data.s_decode_sof[0].v_i;
+  }
+  switch (coro_susp_point) {
+    WUFFS_BASE__COROUTINE_SUSPENSION_POINT_0;
+
+    if (self->private_impl.f_payload_length < 6) {
+      status = wuffs_base__make_status(wuffs_jpeg__error__bad_sof_marker);
+      goto exit;
+    }
+    self->private_impl.f_payload_length -= 6;
+    {
+      WUFFS_BASE__COROUTINE_SUSPENSION_POINT(1);
+      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_0 = *iop_a_src++;
+      v_c = t_0;
+    }
+    if (v_c == 8) {
+    } else if (v_c == 12) {
+      status = wuffs_base__make_status(wuffs_jpeg__error__unsupported_precision_12_bits);
+      goto exit;
+    } else if (v_c == 16) {
+      status = wuffs_base__make_status(wuffs_jpeg__error__unsupported_precision_16_bits);
+      goto exit;
+    } else {
+      status = wuffs_base__make_status(wuffs_jpeg__error__unsupported_precision);
+      goto exit;
+    }
+    {
+      WUFFS_BASE__COROUTINE_SUSPENSION_POINT(2);
+      uint32_t t_1;
+      if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 2)) {
+        t_1 = ((uint32_t)(wuffs_base__peek_u16be__no_bounds_check(iop_a_src)));
+        iop_a_src += 2;
+      } else {
+        self->private_data.s_decode_sof[0].scratch = 0;
+        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);
+            goto suspend;
+          }
+          uint64_t* scratch = &self->private_data.s_decode_sof[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 == 8) {
+            t_1 = ((uint32_t)(*scratch >> 48));
+            break;
+          }
+          num_bits_1 += 8;
+          *scratch |= ((uint64_t)(num_bits_1));
+        }
+      }
+      self->private_impl.f_height = t_1;
+    }
+    if (self->private_impl.f_height == 0) {
+      status = wuffs_base__make_status(wuffs_jpeg__error__unsupported_implicit_height);
+      goto exit;
+    }
+    {
+      WUFFS_BASE__COROUTINE_SUSPENSION_POINT(4);
+      uint32_t t_2;
+      if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 2)) {
+        t_2 = ((uint32_t)(wuffs_base__peek_u16be__no_bounds_check(iop_a_src)));
+        iop_a_src += 2;
+      } else {
+        self->private_data.s_decode_sof[0].scratch = 0;
+        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_sof[0].scratch;
+          uint32_t num_bits_2 = ((uint32_t)(*scratch & 0xFF));
+          *scratch >>= 8;
+          *scratch <<= 8;
+          *scratch |= ((uint64_t)(*iop_a_src++)) << (56 - num_bits_2);
+          if (num_bits_2 == 8) {
+            t_2 = ((uint32_t)(*scratch >> 48));
+            break;
+          }
+          num_bits_2 += 8;
+          *scratch |= ((uint64_t)(num_bits_2));
+        }
+      }
+      self->private_impl.f_width = t_2;
+    }
+    if (self->private_impl.f_width == 0) {
+      status = wuffs_base__make_status(wuffs_base__error__unsupported_image_dimension);
+      goto exit;
+    }
+    {
+      WUFFS_BASE__COROUTINE_SUSPENSION_POINT(6);
+      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_3 = *iop_a_src++;
+      v_c = t_3;
+    }
+    if ((v_c == 0) || (v_c > 4)) {
+      status = wuffs_base__make_status(wuffs_jpeg__error__bad_sof_marker);
+      goto exit;
+    }
+    self->private_impl.f_num_components = ((uint32_t)(v_c));
+    if (self->private_impl.f_payload_length != (3 * self->private_impl.f_num_components)) {
+      status = wuffs_base__make_status(wuffs_jpeg__error__bad_sof_marker);
+      goto exit;
+    }
+    self->private_impl.f_payload_length = 0;
+    v_i = 0;
+    while (v_i < self->private_impl.f_num_components) {
+      {
+        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++;
+        self->private_impl.f_components_c[v_i] = t_4;
+      }
+      {
+        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++;
+        v_c = t_5;
+      }
+      v_comp_h = (v_c >> 4);
+      v_comp_v = (v_c & 15);
+      if ((v_comp_h == 0) ||
+          (v_comp_h > 4) ||
+          (v_comp_v == 0) ||
+          (v_comp_v > 4)) {
+        status = wuffs_base__make_status(wuffs_jpeg__error__bad_sof_marker);
+        goto exit;
+      }
+      self->private_impl.f_components_h[v_i] = v_comp_h;
+      self->private_impl.f_components_v[v_i] = v_comp_v;
+      {
+        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_6 = *iop_a_src++;
+        v_c = t_6;
+      }
+      if (v_c > 4) {
+        status = wuffs_base__make_status(wuffs_jpeg__error__bad_sof_marker);
+        goto exit;
+      }
+      self->private_impl.f_components_tq[v_i] = v_c;
+      v_j = 0;
+      while (v_j < v_i) {
+        if (self->private_impl.f_components_c[v_j] == self->private_impl.f_components_c[v_i]) {
+          status = wuffs_base__make_status(wuffs_jpeg__error__bad_sof_marker);
+          goto exit;
+        }
+        v_j += 1;
+      }
+      v_i += 1;
+    }
+
+    goto ok;
+    ok:
+    self->private_impl.p_decode_sof[0] = 0;
+    goto exit;
+  }
+
+  goto suspend;
+  suspend:
+  self->private_impl.p_decode_sof[0] = wuffs_base__status__is_suspension(&status) ? coro_susp_point : 0;
+  self->private_data.s_decode_sof[0].v_i = v_i;
 
   goto exit;
   exit:
@@ -35816,9 +36456,13 @@
   wuffs_base__status v_status = wuffs_base__make_status(NULL);
   wuffs_base__pixel_format v_dst_pixfmt = {0};
   uint32_t v_dst_bits_per_pixel = 0;
+  uint32_t v_dst_bytes_per_pixel = 0;
   wuffs_base__table_u8 v_tab = {0};
   wuffs_base__slice_u8 v_dst = {0};
   uint8_t v_src[4] = {0};
+  uint32_t v_y = 0;
+  uint32_t v_x = 0;
+  uint64_t v_d = 0;
 
   uint32_t coro_susp_point = self->private_impl.p_do_decode_frame[0];
   if (coro_susp_point) {
@@ -35860,13 +36504,25 @@
       status = wuffs_base__make_status(wuffs_base__error__unsupported_option);
       goto exit;
     }
+    v_dst_bytes_per_pixel = (v_dst_bits_per_pixel / 8);
     v_tab = wuffs_base__pixel_buffer__plane(a_dst, 0);
-    v_dst = wuffs_base__table_u8__row_u32(v_tab, 0);
-    v_src[0] = 127;
-    v_src[1] = 0;
-    v_src[2] = 255;
-    v_src[3] = 255;
-    wuffs_base__pixel_swizzler__swizzle_interleaved_from_slice(&self->private_impl.f_swizzler, v_dst, wuffs_base__pixel_buffer__palette_or_else(a_dst, wuffs_base__make_slice_u8(self->private_data.f_dst_palette, 1024)), wuffs_base__make_slice_u8(v_src, 4));
+    v_y = 0;
+    while (v_y < self->private_impl.f_height) {
+      v_x = 0;
+      while (v_x < self->private_impl.f_width) {
+        v_dst = wuffs_base__table_u8__row_u32(v_tab, v_y);
+        v_d = ((uint64_t)((v_x * v_dst_bytes_per_pixel)));
+        if (v_d < ((uint64_t)(v_dst.len))) {
+          v_src[0] = ((uint8_t)((v_x & 255)));
+          v_src[1] = 127;
+          v_src[2] = ((uint8_t)((v_y & 255)));
+          v_src[3] = 255;
+          wuffs_base__pixel_swizzler__swizzle_interleaved_from_slice(&self->private_impl.f_swizzler, wuffs_base__slice_u8__subslice_i(v_dst, v_d), wuffs_base__pixel_buffer__palette_or_else(a_dst, wuffs_base__make_slice_u8(self->private_data.f_dst_palette, 1024)), wuffs_base__make_slice_u8(v_src, 4));
+        }
+        v_x += 1;
+      }
+      v_y += 1;
+    }
     self->private_impl.f_call_sequence = 96;
 
     ok:
diff --git a/std/jpeg/README.md b/std/jpeg/README.md
new file mode 100644
index 0000000..49090a2
--- /dev/null
+++ b/std/jpeg/README.md
@@ -0,0 +1,5 @@
+# JPEG
+
+JPEG ([Joint Photographic Experts
+Group](https://www.w3.org/Graphics/JPEG/itu-t81.pdf)) is a lossy image
+compression format for opaque, still images.
diff --git a/std/jpeg/common_consts.wuffs b/std/jpeg/common_consts.wuffs
new file mode 100644
index 0000000..5a3b378
--- /dev/null
+++ b/std/jpeg/common_consts.wuffs
@@ -0,0 +1,27 @@
+// Copyright 2023 The Wuffs Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// UNZIG maps from the zig-zag ordering to the natural ordering. For example,
+// UNZIG[3] is the column and row of the fourth element in zig-zag order. The
+// value is 16, which means first column (16%8 == 0) and third row (16/8 == 2).
+pri const UNZIG : roarray[64] base.u8[..= 63] = [
+        0x00, 0x01, 0x08, 0x10, 0x09, 0x02, 0x03, 0x0A,
+        0x11, 0x18, 0x20, 0x19, 0x12, 0x0B, 0x04, 0x05,
+        0x0C, 0x13, 0x1A, 0x21, 0x28, 0x30, 0x29, 0x22,
+        0x1B, 0x14, 0x0D, 0x06, 0x07, 0x0E, 0x15, 0x1C,
+        0x23, 0x2A, 0x31, 0x38, 0x39, 0x32, 0x2B, 0x24,
+        0x1D, 0x16, 0x0F, 0x17, 0x1E, 0x25, 0x2C, 0x33,
+        0x3A, 0x3B, 0x34, 0x2D, 0x26, 0x1F, 0x27, 0x2E,
+        0x35, 0x3C, 0x3D, 0x36, 0x2F, 0x37, 0x3E, 0x3F,
+]
diff --git a/std/jpeg/decode_jpeg.wuffs b/std/jpeg/decode_jpeg.wuffs
index 069c88d..6b49b6f 100644
--- a/std/jpeg/decode_jpeg.wuffs
+++ b/std/jpeg/decode_jpeg.wuffs
@@ -12,8 +12,22 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+pub status "#bad DHT marker"
+pub status "#bad DQT marker"
+pub status "#bad DRI marker"
+pub status "#bad SOF marker"
+pub status "#bad SOS marker"
 pub status "#bad header"
+pub status "#bad marker"
 pub status "#truncated input"
+pub status "#unsupported arithmetic coding"
+pub status "#unsupported hierarchical coding"
+pub status "#unsupported lossless coding"
+pub status "#unsupported implicit height"
+pub status "#unsupported marker"
+pub status "#unsupported precision"
+pub status "#unsupported precision (12 bits)"
+pub status "#unsupported precision (16 bits)"
 
 pub const DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE : base.u64 = 0
 
@@ -25,8 +39,24 @@
         // (/doc/std/image-decoders-call-sequence.md).
         call_sequence : base.u8,
 
+        sof_marker : base.u8,
+
+        num_components : base.u32[..= 4],
+        components_c   : array[4] base.u8,
+        components_h   : array[4] base.u8[..= 4],
+        components_v   : array[4] base.u8[..= 4],
+        components_tq  : array[4] base.u8[..= 4],
+
+        payload_length : base.u32[..= 0xFFFF],
+
+        restart_interval : base.u32[..= 0xFFFF],
+
         frame_config_io_position : base.u64,
 
+        seen_dqt : array[4] base.bool,
+
+        quant_tables : array[4] array[64] base.u8,
+
         swizzler : base.pixel_swizzler,
         util     : base.utility,
 ) + (
@@ -50,7 +80,8 @@
 }
 
 pri func decoder.do_decode_image_config?(dst: nptr base.image_config, src: base.io_reader) {
-    var c : base.u8
+    var c      : base.u8
+    var marker : base.u8
 
     if this.call_sequence <> 0x00 {
         return base."#bad call sequence"
@@ -61,12 +92,117 @@
         return "#bad header"
     }
     c = args.src.read_u8?()
-    if c <> 0xD8 {
+    if c <> 0xD8 {  // SOI (Start Of Image).
         return "#bad header"
     }
 
-    this.width = 1
-    this.height = 1
+    // Process chunks (markers and their payloads).
+    while true {
+        // Read the marker (a two-byte 0xFF 0x?? sequence).
+        while true {
+            c = args.src.read_u8?()
+            if c == 0xFF {
+                break
+            }
+            // Getting here is invalid according to the JPEG spec, but libjpeg
+            // treats this as a warning (JWRN_EXTRANEOUS_DATA), not an error.
+        } endwhile
+        while true {
+            c = args.src.read_u8?()
+            if c <> 0xFF {
+                marker = c
+                break
+            }
+            // Section B.1.1.2: "Any marker may optionally be preceded by any
+            // number of [0xFF] fill bytes".
+        } endwhile
+
+        if marker == 0x00 {
+            // Ignore byte stuffing.
+            continue
+        } else if (0xD0 <= marker) and (marker <= 0xD7) {
+            // RSTn (Restart) markers have no payload.
+            continue
+        }
+
+        // Payload length includes the 2 bytes for the u16be payload length.
+        this.payload_length = args.src.read_u16be_as_u32?()
+        if this.payload_length < 2 {
+            return "#bad marker"
+        }
+        this.payload_length -= 2
+
+        // Switch on the marker.
+        if marker < 0xC0 {
+            return "#unsupported marker"
+
+        } else if marker < 0xD0 {  // SOFn (Start of Frame) and friends.
+            if marker <= 0xC2 {
+                if this.sof_marker <> 0 {
+                    return "#bad SOF marker"
+                }
+                this.sof_marker = marker
+                this.decode_sof?(src: args.src)
+                break
+
+            } else if marker == 0xC3 {
+                return "#unsupported lossless coding"
+
+            } else if marker == 0xC4 {  // DHT (Define Huffman Table).
+                // We shouldn't see DHT before SOF.
+                return "#bad DHT marker"
+
+            } else if (0xC5 <= marker) and (marker <= 0xC7) {
+                return "#unsupported hierarchical coding"
+
+            } else if marker == 0xC8 {  // JPG (JPEG extension).
+                return "#unsupported marker"
+
+            } else {
+                return "#unsupported arithmetic coding"
+            }
+
+        } else if marker < 0xE0 {
+            if marker < 0xDA {
+                // RSTn markers are already handled above. We either have 0xD8
+                // (SOI: Start Of Image) or 0xD9 (EOI: End Of Image), neither
+                // of which are valid here.
+                return "#bad marker"
+
+            } else if marker == 0xDA {  // SOS (Start Of Scan).
+                // We shouldn't see SOS before SOF.
+                return "#bad SOS marker"
+
+            } else if marker == 0xDB {  // DQT (Define Quantization Table).
+                this.decode_dqt?(src: args.src)
+                continue
+
+            } else if marker == 0xDD {  // DRI (Define Restart Interval).
+                this.decode_dri?(src: args.src)
+                continue
+
+            } else {
+                // 0xDC (DNL: Define Number of Lines).
+                // 0xDE (DHP: Define Hierarchical Progression).
+                // 0xDF (EXP: Expand Reference Components).
+                return "#unsupported marker"
+            }
+
+        } else if marker < 0xF0 {  // APPn (Application specific).
+            // No-op.
+
+        } else {
+            if marker == 0xFE {  // COM (Comment).
+                // No-op.
+
+            } else {
+                return "#unsupported marker"
+            }
+        }
+
+        args.src.skip_u32?(n: this.payload_length)
+        this.payload_length = 0
+    } endwhile
 
     this.frame_config_io_position = args.src.position()
 
@@ -85,6 +221,117 @@
     this.call_sequence = 0x20
 }
 
+pri func decoder.decode_dqt?(src: base.io_reader) {
+    var c : base.u8
+    var q : base.u8[..= 3]
+    var i : base.u32
+
+    while this.payload_length > 0 {
+        this.payload_length -= 1
+        c = args.src.read_u8?()
+        if (c & 0x0F) > 3 {
+            return "#bad DQT marker"
+        }
+        q = c & 0x0F
+        if (c >> 4) == 1 {
+            return "#unsupported precision"
+        } else if ((c >> 4) > 1) or (this.payload_length < 64) {
+            return "#bad DQT marker"
+        }
+        this.payload_length -= 64
+
+        i = 0
+        while i < 64 {
+            this.quant_tables[q][UNZIG[i]] = args.src.read_u8?()
+            i += 1
+        } endwhile
+        this.seen_dqt[q] = true
+    } endwhile
+}
+
+pri func decoder.decode_dri?(src: base.io_reader) {
+    if this.payload_length <> 2 {
+        return "#bad DRI marker"
+    }
+    this.payload_length = 0
+
+    this.restart_interval = args.src.read_u16be_as_u32?()
+}
+
+pri func decoder.decode_sof?(src: base.io_reader) {
+    var c      : base.u8
+    var comp_h : base.u8
+    var comp_v : base.u8
+    var i      : base.u32
+    var j      : base.u32
+
+    if this.payload_length < 6 {
+        return "#bad SOF marker"
+    }
+    this.payload_length -= 6
+    c = args.src.read_u8?()
+    if c == 8 {
+        // No-op.
+    } else if c == 12 {
+        return "#unsupported precision (12 bits)"
+    } else if c == 16 {
+        return "#unsupported precision (16 bits)"
+    } else {
+        return "#unsupported precision"
+    }
+    this.height = args.src.read_u16be_as_u32?()
+    if this.height == 0 {
+        return "#unsupported implicit height"
+    }
+    this.width = args.src.read_u16be_as_u32?()
+    if this.width == 0 {
+        return base."#unsupported image dimension"
+    }
+    c = args.src.read_u8?()
+    if (c == 0) or (c > 4) {
+        return "#bad SOF marker"
+    }
+    this.num_components = c as base.u32
+    if this.payload_length <> (3 * this.num_components) {
+        return "#bad SOF marker"
+    }
+    this.payload_length = 0
+
+    i = 0
+    while i < this.num_components {
+        assert i < 4 via "a < b: a < c; c <= b"(c: this.num_components)
+        this.components_c[i] = args.src.read_u8?()
+        c = args.src.read_u8?()
+        comp_h = c >> 4
+        comp_v = c & 0x0F
+        if (comp_h == 0) or (comp_h > 4) or (comp_v == 0) or (comp_v > 4) {
+            return "#bad SOF marker"
+        }
+        this.components_h[i] = comp_h
+        this.components_v[i] = comp_v
+        c = args.src.read_u8?()
+        if c > 4 {
+            return "#bad SOF marker"
+        }
+        this.components_tq[i] = c
+
+        // Section B.2.2: "the value of C_i shall be different from the values
+        // of C_1 through C_(i-1)".
+        j = 0
+        while j < i,
+                inv i < 4,
+        {
+            assert j < 4 via "a < b: a < c; c < b"(c: i)
+            if this.components_c[j] == this.components_c[i] {
+                return "#bad SOF marker"
+            }
+            j += 1
+        } endwhile
+
+        i += 1
+    } endwhile
+}
+
 pub func decoder.decode_frame_config?(dst: nptr base.frame_config, src: base.io_reader) {
     var status : base.status
 
@@ -144,12 +391,16 @@
 }
 
 pri func decoder.do_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) {
-    var status             : base.status
-    var dst_pixfmt         : base.pixel_format
-    var dst_bits_per_pixel : base.u32[..= 256]
-    var tab                : table base.u8
-    var dst                : slice base.u8
-    var src                : array[4] base.u8
+    var status              : base.status
+    var dst_pixfmt          : base.pixel_format
+    var dst_bits_per_pixel  : base.u32[..= 256]
+    var dst_bytes_per_pixel : base.u32[..= 32]
+    var tab                 : table base.u8
+    var dst                 : slice base.u8
+    var src                 : array[4] base.u8
+    var y                   : base.u32
+    var x                   : base.u32
+    var d                   : base.u64
 
     if this.call_sequence == 0x40 {
         // No-op.
@@ -176,17 +427,34 @@
     if (dst_bits_per_pixel & 7) <> 0 {
         return base."#unsupported option"
     }
+    dst_bytes_per_pixel = dst_bits_per_pixel / 8
 
+    // TODO: actually decode the pixels. Until then, fill with gradient tiles.
     tab = args.dst.plane(p: 0)
-    dst = tab.row_u32(y: 0)
-    src[0] = 0x7F
-    src[1] = 0x00
-    src[2] = 0xFF
-    src[3] = 0xFF
-    this.swizzler.swizzle_interleaved_from_slice!(
-            dst: dst,
-            dst_palette: args.dst.palette_or_else(fallback: this.dst_palette[..]),
-            src: src[.. 4])
+    y = 0
+    while y < this.height {
+        assert y < 0xFFFF via "a < b: a < c; c <= b"(c: this.height)
+        x = 0
+        while x < this.width,
+                inv y < 0xFFFF,
+        {
+            assert x < 0xFFFF via "a < b: a < c; c <= b"(c: this.width)
+            dst = tab.row_u32(y: y)
+            d = (x * dst_bytes_per_pixel) as base.u64
+            if d < dst.length() {
+                src[0] = (x & 0xFF) as base.u8
+                src[1] = 0x7F
+                src[2] = (y & 0xFF) as base.u8
+                src[3] = 0xFF
+                this.swizzler.swizzle_interleaved_from_slice!(
+                        dst: dst[d ..],
+                        dst_palette: args.dst.palette_or_else(fallback: this.dst_palette[..]),
+                        src: src[.. 4])
+            }
+            x += 1
+        } endwhile
+        y += 1
+    } endwhile
 
     this.call_sequence = 0x60
 }
diff --git a/test/c/std/jpeg.c b/test/c/std/jpeg.c
index 4603d4c..5a42c85 100644
--- a/test/c/std/jpeg.c
+++ b/test/c/std/jpeg.c
@@ -96,7 +96,7 @@
                    WUFFS_INITIALIZE__LEAVE_INTERNAL_BUFFERS_UNINITIALIZED));
   return do_test__wuffs_base__image_decoder(
       wuffs_jpeg__decoder__upcast_as__wuffs_base__image_decoder(&dec),
-      "test/data/bricks-color.jpeg", 0, SIZE_MAX, 1, 1, 0xFFFF007F);
+      "test/data/bricks-color.jpeg", 0, SIZE_MAX, 160, 120, 0xFF777F9F);
 }
 
 const char*  //
diff --git a/test/nia-checksums-of-data.txt b/test/nia-checksums-of-data.txt
index 5d1cdcb..77845da 100644
--- a/test/nia-checksums-of-data.txt
+++ b/test/nia-checksums-of-data.txt
@@ -25,7 +25,7 @@
 e08a7cc8 test/data/artificial-png/exif.png
 e08a7cc8 test/data/artificial-png/key-value-pairs.png
 076cb375 test/data/bricks-color.bmp
-1f87b217 test/data/bricks-color.jpeg
+84e5ba7c test/data/bricks-color.jpeg
 076cb375 test/data/bricks-color.png
 076cb375 test/data/bricks-color.tga
 f36c2e80 test/data/bricks-dither.bmp
@@ -34,7 +34,7 @@
 f36c2e80 test/data/bricks-dither.png
 c2bce675 test/data/bricks-gray.bmp
 c2bce675 test/data/bricks-gray.gif
-1f87b217 test/data/bricks-gray.jpeg
+84e5ba7c test/data/bricks-gray.jpeg
 c2bce675 test/data/bricks-gray.no-ancillary.png
 c2bce675 test/data/bricks-gray.png
 c2bce675 test/data/bricks-gray.tga
@@ -49,27 +49,27 @@
 3014b4c0 test/data/gifplayer-muybridge.gif
 030f5a48 test/data/harvesters.bmp
 c18b3d5a test/data/harvesters.gif
-1f87b217 test/data/harvesters.jpeg
+58e255d4 test/data/harvesters.jpeg
 030f5a48 test/data/harvesters.png
 e776c90f test/data/hat.bmp
 6dcba6a4 test/data/hat.gif
-1f87b217 test/data/hat.jpeg
+486c598d test/data/hat.jpeg
 e776c90f test/data/hat.png
 d30bfe5d test/data/hat.wbmp
 33a44f22 test/data/hibiscus.primitive.bmp
 25e212b3 test/data/hibiscus.primitive.gif
-1f87b217 test/data/hibiscus.primitive.jpeg
+5d1391ce test/data/hibiscus.primitive.jpeg
 33a44f22 test/data/hibiscus.primitive.png
 60040742 test/data/hibiscus.regular.bmp
 b727da8b test/data/hibiscus.regular.gif
-1f87b217 test/data/hibiscus.regular.jpeg
+5d1391ce test/data/hibiscus.regular.jpeg
 60040742 test/data/hibiscus.regular.png
 dcbb225a test/data/hippopotamus.bmp
 ed4b78fc test/data/hippopotamus.interlaced.gif
 dcbb225a test/data/hippopotamus.interlaced.png
 c3c4bd65 test/data/hippopotamus.interlaced.truncated.gif
 7c6a771b test/data/hippopotamus.interlaced.truncated.png
-1f87b217 test/data/hippopotamus.jpeg
+cbd26e18 test/data/hippopotamus.jpeg
 d3bbed27 test/data/hippopotamus.masked-with-muybridge.gif
 7e6acf01 test/data/hippopotamus.masked-with-muybridge.png
 dcbb225a test/data/hippopotamus.nie
@@ -81,7 +81,7 @@
 db2733f5 test/data/muybridge.gif
 bf7e8c96 test/data/pjw-thumbnail.bmp
 bf7e8c96 test/data/pjw-thumbnail.gif
-1f87b217 test/data/pjw-thumbnail.jpeg
+485d4e58 test/data/pjw-thumbnail.jpeg
 bf7e8c96 test/data/pjw-thumbnail.png
 38cb4cbf test/data/red-blue-gradient.dcip3d65-no-chrm-no-gama.png
 38cb4cbf test/data/red-blue-gradient.gamma1dot0.png