Add wuffs_gif__quirk_image_bounds_are_strict
diff --git a/release/c/wuffs-unsupported-snapshot.c b/release/c/wuffs-unsupported-snapshot.c
index 7034209..7abf784 100644
--- a/release/c/wuffs-unsupported-snapshot.c
+++ b/release/c/wuffs-unsupported-snapshot.c
@@ -10509,7 +10509,8 @@
     self->private_impl.f_frame_rect_y1 += self->private_impl.f_frame_rect_y0;
     self->private_impl.f_dst_x = self->private_impl.f_frame_rect_x0;
     self->private_impl.f_dst_y = self->private_impl.f_frame_rect_y0;
-    if (self->private_impl.f_call_sequence == 0) {
+    if ((self->private_impl.f_call_sequence == 0) &&
+        !self->private_impl.f_quirk_enabled_image_bounds_are_strict) {
       self->private_impl.f_width = wuffs_base__u32__max(
           self->private_impl.f_width, self->private_impl.f_frame_rect_x1);
       self->private_impl.f_height = wuffs_base__u32__max(
diff --git a/std/gif/decode_gif.wuffs b/std/gif/decode_gif.wuffs
index 3a15ebb..98dc747 100644
--- a/std/gif/decode_gif.wuffs
+++ b/std/gif/decode_gif.wuffs
@@ -743,7 +743,7 @@
 	// and the bottom right extent of the first frame. See
 	// test/data/artificial/gif-frame-out-of-bounds.gif.make-artificial.txt for
 	// more discussion.
-	if this.call_sequence == 0 {
+	if (this.call_sequence == 0) and (not this.quirk_enabled_image_bounds_are_strict) {
 		this.width = this.width.max(x:this.frame_rect_x1)
 		this.height = this.height.max(x:this.frame_rect_y1)
 	}
diff --git a/test/c/std/gif.c b/test/c/std/gif.c
index 14f2da3..2c8fa49 100644
--- a/test/c/std/gif.c
+++ b/test/c/std/gif.c
@@ -731,125 +731,139 @@
     return status;
   }
 
-  wuffs_gif__decoder dec;
-  status = wuffs_gif__decoder__initialize(
-      &dec, sizeof dec, WUFFS_VERSION,
-      WUFFS_INITIALIZE__LEAVE_INTERNAL_BUFFERS_UNINITIALIZED);
-  if (status) {
-    RETURN_FAIL("initialize: \"%s\"", status);
-  }
-  wuffs_base__image_config ic = ((wuffs_base__image_config){});
-  wuffs_base__io_reader src_reader = wuffs_base__io_buffer__reader(&src);
-  status = wuffs_gif__decoder__decode_image_config(&dec, &ic, src_reader);
-  if (status) {
-    RETURN_FAIL("decode_image_config: \"%s\"", status);
-  }
+  int q;
+  for (q = 0; q < 2; q++) {
+    src.meta.ri = 0;
 
-  // The nominal width and height for the overall image is 2×2, but its first
-  // frame extends those bounds to 4×2. See
-  // test/data/artificial/gif-frame-out-of-bounds.gif.make-artificial.txt for
-  // more discussion.
+    wuffs_gif__decoder dec;
+    status = wuffs_gif__decoder__initialize(
+        &dec, sizeof dec, WUFFS_VERSION,
+        WUFFS_INITIALIZE__LEAVE_INTERNAL_BUFFERS_UNINITIALIZED);
+    if (status) {
+      RETURN_FAIL("q=%d: initialize: \"%s\"", q, status);
+    }
+    wuffs_gif__decoder__set_quirk_enabled(
+        &dec, wuffs_gif__quirk_image_bounds_are_strict, q);
 
-  const uint32_t width = 4;
-  const uint32_t height = 2;
-
-  if (wuffs_base__pixel_config__width(&ic.pixcfg) != width) {
-    RETURN_FAIL("width: got %" PRIu32 ", want %" PRIu32,
-                wuffs_base__pixel_config__width(&ic.pixcfg), width);
-  }
-  if (wuffs_base__pixel_config__height(&ic.pixcfg) != height) {
-    RETURN_FAIL("height: got %" PRIu32 ", want %" PRIu32,
-                wuffs_base__pixel_config__height(&ic.pixcfg), height);
-  }
-
-  wuffs_base__pixel_buffer pb = ((wuffs_base__pixel_buffer){});
-  status = wuffs_base__pixel_buffer__set_from_slice(&pb, &ic.pixcfg,
-                                                    global_pixel_slice);
-  if (status) {
-    RETURN_FAIL("set_from_slice: \"%s\"", status);
-  }
-
-  // See test/data/artificial/gif-frame-out-of-bounds.gif.make-artificial.txt
-  // for the want_frame_config_bounds and want_pixel_indexes source.
-
-  wuffs_base__rect_ie_u32 want_frame_config_bounds[4] = {
-      make_rect_ie_u32(1, 0, 4, 1),
-      make_rect_ie_u32(0, 1, 2, 2),
-      make_rect_ie_u32(0, 2, 1, 2),
-      make_rect_ie_u32(2, 0, 4, 2),
-  };
-
-  const char* want_pixel_indexes[4] = {
-      ".123....",
-      "....89..",
-      "........",
-      "..45..89",
-  };
-
-  uint32_t i;
-  for (i = 0; true; i++) {
-    wuffs_base__frame_config fc = ((wuffs_base__frame_config){});
-    {
-      status = wuffs_gif__decoder__decode_frame_config(&dec, &fc, src_reader);
-      if (i == WUFFS_TESTLIB_ARRAY_SIZE(want_frame_config_bounds)) {
-        if (status != wuffs_base__warning__end_of_data) {
-          RETURN_FAIL("decode_frame_config #%" PRIu32 ": got \"%s\"", i,
-                      status);
-        }
-        break;
-      }
-      if (status) {
-        RETURN_FAIL("decode_frame_config #%" PRIu32 ": got \"%s\"", i, status);
-      }
-
-      wuffs_base__rect_ie_u32 got = wuffs_base__frame_config__bounds(&fc);
-      wuffs_base__rect_ie_u32 want = want_frame_config_bounds[i];
-      if (!wuffs_base__rect_ie_u32__equals(&got, want)) {
-        RETURN_FAIL("decode_frame_config #%" PRIu32 ": bounds: got (%" PRIu32
-                    ", %" PRIu32 ")-(%" PRIu32 ", %" PRIu32 "), want (%" PRIu32
-                    ", %" PRIu32 ")-(%" PRIu32 ", %" PRIu32 ")",
-                    i, got.min_incl_x, got.min_incl_y, got.max_excl_x,
-                    got.max_excl_y, want.min_incl_x, want.min_incl_y,
-                    want.max_excl_x, want.max_excl_y);
-      }
+    wuffs_base__image_config ic = ((wuffs_base__image_config){});
+    wuffs_base__io_reader src_reader = wuffs_base__io_buffer__reader(&src);
+    status = wuffs_gif__decoder__decode_image_config(&dec, &ic, src_reader);
+    if (status) {
+      RETURN_FAIL("q=%d: decode_image_config: \"%s\"", q, status);
     }
 
-    {
-      wuffs_base__table_u8 p = wuffs_base__pixel_buffer__plane(&pb, 0);
-      uint32_t y, x;
-      for (y = 0; y < height; y++) {
-        for (x = 0; x < width; x++) {
-          p.ptr[(y * p.stride) + x] = 0;
+    // The nominal width and height for the overall image is 2×2, but its first
+    // frame extends those bounds to 4×2. See
+    // test/data/artificial/gif-frame-out-of-bounds.gif.make-artificial.txt for
+    // more discussion.
+
+    const uint32_t width = q ? 2 : 4;
+    const uint32_t height = 2;
+
+    if (wuffs_base__pixel_config__width(&ic.pixcfg) != width) {
+      RETURN_FAIL("q=%d: width: got %" PRIu32 ", want %" PRIu32, q,
+                  wuffs_base__pixel_config__width(&ic.pixcfg), width);
+    }
+    if (wuffs_base__pixel_config__height(&ic.pixcfg) != height) {
+      RETURN_FAIL("q=%d: height: got %" PRIu32 ", want %" PRIu32, q,
+                  wuffs_base__pixel_config__height(&ic.pixcfg), height);
+    }
+
+    wuffs_base__pixel_buffer pb = ((wuffs_base__pixel_buffer){});
+    status = wuffs_base__pixel_buffer__set_from_slice(&pb, &ic.pixcfg,
+                                                      global_pixel_slice);
+    if (status) {
+      RETURN_FAIL("q=%d: set_from_slice: \"%s\"", q, status);
+    }
+
+    // See test/data/artificial/gif-frame-out-of-bounds.gif.make-artificial.txt
+    // for the want_frame_config_bounds and want_pixel_indexes source.
+
+    wuffs_base__rect_ie_u32 want_frame_config_bounds[4] = {
+        q ? make_rect_ie_u32(1, 0, 2, 1) : make_rect_ie_u32(1, 0, 4, 1),
+        q ? make_rect_ie_u32(0, 1, 2, 2) : make_rect_ie_u32(0, 1, 2, 2),
+        q ? make_rect_ie_u32(0, 2, 1, 2) : make_rect_ie_u32(0, 2, 1, 2),
+        q ? make_rect_ie_u32(2, 0, 2, 2) : make_rect_ie_u32(2, 0, 4, 2),
+    };
+
+    const char* want_pixel_indexes[4] = {
+        q ? ".1.." : ".123....",
+        q ? "..89" : "....89..",
+        q ? "...." : "........",
+        q ? "...." : "..45..89",
+    };
+
+    uint32_t i;
+    for (i = 0; true; i++) {
+      wuffs_base__frame_config fc = ((wuffs_base__frame_config){});
+      {
+        status = wuffs_gif__decoder__decode_frame_config(&dec, &fc, src_reader);
+        if (i == WUFFS_TESTLIB_ARRAY_SIZE(want_frame_config_bounds)) {
+          if (status != wuffs_base__warning__end_of_data) {
+            RETURN_FAIL("q=%d: decode_frame_config #%" PRIu32 ": got \"%s\"", q,
+                        i, status);
+          }
+          break;
+        }
+        if (status) {
+          RETURN_FAIL("q=%d: decode_frame_config #%" PRIu32 ": got \"%s\"", q,
+                      i, status);
+        }
+
+        wuffs_base__rect_ie_u32 got = wuffs_base__frame_config__bounds(&fc);
+        wuffs_base__rect_ie_u32 want = want_frame_config_bounds[i];
+        if (!wuffs_base__rect_ie_u32__equals(&got, want)) {
+          RETURN_FAIL("q=%d: decode_frame_config #%" PRIu32
+                      ": bounds: got (%" PRIu32 ", %" PRIu32 ")-(%" PRIu32
+                      ", %" PRIu32 "), want (%" PRIu32 ", %" PRIu32
+                      ")-(%" PRIu32 ", %" PRIu32 ")",
+                      q, i, got.min_incl_x, got.min_incl_y, got.max_excl_x,
+                      got.max_excl_y, want.min_incl_x, want.min_incl_y,
+                      want.max_excl_x, want.max_excl_y);
         }
       }
 
-      status = wuffs_gif__decoder__decode_frame(&dec, &pb, src_reader,
-                                                global_work_slice, NULL);
-      if (status) {
-        RETURN_FAIL("decode_frame #%" PRIu32 ": got \"%s\"", i, status);
-      }
-
-      wuffs_base__rect_ie_u32 frame_rect =
-          wuffs_base__frame_config__bounds(&fc);
-      wuffs_base__rect_ie_u32 dirty_rect =
-          wuffs_gif__decoder__frame_dirty_rect(&dec);
-      if (!wuffs_base__rect_ie_u32__contains_rect(&frame_rect, dirty_rect)) {
-        RETURN_FAIL("internal error: frame_rect does not contain dirty_rect");
-      }
-
-      char got[(width * height) + 1];
-      for (y = 0; y < height; y++) {
-        for (x = 0; x < width; x++) {
-          uint8_t index = 0x0F & p.ptr[(y * p.stride) + x];
-          got[(y * width) + x] = index ? ('0' + index) : '.';
+      {
+        wuffs_base__table_u8 p = wuffs_base__pixel_buffer__plane(&pb, 0);
+        uint32_t y, x;
+        for (y = 0; y < height; y++) {
+          for (x = 0; x < width; x++) {
+            p.ptr[(y * p.stride) + x] = 0;
+          }
         }
-      }
-      got[width * height] = 0;
 
-      const char* want = want_pixel_indexes[i];
-      if (memcmp(got, want, width * height)) {
-        RETURN_FAIL("decode_frame #%" PRIu32 ": got \"%s\", want \"%s\"", i,
-                    got, want);
+        status = wuffs_gif__decoder__decode_frame(&dec, &pb, src_reader,
+                                                  global_work_slice, NULL);
+        if (status) {
+          RETURN_FAIL("q=%d: decode_frame #%" PRIu32 ": got \"%s\"", q, i,
+                      status);
+        }
+
+        wuffs_base__rect_ie_u32 frame_rect =
+            wuffs_base__frame_config__bounds(&fc);
+        wuffs_base__rect_ie_u32 dirty_rect =
+            wuffs_gif__decoder__frame_dirty_rect(&dec);
+        if (!wuffs_base__rect_ie_u32__contains_rect(&frame_rect, dirty_rect)) {
+          RETURN_FAIL(
+              "q=%d: internal error: frame_rect does not contain dirty_rect",
+              q);
+        }
+
+        char got[(width * height) + 1];
+        for (y = 0; y < height; y++) {
+          for (x = 0; x < width; x++) {
+            uint8_t index = 0x0F & p.ptr[(y * p.stride) + x];
+            got[(y * width) + x] = index ? ('0' + index) : '.';
+          }
+        }
+        got[width * height] = 0;
+
+        const char* want = want_pixel_indexes[i];
+        if (memcmp(got, want, width * height)) {
+          RETURN_FAIL("q=%d: decode_frame #%" PRIu32
+                      ": got \"%s\", want \"%s\"",
+                      q, i, got, want);
+        }
       }
     }
   }
diff --git a/test/data/artificial/gif-frame-out-of-bounds.gif.make-artificial.txt b/test/data/artificial/gif-frame-out-of-bounds.gif.make-artificial.txt
index 20ce402..da7111c 100644
--- a/test/data/artificial/gif-frame-out-of-bounds.gif.make-artificial.txt
+++ b/test/data/artificial/gif-frame-out-of-bounds.gif.make-artificial.txt
@@ -47,7 +47,8 @@
 #  - giflib enforces no particular policy. It provides image and frame bounds
 #    as encoded in the file and leaves it up to the caller how to respond.
 #
-# Wuffs chooses policy 4, the same as Chromium.
+# Wuffs chooses policy 4 by default, the same as Chromium. Wuffs can be
+# configured to choose policy 1, by enabling a 'quirk'.
 
 make gif