Add wuffs_gif__quirk_first_frame_local_palette_etc
diff --git a/release/c/wuffs-unsupported-snapshot.c b/release/c/wuffs-unsupported-snapshot.c
index fe530cb..56a2665 100644
--- a/release/c/wuffs-unsupported-snapshot.c
+++ b/release/c/wuffs-unsupported-snapshot.c
@@ -3324,6 +3324,13 @@
     wuffs_gif__quirk_delay_num_decoded_frames  //
         WUFFS_BASE__POTENTIALLY_UNUSED = 1041635328;
 
+#define WUFFS_GIF__QUIRK_FIRST_FRAME_LOCAL_PALETTE_MEANS_BLACK_BACKGROUND \
+  1041635334
+
+static const uint32_t                                                  //
+    wuffs_gif__quirk_first_frame_local_palette_means_black_background  //
+        WUFFS_BASE__POTENTIALLY_UNUSED = 1041635334;
+
 #define WUFFS_GIF__QUIRK_HONOR_BACKGROUND_COLOR 1041635329
 
 static const uint32_t                        //
@@ -3467,6 +3474,7 @@
     uint64_t f_metadata_chunk_length_value;
     uint64_t f_metadata_io_position;
     bool f_quirk_enabled_delay_num_decoded_frames;
+    bool f_quirk_enabled_first_frame_local_palette_means_black_background;
     bool f_quirk_enabled_honor_background_color;
     bool f_quirk_enabled_ignore_too_much_pixel_data;
     bool f_quirk_enabled_image_bounds_are_strict;
@@ -3481,6 +3489,7 @@
     bool f_seen_num_loops;
     uint32_t f_num_loops;
     uint32_t f_background_color_u32_argb_premul;
+    uint32_t f_black_color_u32_argb_premul;
     bool f_gc_has_transparent_index;
     uint8_t f_gc_transparent_index;
     uint8_t f_gc_disposal;
@@ -3523,6 +3532,10 @@
     wuffs_lzw__decoder f_lzw;
 
     struct {
+      uint8_t v_blend;
+      uint32_t v_background_color;
+    } s_decode_frame_config[1];
+    struct {
       uint64_t scratch;
     } s_skip_frame[1];
     struct {
@@ -8769,6 +8782,10 @@
   if (self->private_impl.f_call_sequence == 0) {
     if (a_quirk == 1041635328) {
       self->private_impl.f_quirk_enabled_delay_num_decoded_frames = a_enabled;
+    } else if (a_quirk == 1041635334) {
+      self->private_impl
+          .f_quirk_enabled_first_frame_local_palette_means_black_background =
+          a_enabled;
     } else if (a_quirk == 1041635329) {
       self->private_impl.f_quirk_enabled_honor_background_color = a_enabled;
     } else if (a_quirk == 1041635330) {
@@ -8841,6 +8858,12 @@
            (self->private_impl.f_frame_rect_y0 == 0) &&
            (self->private_impl.f_frame_rect_x1 == self->private_impl.f_width) &&
            (self->private_impl.f_frame_rect_y1 == self->private_impl.f_height));
+    } else if (v_ffio) {
+      self->private_impl.f_black_color_u32_argb_premul = 4278190080;
+    }
+    if (self->private_impl.f_background_color_u32_argb_premul == 77) {
+      self->private_impl.f_background_color_u32_argb_premul =
+          self->private_impl.f_black_color_u32_argb_premul;
     }
     if (a_dst != NULL) {
       wuffs_base__image_config__set(
@@ -9181,9 +9204,28 @@
 
   uint8_t v_blend = 0;
   uint32_t v_background_color = 0;
+  uint8_t v_flags = 0;
+
+  uint8_t* iop_a_src = NULL;
+  uint8_t* io0_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  uint8_t* io1_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
+  if (a_src.private_impl.buf) {
+    iop_a_src =
+        a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.ri;
+    if (!a_src.private_impl.mark) {
+      a_src.private_impl.mark = iop_a_src;
+      a_src.private_impl.limit =
+          a_src.private_impl.buf->data.ptr + a_src.private_impl.buf->meta.wi;
+    }
+    io0_a_src = a_src.private_impl.mark;
+    io1_a_src = a_src.private_impl.limit;
+  }
 
   uint32_t coro_susp_point = self->private_impl.p_decode_frame_config[0];
   if (coro_susp_point) {
+    v_blend = self->private_data.s_decode_frame_config[0].v_blend;
+    v_background_color =
+        self->private_data.s_decode_frame_config[0].v_background_color;
   }
   switch (coro_susp_point) {
     WUFFS_BASE__COROUTINE_SUSPENSION_POINT_0;
@@ -9193,21 +9235,45 @@
      wuffs_base__make_empty_struct());
     if (!self->private_impl.f_end_of_data) {
       if (self->private_impl.f_call_sequence == 0) {
+        if (a_src.private_impl.buf) {
+          a_src.private_impl.buf->meta.ri =
+              ((size_t)(iop_a_src - a_src.private_impl.buf->data.ptr));
+        }
         WUFFS_BASE__COROUTINE_SUSPENSION_POINT(1);
         status = wuffs_gif__decoder__decode_image_config(self, NULL, a_src);
+        if (a_src.private_impl.buf) {
+          iop_a_src = a_src.private_impl.buf->data.ptr +
+                      a_src.private_impl.buf->meta.ri;
+        }
         if (status) {
           goto suspend;
         }
       } else if (self->private_impl.f_call_sequence != 3) {
         if (self->private_impl.f_call_sequence == 4) {
+          if (a_src.private_impl.buf) {
+            a_src.private_impl.buf->meta.ri =
+                ((size_t)(iop_a_src - a_src.private_impl.buf->data.ptr));
+          }
           WUFFS_BASE__COROUTINE_SUSPENSION_POINT(2);
           status = wuffs_gif__decoder__skip_frame(self, a_src);
+          if (a_src.private_impl.buf) {
+            iop_a_src = a_src.private_impl.buf->data.ptr +
+                        a_src.private_impl.buf->meta.ri;
+          }
           if (status) {
             goto suspend;
           }
         }
+        if (a_src.private_impl.buf) {
+          a_src.private_impl.buf->meta.ri =
+              ((size_t)(iop_a_src - a_src.private_impl.buf->data.ptr));
+        }
         WUFFS_BASE__COROUTINE_SUSPENSION_POINT(3);
         status = wuffs_gif__decoder__decode_up_to_id_part1(self, a_src);
+        if (a_src.private_impl.buf) {
+          iop_a_src = a_src.private_impl.buf->data.ptr +
+                      a_src.private_impl.buf->meta.ri;
+        }
         if (status) {
           goto suspend;
         }
@@ -9218,11 +9284,23 @@
       goto ok;
     }
     v_blend = 0;
-    v_background_color = 0;
+    v_background_color = self->private_impl.f_black_color_u32_argb_premul;
     if (!self->private_impl.f_gc_has_transparent_index) {
       v_blend = 2;
       v_background_color =
           self->private_impl.f_background_color_u32_argb_premul;
+      if (self->private_impl
+              .f_quirk_enabled_first_frame_local_palette_means_black_background &&
+          (self->private_impl.f_num_decoded_frame_configs_value == 0)) {
+        while (((uint64_t)(io1_a_src - iop_a_src)) <= 0) {
+          status = wuffs_base__suspension__short_read;
+          WUFFS_BASE__COROUTINE_SUSPENSION_POINT_MAYBE_SUSPEND(4);
+        }
+        v_flags = wuffs_base__load_u8be(iop_a_src);
+        if ((v_flags & 128) != 0) {
+          v_background_color = self->private_impl.f_black_color_u32_argb_premul;
+        }
+      }
     }
     if (a_dst != NULL) {
       wuffs_base__frame_config__update(
@@ -9257,9 +9335,17 @@
       wuffs_base__status__is_suspension(status) ? coro_susp_point : 0;
   self->private_impl.active_coroutine =
       wuffs_base__status__is_suspension(status) ? 3 : 0;
+  self->private_data.s_decode_frame_config[0].v_blend = v_blend;
+  self->private_data.s_decode_frame_config[0].v_background_color =
+      v_background_color;
 
   goto exit;
 exit:
+  if (a_src.private_impl.buf) {
+    a_src.private_impl.buf->meta.ri =
+        ((size_t)(iop_a_src - a_src.private_impl.buf->data.ptr));
+  }
+
   if (wuffs_base__status__is_error(status)) {
     self->private_impl.magic = WUFFS_BASE__DISABLED;
   }
@@ -9860,7 +9946,7 @@
                (((uint32_t)(self->private_data.f_palettes[0][(v_j + 3)]))
                 << 24));
         } else {
-          self->private_impl.f_background_color_u32_argb_premul = 4278190080;
+          self->private_impl.f_background_color_u32_argb_premul = 77;
         }
       }
     }
diff --git a/std/gif/decode_gif.wuffs b/std/gif/decode_gif.wuffs
index 272fbf5..5ba6e7d 100644
--- a/std/gif/decode_gif.wuffs
+++ b/std/gif/decode_gif.wuffs
@@ -74,6 +74,18 @@
 // final frame. Enabling this quirk allows for matching that behavior.
 pub const quirk_delay_num_decoded_frames base.u32 = (0xF8586 << 10) | 0
 
+// When this quirk is enabled, the background color of the first frame is set
+// to black whenever that first frame has a local (frame-specific) palette.
+// That black can be either opaque black or transparent black, depending on
+// whether or not that first frame is opaque: whether that local palette
+// contains a transparent color.
+//
+// This has no effect unless quirk_honor_background_color is also enabled.
+//
+// There isn't really much of a rationale for this, other than it matches the
+// behavior of another GIF implementation.
+pub const quirk_first_frame_local_palette_means_black_background base.u32 = (0xF8586 << 10) | 6
+
 // When this quirk is enabled, the background color is taken from the GIF
 // instead of always being transparent black. If the background color index in
 // the GIF header is non-zero but less than the global palette's size, the
@@ -178,12 +190,13 @@
 	metadata_chunk_length_value base.u64,
 	metadata_io_position        base.u64,
 
-	quirk_enabled_delay_num_decoded_frames   base.bool,
-	quirk_enabled_honor_background_color     base.bool,
-	quirk_enabled_ignore_too_much_pixel_data base.bool,
-	quirk_enabled_image_bounds_are_strict    base.bool,
-	quirk_enabled_reject_empty_frame         base.bool,
-	quirk_enabled_reject_empty_palette       base.bool,
+	quirk_enabled_delay_num_decoded_frames                         base.bool,
+	quirk_enabled_first_frame_local_palette_means_black_background base.bool,
+	quirk_enabled_honor_background_color                           base.bool,
+	quirk_enabled_ignore_too_much_pixel_data                       base.bool,
+	quirk_enabled_image_bounds_are_strict                          base.bool,
+	quirk_enabled_reject_empty_frame                               base.bool,
+	quirk_enabled_reject_empty_palette                             base.bool,
 
 	delayed_num_decoded_frames         base.bool,
 	end_of_data                        base.bool,
@@ -201,6 +214,7 @@
 	num_loops      base.u32,
 
 	background_color_u32_argb_premul base.u32,
+	black_color_u32_argb_premul      base.u32,
 
 	gc_has_transparent_index base.bool,
 	gc_transparent_index     base.u8,
@@ -243,6 +257,8 @@
 	if this.call_sequence == 0 {
 		if args.quirk == quirk_delay_num_decoded_frames {
 			this.quirk_enabled_delay_num_decoded_frames = args.enabled
+		} else if args.quirk == quirk_first_frame_local_palette_means_black_background {
+			this.quirk_enabled_first_frame_local_palette_means_black_background = args.enabled
 		} else if args.quirk == quirk_honor_background_color {
 			this.quirk_enabled_honor_background_color = args.enabled
 		} else if args.quirk == quirk_ignore_too_much_pixel_data {
@@ -278,6 +294,13 @@
 			(this.frame_rect_y0 == 0) and
 			(this.frame_rect_x1 == this.width) and
 			(this.frame_rect_y1 == this.height)
+	} else if ffio {
+		// Use opaque black, not transparent black.
+		this.black_color_u32_argb_premul = 0xFF000000
+	}
+
+	if this.background_color_u32_argb_premul == 77 {
+		this.background_color_u32_argb_premul = this.black_color_u32_argb_premul
 	}
 
 	if args.dst <> nullptr {
@@ -401,6 +424,7 @@
 pub func decoder.decode_frame_config?(dst nptr base.frame_config, src base.io_reader) {
 	var blend            base.u8
 	var background_color base.u32
+	var flags            base.u8
 
 	this.ignore_metadata = true
 	this.dirty_y.reset!()
@@ -423,10 +447,26 @@
 	}
 
 	blend = 0
-	background_color = 0
+	background_color = this.black_color_u32_argb_premul
 	if not this.gc_has_transparent_index {
 		blend = 2  // 2 is WUFFS_BASE__ANIMATION_BLEND__OPAQUE.
 		background_color = this.background_color_u32_argb_premul
+
+		// If the quirk is enabled and the first frame has a local color
+		// palette, its background color is black.
+		if this.quirk_enabled_first_frame_local_palette_means_black_background and
+			(this.num_decoded_frame_configs_value == 0) {
+
+			while args.src.available() <= 0,
+				post args.src.available() > 0,
+			{
+				yield? base."$short read"
+			}
+			flags = args.src.peek_u8()
+			if (flags & 0x80) <> 0 {
+				background_color = this.black_color_u32_argb_premul
+			}
+		}
 	}
 
 	if args.dst <> nullptr {
@@ -602,7 +642,11 @@
 					((this.palettes[0][j + 2] as base.u32) << 16) |
 					((this.palettes[0][j + 3] as base.u32) << 24)
 			} else {
-				this.background_color_u32_argb_premul = 0xFF000000
+				// The background color is either opaque black or transparent
+				// black. We set it to an arbitrary nonsense value (77) for
+				// now, and set it to its real value later, once we know
+				// whether the first frame is opaque (the ffio value).
+				this.background_color_u32_argb_premul = 77
 			}
 		}
 	}