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
}
}
}