std/vp8: accept VP8X (but ignore any ALPH chunk)
diff --git a/doc/note/quirks.md b/doc/note/quirks.md index 8ebdf18..6759f94 100644 --- a/doc/note/quirks.md +++ b/doc/note/quirks.md
@@ -76,11 +76,12 @@ Package-specific quirks: -- [GIF image decoder quirks](/std/gif/decode_quirks.wuffs) +- [GIF decoder quirks](/std/gif/decode_quirks.wuffs) - [JPEG decoder quirks](/std/jpeg/decode_quirks.wuffs) - [JSON decoder quirks](/std/json/decode_quirks.wuffs) - [LZMA decoder quirks](/std/lzma/decode_quirks.wuffs) - [LZW decoder quirks](/std/lzw/decode_quirks.wuffs) -- [TH decoder quirks](/std/thumbhash/decode_quirks.wuffs) +- [THUMBHASH decoder quirks](/std/thumbhash/decode_quirks.wuffs) +- [VP8 decoder quirks](/std/vp8/decode_quirks.wuffs) - [XZ decoder quirks](/std/xz/decode_quirks.wuffs) - [ZLIB decoder quirks](/std/zlib/decode_quirks.wuffs)
diff --git a/doc/std/image-decoders.md b/doc/std/image-decoders.md index 92a26b0..ac55351 100644 --- a/doc/std/image-decoders.md +++ b/doc/std/image-decoders.md
@@ -132,8 +132,9 @@ - [std/nie](/std/nie) - [std/png](/std/png) - [std/qoi](/std/qoi) -- [std/tga](/std/tga) +- [std/targa](/std/targa) - [std/thumbhash](/std/thumbhash) +- [std/vp8](/std/vp8) - [std/wbmp](/std/wbmp) - [std/webp](/std/webp) @@ -160,7 +161,8 @@ - [GIF decoder quirks](/std/gif/decode_quirks.wuffs) - [JPEG decoder quirks](/std/jpeg/decode_quirks.wuffs) -- [TH decoder quirks](/std/thumbhash/decode_quirks.wuffs) +- [THUMBHASH decoder quirks](/std/thumbhash/decode_quirks.wuffs) +- [VP8 decoder quirks](/std/vp8/decode_quirks.wuffs) ## Related Documentation
diff --git a/internal/cgen/expr.go b/internal/cgen/expr.go index fae4007..8a09124 100644 --- a/internal/cgen/expr.go +++ b/internal/cgen/expr.go
@@ -551,8 +551,16 @@ return fmt.Errorf("cannot generate user-defined method call %q for receiver type %q", n.Str(g.tm), recv.MType().Str(g.tm)) } + qid := recvTyp.QID() - b.printf("%s%s__%s(", g.packagePrefix(qid), qid[1].Str(g.tm), method.Ident().Str(g.tm)) + pkgStr := g.packagePrefix(qid) + typStr := qid[1].Str(g.tm) + methodStr := method.Ident().Str(g.tm) + if (qid[0] == t.IDBase) && qid[1].IsRangeType() && strings.HasPrefix(methodStr, "get_") { + pkgStr = "wuffs_private_impl__" + } + b.printf("%s%s__%s(", pkgStr, typStr, methodStr) + if !recvTyp.IsEtcUtilityType() { b.writes(addr) if err := g.writeExpr(b, recv, false, depth); err != nil {
diff --git a/release/c/wuffs-unsupported-snapshot.c b/release/c/wuffs-unsupported-snapshot.c index aa66514..81444a6 100644 --- a/release/c/wuffs-unsupported-snapshot.c +++ b/release/c/wuffs-unsupported-snapshot.c
@@ -15457,7 +15457,9 @@ // ---------------- Status Codes extern const char wuffs_vp8__error__bad_quirk_source_length[]; +extern const char wuffs_vp8__error__bad_quirk_width_and_height[]; extern const char wuffs_vp8__error__bad_header[]; +extern const char wuffs_vp8__error__inconsistent_width_and_height[]; extern const char wuffs_vp8__error__missing_quirk_source_length[]; extern const char wuffs_vp8__error__truncated_input[]; extern const char wuffs_vp8__error__unsupported_quirk_source_length[]; @@ -15465,6 +15467,8 @@ // ---------------- Public Consts +#define WUFFS_VP8__QUIRK_WIDTH_AND_HEIGHT 1836840960u + #define WUFFS_VP8__DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE 553648127u // ---------------- Struct Declarations @@ -15638,6 +15642,7 @@ bool f_is_key_frame; uint64_t f_frame_config_io_position; uint64_t f_quirk_source_length; + uint64_t f_quirk_width_and_height; uint32_t f_partitioned_data_length; uint32_t f_workbuf_yuv_y_stride; uint32_t f_workbuf_yuv_uv_stride; @@ -16214,22 +16219,28 @@ // ---------------- Status Codes +extern const char wuffs_webp__error__bad_alph_vp8l_combination[]; extern const char wuffs_webp__error__bad_huffman_code_over_subscribed[]; extern const char wuffs_webp__error__bad_huffman_code_under_subscribed[]; extern const char wuffs_webp__error__bad_huffman_code[]; +extern const char wuffs_webp__error__bad_vp8x_chunk[]; extern const char wuffs_webp__error__bad_back_reference[]; +extern const char wuffs_webp__error__bad_chunk_order[]; extern const char wuffs_webp__error__bad_color_cache[]; extern const char wuffs_webp__error__bad_header[]; extern const char wuffs_webp__error__bad_transform[]; extern const char wuffs_webp__error__short_chunk[]; extern const char wuffs_webp__error__truncated_input[]; -extern const char wuffs_webp__error__unsupported_number_of_huffman_groups[]; +extern const char wuffs_webp__error__unsupported_vp8x_vp8_chunk_length[]; extern const char wuffs_webp__error__unsupported_webp_file[]; +extern const char wuffs_webp__error__unsupported_number_of_huffman_groups[]; // ---------------- Public Consts #define WUFFS_WEBP__DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE 1275068416u +#define WUFFS_WEBP__VP8X_MAX_INCL_VP8_CHUNK_LENGTH_DEFAULT 8388608u + // ---------------- Struct Declarations typedef struct wuffs_webp__decoder__struct wuffs_webp__decoder; @@ -16398,7 +16409,8 @@ uint8_t f_call_sequence; uint8_t f_code_length_code_lengths[19]; bool f_sub_chunk_has_padding; - bool f_is_vp8_lossy; + uint8_t f_variant; + uint8_t f_vp8x_header_flags; uint64_t f_frame_config_io_position; uint32_t f_riff_chunk_length; uint32_t f_sub_chunk_length; @@ -16430,10 +16442,12 @@ uint32_t p_do_decode_image_config; uint32_t p_do_decode_image_config_limited; uint32_t p_do_decode_image_config_limited_vp8l; + uint32_t p_do_decode_image_config_limited_vp8x; uint32_t p_decode_frame_config; uint32_t p_do_decode_frame_config; uint32_t p_decode_frame; uint32_t p_do_decode_frame; + uint32_t p_do_decode_frame_vp8l; uint32_t p_decode_transform; uint32_t p_decode_color_cache_parameters; uint32_t p_decode_hg_table; @@ -16498,9 +16512,16 @@ uint64_t scratch; } s_do_decode_image_config_limited_vp8l; struct { - uint32_t v_width; + uint64_t scratch; + } s_do_decode_image_config_limited_vp8x; + struct { + uint32_t v_chunk_type; + uint64_t scratch; } s_do_decode_frame; struct { + uint32_t v_width; + } s_do_decode_frame_vp8l; + struct { uint32_t v_transform_type; uint32_t v_tile_size_log2; } s_decode_transform; @@ -80931,7 +80952,9 @@ // ---------------- Status Codes Implementations const char wuffs_vp8__error__bad_quirk_source_length[] = "#vp8: bad QUIRK_SOURCE_LENGTH"; +const char wuffs_vp8__error__bad_quirk_width_and_height[] = "#vp8: bad QUIRK_WIDTH_AND_HEIGHT"; const char wuffs_vp8__error__bad_header[] = "#vp8: bad header"; +const char wuffs_vp8__error__inconsistent_width_and_height[] = "#vp8: inconsistent width and height"; const char wuffs_vp8__error__missing_quirk_source_length[] = "#vp8: missing QUIRK_SOURCE_LENGTH"; const char wuffs_vp8__error__truncated_input[] = "#vp8: truncated input"; const char wuffs_vp8__error__unsupported_quirk_source_length[] = "#vp8: unsupported QUIRK_SOURCE_LENGTH"; @@ -81826,6 +81849,8 @@ 9u, 12u, 13u, 10u, 7u, 11u, 14u, 15u, }; +#define WUFFS_VP8__QUIRKS_BASE 1836840960u + // ---------------- Private Initializer Prototypes // ---------------- Private Function Prototypes @@ -82134,6 +82159,11 @@ wuffs_base__io_buffer* a_src); WUFFS_BASE__GENERATED_C_CODE +static wuffs_base__empty_struct +wuffs_vp8__decoder__calculate_mbw_mbh( + wuffs_vp8__decoder* self); + +WUFFS_BASE__GENERATED_C_CODE static wuffs_base__status wuffs_vp8__decoder__do_decode_frame_config( wuffs_vp8__decoder* self, @@ -85191,6 +85221,8 @@ if (a_key == 3u) { return self->private_impl.f_quirk_source_length; + } else if (a_key == 1836840960u) { + return self->private_impl.f_quirk_width_and_height; } return 0u; } @@ -85213,8 +85245,38 @@ : wuffs_base__error__initialize_not_called); } + uint32_t v_w = 0; + uint32_t v_h = 0; + if (a_key == 3u) { - self->private_impl.f_quirk_source_length = a_value; + if ((a_value & 1u) == 0u) { + if (a_value != 0u) { + return wuffs_base__make_status(wuffs_vp8__error__bad_quirk_source_length); + } + self->private_impl.f_quirk_source_length = 0u; + self->private_impl.f_partitioned_data_length = 0u; + } else if ((a_value >> 1u) < 10u) { + return wuffs_base__make_status(wuffs_vp8__error__bad_quirk_source_length); + } else if ((a_value >> 1u) > 150994945u) { + return wuffs_base__make_status(wuffs_vp8__error__unsupported_quirk_source_length); + } else { + self->private_impl.f_quirk_source_length = a_value; + self->private_impl.f_partitioned_data_length = (((uint32_t)((a_value >> 1u))) - 10u); + } + return wuffs_base__make_status(NULL); + } else if (a_key == 1836840960u) { + v_w = ((uint32_t)(a_value)); + v_h = ((uint32_t)(a_value)); + if ((v_w < 1u) || + (16383u < v_w) || + (v_h < 1u) || + (16383u < v_h)) { + return wuffs_base__make_status(wuffs_vp8__error__bad_quirk_width_and_height); + } + self->private_impl.f_quirk_width_and_height = a_value; + self->private_impl.f_width = v_w; + self->private_impl.f_height = v_h; + wuffs_vp8__decoder__calculate_mbw_mbh(self); return wuffs_base__make_status(NULL); } return wuffs_base__make_status(wuffs_base__error__unsupported_option); @@ -85317,17 +85379,10 @@ status = wuffs_base__make_status(wuffs_base__error__bad_call_sequence); goto exit; } - if ((self->private_impl.f_quirk_source_length & 1u) == 0u) { + if (self->private_impl.f_quirk_source_length == 0u) { status = wuffs_base__make_status(wuffs_vp8__error__missing_quirk_source_length); goto exit; - } else if ((self->private_impl.f_quirk_source_length >> 1u) < 10u) { - status = wuffs_base__make_status(wuffs_vp8__error__bad_quirk_source_length); - goto exit; - } else if ((self->private_impl.f_quirk_source_length >> 1u) > 150994945u) { - status = wuffs_base__make_status(wuffs_vp8__error__unsupported_quirk_source_length); - goto exit; } - self->private_impl.f_partitioned_data_length = (((uint32_t)((self->private_impl.f_quirk_source_length >> 1u))) - 10u); { WUFFS_BASE__COROUTINE_SUSPENSION_POINT(1); uint32_t t_0; @@ -85431,13 +85486,14 @@ } self->private_impl.f_width = (16383u & v_c32); self->private_impl.f_height = (16383u & (v_c32 >> 16u)); - self->private_impl.f_mbw = ((self->private_impl.f_width + 15u) / 16u); - self->private_impl.f_mbh = ((self->private_impl.f_height + 15u) / 16u); - self->private_impl.f_workbuf_yuv_y_stride = (self->private_impl.f_mbw * 16u); - self->private_impl.f_workbuf_yuv_uv_stride = (self->private_impl.f_mbw * 8u); - self->private_impl.f_workbuf_yuv_y_end = (((uint64_t)(self->private_impl.f_workbuf_yuv_y_stride)) * ((uint64_t)((self->private_impl.f_mbh * 16u)))); - self->private_impl.f_workbuf_yuv_u_end = (self->private_impl.f_workbuf_yuv_y_end + (((uint64_t)(self->private_impl.f_workbuf_yuv_uv_stride)) * ((uint64_t)((self->private_impl.f_mbh * 8u))))); - self->private_impl.f_workbuf_yuv_v_end = (self->private_impl.f_workbuf_yuv_u_end + (((uint64_t)(self->private_impl.f_workbuf_yuv_uv_stride)) * ((uint64_t)((self->private_impl.f_mbh * 8u))))); + if ((self->private_impl.f_width == 0u) || (self->private_impl.f_height == 0u)) { + status = wuffs_base__make_status(wuffs_vp8__error__bad_header); + goto exit; + } else if ((self->private_impl.f_quirk_width_and_height != 0u) && (self->private_impl.f_quirk_width_and_height != (((uint64_t)(self->private_impl.f_width)) | (((uint64_t)(self->private_impl.f_height)) << 32u)))) { + status = wuffs_base__make_status(wuffs_vp8__error__inconsistent_width_and_height); + goto exit; + } + wuffs_vp8__decoder__calculate_mbw_mbh(self); 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( @@ -85470,6 +85526,22 @@ return status; } +// -------- func vp8.decoder.calculate_mbw_mbh + +WUFFS_BASE__GENERATED_C_CODE +static wuffs_base__empty_struct +wuffs_vp8__decoder__calculate_mbw_mbh( + wuffs_vp8__decoder* self) { + self->private_impl.f_mbw = ((self->private_impl.f_width + 15u) / 16u); + self->private_impl.f_mbh = ((self->private_impl.f_height + 15u) / 16u); + self->private_impl.f_workbuf_yuv_y_stride = (self->private_impl.f_mbw * 16u); + self->private_impl.f_workbuf_yuv_uv_stride = (self->private_impl.f_mbw * 8u); + self->private_impl.f_workbuf_yuv_y_end = (((uint64_t)(self->private_impl.f_workbuf_yuv_y_stride)) * ((uint64_t)((self->private_impl.f_mbh * 16u)))); + self->private_impl.f_workbuf_yuv_u_end = (self->private_impl.f_workbuf_yuv_y_end + (((uint64_t)(self->private_impl.f_workbuf_yuv_uv_stride)) * ((uint64_t)((self->private_impl.f_mbh * 8u))))); + self->private_impl.f_workbuf_yuv_v_end = (self->private_impl.f_workbuf_yuv_u_end + (((uint64_t)(self->private_impl.f_workbuf_yuv_uv_stride)) * ((uint64_t)((self->private_impl.f_mbh * 8u))))); + return wuffs_base__make_empty_struct(); +} + // -------- func vp8.decoder.decode_frame_config WUFFS_BASE__GENERATED_C_CODE @@ -86922,17 +86994,21 @@ // ---------------- Status Codes Implementations +const char wuffs_webp__error__bad_alph_vp8l_combination[] = "#webp: bad ALPH+VP8L combination"; const char wuffs_webp__error__bad_huffman_code_over_subscribed[] = "#webp: bad Huffman code (over-subscribed)"; const char wuffs_webp__error__bad_huffman_code_under_subscribed[] = "#webp: bad Huffman code (under-subscribed)"; const char wuffs_webp__error__bad_huffman_code[] = "#webp: bad Huffman code"; +const char wuffs_webp__error__bad_vp8x_chunk[] = "#webp: bad VP8X chunk"; const char wuffs_webp__error__bad_back_reference[] = "#webp: bad back-reference"; +const char wuffs_webp__error__bad_chunk_order[] = "#webp: bad chunk order"; const char wuffs_webp__error__bad_color_cache[] = "#webp: bad color cache"; const char wuffs_webp__error__bad_header[] = "#webp: bad header"; const char wuffs_webp__error__bad_transform[] = "#webp: bad transform"; const char wuffs_webp__error__short_chunk[] = "#webp: short chunk"; const char wuffs_webp__error__truncated_input[] = "#webp: truncated input"; -const char wuffs_webp__error__unsupported_number_of_huffman_groups[] = "#webp: unsupported number of Huffman groups"; +const char wuffs_webp__error__unsupported_vp8x_vp8_chunk_length[] = "#webp: unsupported VP8X/VP8 chunk length"; const char wuffs_webp__error__unsupported_webp_file[] = "#webp: unsupported WebP file"; +const char wuffs_webp__error__unsupported_number_of_huffman_groups[] = "#webp: unsupported number of Huffman groups"; const char wuffs_webp__error__internal_error_inconsistent_huffman_code[] = "#webp: internal error: inconsistent Huffman code"; const char wuffs_webp__error__internal_error_inconsistent_dst_buffer[] = "#webp: internal error: inconsistent dst buffer"; const char wuffs_webp__error__internal_error_inconsistent_n_bits[] = "#webp: internal error: inconsistent n_bits"; @@ -87116,6 +87192,12 @@ WUFFS_BASE__GENERATED_C_CODE static wuffs_base__status +wuffs_webp__decoder__do_decode_image_config_limited_vp8x( + wuffs_webp__decoder* self, + wuffs_base__io_buffer* a_src); + +WUFFS_BASE__GENERATED_C_CODE +static wuffs_base__status wuffs_webp__decoder__do_decode_frame_config( wuffs_webp__decoder* self, wuffs_base__frame_config* a_dst, @@ -87133,6 +87215,16 @@ WUFFS_BASE__GENERATED_C_CODE static wuffs_base__status +wuffs_webp__decoder__do_decode_frame_vp8l( + wuffs_webp__decoder* self, + wuffs_base__pixel_buffer* a_dst, + wuffs_base__io_buffer* a_src, + wuffs_base__pixel_blend a_blend, + wuffs_base__slice_u8 a_workbuf, + wuffs_base__decode_frame_options* a_opts); + +WUFFS_BASE__GENERATED_C_CODE +static wuffs_base__status wuffs_webp__decoder__decode_transform( wuffs_webp__decoder* self, wuffs_base__io_buffer* a_src, @@ -89288,7 +89380,7 @@ WUFFS_BASE__COROUTINE_SUSPENSION_POINT_MAYBE_SUSPEND(5); } 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 ( ! self->private_impl.f_is_vp8_lossy && (a_dst != NULL)) { + if ((self->private_impl.f_variant != 1u) && (a_dst != NULL)) { wuffs_base__image_config__set( a_dst, 2164295816u, @@ -89410,11 +89502,11 @@ v_c32 = t_1; } if (v_c32 == 540561494u) { - self->private_impl.f_is_vp8_lossy = true; + self->private_impl.f_variant = 1u; } else if (v_c32 == 1278758998u) { + self->private_impl.f_variant = 2u; } else if (v_c32 == 1480085590u) { - status = wuffs_base__make_status(wuffs_webp__error__unsupported_webp_file); - goto exit; + self->private_impl.f_variant = 4u; } else { status = wuffs_base__make_status(wuffs_webp__error__bad_header); goto exit; @@ -89453,8 +89545,14 @@ goto exit; } self->private_impl.f_sub_chunk_has_padding = ((self->private_impl.f_sub_chunk_length & 1u) != 0u); - if (self->private_impl.f_is_vp8_lossy) { + if (self->private_impl.f_variant == 1u) { wuffs_vp8__decoder__set_quirk(&self->private_data.f_vp8, 3u, ((((uint64_t)(self->private_impl.f_sub_chunk_length)) << 1u) | 1u)); + } else if (self->private_impl.f_variant == 2u) { + } else if (self->private_impl.f_sub_chunk_length != 10u) { + status = wuffs_base__make_status(wuffs_webp__error__bad_header); + goto exit; + } else { + wuffs_vp8__decoder__set_quirk(&self->private_data.f_vp8, 3u, 16777217u); } while (true) { { @@ -89468,7 +89566,7 @@ a_src->meta.wi = n; } v_r_mark = ((uint64_t)(iop_a_src - io0_a_src)); - if (self->private_impl.f_is_vp8_lossy) { + if (self->private_impl.f_variant == 1u) { { if (a_src) { a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr)); @@ -89479,7 +89577,7 @@ iop_a_src = a_src->data.ptr + a_src->meta.ri; } } - } else { + } else if (self->private_impl.f_variant == 2u) { { if (a_src) { a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr)); @@ -89490,6 +89588,17 @@ iop_a_src = a_src->data.ptr + a_src->meta.ri; } } + } else { + { + if (a_src) { + a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr)); + } + wuffs_base__status t_5 = wuffs_webp__decoder__do_decode_image_config_limited_vp8x(self, a_src); + v_status = t_5; + if (a_src) { + iop_a_src = a_src->data.ptr + a_src->meta.ri; + } + } } wuffs_private_impl__u32__sat_sub_indirect(&self->private_impl.f_sub_chunk_length, ((uint32_t)(wuffs_private_impl__io__count_since(v_r_mark, ((uint64_t)(iop_a_src - io0_a_src)))))); io2_a_src = o_0_io2_a_src; @@ -89638,6 +89747,166 @@ return status; } +// -------- func webp.decoder.do_decode_image_config_limited_vp8x + +WUFFS_BASE__GENERATED_C_CODE +static wuffs_base__status +wuffs_webp__decoder__do_decode_image_config_limited_vp8x( + wuffs_webp__decoder* self, + wuffs_base__io_buffer* a_src) { + wuffs_base__status status = wuffs_base__make_status(NULL); + + uint32_t v_c32 = 0; + uint32_t v_transform_size = 0; + wuffs_base__status v_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_do_decode_image_config_limited_vp8x; + switch (coro_susp_point) { + WUFFS_BASE__COROUTINE_SUSPENSION_POINT_0; + + { + WUFFS_BASE__COROUTINE_SUSPENSION_POINT(1); + uint32_t t_0; + if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 4)) { + t_0 = wuffs_base__peek_u32le__no_bounds_check(iop_a_src); + iop_a_src += 4; + } else { + self->private_data.s_do_decode_image_config_limited_vp8x.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_do_decode_image_config_limited_vp8x.scratch; + uint32_t num_bits_0 = ((uint32_t)(*scratch >> 56)); + *scratch <<= 8; + *scratch >>= 8; + *scratch |= ((uint64_t)(*iop_a_src++)) << num_bits_0; + if (num_bits_0 == 24) { + t_0 = ((uint32_t)(*scratch)); + break; + } + num_bits_0 += 8u; + *scratch |= ((uint64_t)(num_bits_0)) << 56; + } + } + v_c32 = t_0; + } + if ((v_c32 & 4294967233u) != 0u) { + status = wuffs_base__make_status(wuffs_webp__error__unsupported_webp_file); + goto exit; + } + self->private_impl.f_vp8x_header_flags = ((uint8_t)((v_c32 | 1u))); + { + WUFFS_BASE__COROUTINE_SUSPENSION_POINT(3); + uint32_t t_1; + if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 3)) { + t_1 = ((uint32_t)(wuffs_base__peek_u24le__no_bounds_check(iop_a_src))); + iop_a_src += 3; + } else { + self->private_data.s_do_decode_image_config_limited_vp8x.scratch = 0; + WUFFS_BASE__COROUTINE_SUSPENSION_POINT(4); + 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_limited_vp8x.scratch; + uint32_t num_bits_1 = ((uint32_t)(*scratch >> 56)); + *scratch <<= 8; + *scratch >>= 8; + *scratch |= ((uint64_t)(*iop_a_src++)) << num_bits_1; + if (num_bits_1 == 16) { + t_1 = ((uint32_t)(*scratch)); + break; + } + num_bits_1 += 8u; + *scratch |= ((uint64_t)(num_bits_1)) << 56; + } + } + v_c32 = t_1; + } + if (v_c32 >= 16383u) { + status = wuffs_base__make_status(wuffs_webp__error__unsupported_webp_file); + goto exit; + } + self->private_impl.f_width = ((v_c32 & 16383u) + 1u); + { + WUFFS_BASE__COROUTINE_SUSPENSION_POINT(5); + uint32_t t_2; + if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 3)) { + t_2 = ((uint32_t)(wuffs_base__peek_u24le__no_bounds_check(iop_a_src))); + iop_a_src += 3; + } else { + self->private_data.s_do_decode_image_config_limited_vp8x.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_limited_vp8x.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 == 16) { + t_2 = ((uint32_t)(*scratch)); + break; + } + num_bits_2 += 8u; + *scratch |= ((uint64_t)(num_bits_2)) << 56; + } + } + v_c32 = t_2; + } + if (v_c32 >= 16383u) { + status = wuffs_base__make_status(wuffs_webp__error__unsupported_webp_file); + goto exit; + } + self->private_impl.f_height = ((v_c32 & 16383u) + 1u); + v_transform_size = (4u * ((self->private_impl.f_width + 3u) >> 2u) * ((self->private_impl.f_height + 3u) >> 2u)); + self->private_impl.f_workbuf_offset_for_transform[0u] = ((4u * self->private_impl.f_width * self->private_impl.f_height) + 0u); + self->private_impl.f_workbuf_offset_for_transform[1u] = ((4u * self->private_impl.f_width * self->private_impl.f_height) + v_transform_size); + self->private_impl.f_workbuf_offset_for_transform[2u] = ((4u * self->private_impl.f_width * self->private_impl.f_height) + (2u * v_transform_size)); + self->private_impl.f_workbuf_offset_for_transform[3u] = ((4u * self->private_impl.f_width * self->private_impl.f_height) + (3u * v_transform_size)); + v_status = wuffs_vp8__decoder__set_quirk(&self->private_data.f_vp8, 1836840960u, (((uint64_t)(self->private_impl.f_width)) | (((uint64_t)(self->private_impl.f_height)) << 32u))); + if ( ! wuffs_base__status__is_ok(&v_status)) { + status = wuffs_base__make_status(wuffs_webp__error__unsupported_webp_file); + goto exit; + } + + goto ok; + ok: + self->private_impl.p_do_decode_image_config_limited_vp8x = 0; + goto exit; + } + + goto suspend; + suspend: + self->private_impl.p_do_decode_image_config_limited_vp8x = 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 webp.decoder.decode_frame_config WUFFS_BASE__GENERATED_C_CODE @@ -89674,7 +89943,7 @@ WUFFS_BASE__COROUTINE_SUSPENSION_POINT_0; while (true) { - if (self->private_impl.f_is_vp8_lossy) { + if (self->private_impl.f_variant == 1u) { { wuffs_base__status t_0 = wuffs_vp8__decoder__decode_frame_config(&self->private_data.f_vp8, a_dst, a_src); v_status = t_0; @@ -89762,7 +90031,7 @@ status = wuffs_base__make_status(wuffs_base__note__end_of_data); goto ok; } - if (a_dst != NULL) { + if ((self->private_impl.f_variant != 1u) && (a_dst != NULL)) { wuffs_base__frame_config__set( a_dst, wuffs_base__utility__make_rect_ie_u32( @@ -89837,26 +90106,14 @@ WUFFS_BASE__COROUTINE_SUSPENSION_POINT_0; while (true) { - if (self->private_impl.f_is_vp8_lossy) { - { - wuffs_base__status t_0 = wuffs_vp8__decoder__decode_frame(&self->private_data.f_vp8, - a_dst, - a_src, - a_blend, - a_workbuf, - a_opts); - v_status = t_0; - } - } else { - { - wuffs_base__status t_1 = wuffs_webp__decoder__do_decode_frame(self, - a_dst, - a_src, - a_blend, - a_workbuf, - a_opts); - v_status = t_1; - } + { + wuffs_base__status t_0 = wuffs_webp__decoder__do_decode_frame(self, + a_dst, + a_src, + a_blend, + a_workbuf, + a_opts); + v_status = t_0; } if ((v_status.repr == wuffs_base__suspension__short_read) && (a_src && a_src->meta.closed)) { status = wuffs_base__make_status(wuffs_webp__error__truncated_input); @@ -89897,6 +90154,249 @@ wuffs_base__decode_frame_options* a_opts) { wuffs_base__status status = wuffs_base__make_status(NULL); + uint32_t v_chunk_type = 0; + uint32_t v_chunk_length = 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_do_decode_frame; + if (coro_susp_point) { + v_chunk_type = self->private_data.s_do_decode_frame.v_chunk_type; + } + switch (coro_susp_point) { + WUFFS_BASE__COROUTINE_SUSPENSION_POINT_0; + + if (self->private_impl.f_call_sequence == 64u) { + } else if (self->private_impl.f_call_sequence < 64u) { + if (a_src) { + a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr)); + } + WUFFS_BASE__COROUTINE_SUSPENSION_POINT(1); + status = wuffs_webp__decoder__do_decode_frame_config(self, NULL, a_src); + if (a_src) { + iop_a_src = a_src->data.ptr + a_src->meta.ri; + } + if (status.repr) { + goto suspend; + } + } else { + status = wuffs_base__make_status(wuffs_base__note__end_of_data); + goto ok; + } + while (((uint8_t)(self->private_impl.f_variant & 3u)) == 0u) { + { + WUFFS_BASE__COROUTINE_SUSPENSION_POINT(2); + uint32_t t_0; + if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 4)) { + t_0 = wuffs_base__peek_u32le__no_bounds_check(iop_a_src); + iop_a_src += 4; + } else { + self->private_data.s_do_decode_frame.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_do_decode_frame.scratch; + uint32_t num_bits_0 = ((uint32_t)(*scratch >> 56)); + *scratch <<= 8; + *scratch >>= 8; + *scratch |= ((uint64_t)(*iop_a_src++)) << num_bits_0; + if (num_bits_0 == 24) { + t_0 = ((uint32_t)(*scratch)); + break; + } + num_bits_0 += 8u; + *scratch |= ((uint64_t)(num_bits_0)) << 56; + } + } + v_chunk_type = t_0; + } + { + WUFFS_BASE__COROUTINE_SUSPENSION_POINT(4); + uint32_t t_1; + if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 4)) { + t_1 = wuffs_base__peek_u32le__no_bounds_check(iop_a_src); + iop_a_src += 4; + } else { + self->private_data.s_do_decode_frame.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_do_decode_frame.scratch; + uint32_t num_bits_1 = ((uint32_t)(*scratch >> 56)); + *scratch <<= 8; + *scratch >>= 8; + *scratch |= ((uint64_t)(*iop_a_src++)) << num_bits_1; + if (num_bits_1 == 24) { + t_1 = ((uint32_t)(*scratch)); + break; + } + num_bits_1 += 8u; + *scratch |= ((uint64_t)(num_bits_1)) << 56; + } + } + v_chunk_length = t_1; + } + if (v_chunk_type == 1346585417u) { + if (self->private_impl.f_variant > 4u) { + status = wuffs_base__make_status(wuffs_webp__error__bad_chunk_order); + goto exit; + } else if (((uint8_t)(self->private_impl.f_vp8x_header_flags & 32u)) == 0u) { + status = wuffs_base__make_status(wuffs_webp__error__bad_vp8x_chunk); + goto exit; + } + self->private_impl.f_variant = 8u; + } else if (v_chunk_type == 1296649793u) { + if (self->private_impl.f_variant > 8u) { + status = wuffs_base__make_status(wuffs_webp__error__bad_chunk_order); + goto exit; + } else if (((uint8_t)(self->private_impl.f_vp8x_header_flags & 2u)) == 0u) { + status = wuffs_base__make_status(wuffs_webp__error__bad_vp8x_chunk); + goto exit; + } + self->private_impl.f_variant = 16u; + } else if (v_chunk_type == 1179471425u) { + if (self->private_impl.f_variant > 16u) { + status = wuffs_base__make_status(wuffs_webp__error__bad_chunk_order); + goto exit; + } else if (((uint8_t)(self->private_impl.f_vp8x_header_flags & 2u)) == 0u) { + status = wuffs_base__make_status(wuffs_webp__error__bad_vp8x_chunk); + goto exit; + } + self->private_impl.f_variant = 32u; + } else if (v_chunk_type == 1213221953u) { + if (self->private_impl.f_variant > 32u) { + status = wuffs_base__make_status(wuffs_webp__error__bad_chunk_order); + goto exit; + } else if (((uint8_t)(self->private_impl.f_vp8x_header_flags & 16u)) == 0u) { + status = wuffs_base__make_status(wuffs_webp__error__bad_vp8x_chunk); + goto exit; + } + self->private_impl.f_variant = 64u; + } else if (v_chunk_type == 540561494u) { + if (((uint64_t)(v_chunk_length)) > 8388608u) { + status = wuffs_base__make_status(wuffs_webp__error__unsupported_vp8x_vp8_chunk_length); + goto exit; + } + wuffs_vp8__decoder__set_quirk(&self->private_data.f_vp8, 3u, ((((uint64_t)(v_chunk_length)) << 1u) | 1u)); + self->private_impl.f_variant = 129u; + break; + } else if (v_chunk_type == 1278758998u) { + if (((uint8_t)(self->private_impl.f_vp8x_header_flags & 16u)) != 0u) { + status = wuffs_base__make_status(wuffs_webp__error__bad_alph_vp8l_combination); + goto exit; + } + self->private_impl.f_variant = 130u; + break; + } else if (v_chunk_type == 542133592u) { + if (((uint8_t)(self->private_impl.f_vp8x_header_flags & 4u)) == 0u) { + status = wuffs_base__make_status(wuffs_webp__error__bad_vp8x_chunk); + goto exit; + } + } else if (v_chunk_type == 1179211845u) { + if (((uint8_t)(self->private_impl.f_vp8x_header_flags & 8u)) == 0u) { + status = wuffs_base__make_status(wuffs_webp__error__bad_vp8x_chunk); + goto exit; + } + } + if (v_chunk_length > 2147483647u) { + status = wuffs_base__make_status(wuffs_webp__error__unsupported_webp_file); + goto exit; + } else if ((v_chunk_length & 1u) != 0u) { + v_chunk_length += 1u; + } + self->private_data.s_do_decode_frame.scratch = v_chunk_length; + WUFFS_BASE__COROUTINE_SUSPENSION_POINT(6); + if (self->private_data.s_do_decode_frame.scratch > ((uint64_t)(io2_a_src - iop_a_src))) { + self->private_data.s_do_decode_frame.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_frame.scratch; + } + if (((uint8_t)(self->private_impl.f_variant & 1u)) != 0u) { + if (a_src) { + a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr)); + } + WUFFS_BASE__COROUTINE_SUSPENSION_POINT(7); + status = wuffs_vp8__decoder__decode_frame(&self->private_data.f_vp8, + a_dst, + a_src, + a_blend, + a_workbuf, + a_opts); + if (a_src) { + iop_a_src = a_src->data.ptr + a_src->meta.ri; + } + if (status.repr) { + goto suspend; + } + } else { + if (a_src) { + a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr)); + } + WUFFS_BASE__COROUTINE_SUSPENSION_POINT(8); + status = wuffs_webp__decoder__do_decode_frame_vp8l(self, + a_dst, + a_src, + a_blend, + a_workbuf, + a_opts); + if (a_src) { + iop_a_src = a_src->data.ptr + a_src->meta.ri; + } + if (status.repr) { + goto suspend; + } + } + + ok: + self->private_impl.p_do_decode_frame = 0; + goto exit; + } + + goto suspend; + suspend: + self->private_impl.p_do_decode_frame = wuffs_base__status__is_suspension(&status) ? coro_susp_point : 0; + self->private_data.s_do_decode_frame.v_chunk_type = v_chunk_type; + + 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 webp.decoder.do_decode_frame_vp8l + +WUFFS_BASE__GENERATED_C_CODE +static wuffs_base__status +wuffs_webp__decoder__do_decode_frame_vp8l( + wuffs_webp__decoder* self, + wuffs_base__pixel_buffer* a_dst, + wuffs_base__io_buffer* a_src, + wuffs_base__pixel_blend a_blend, + wuffs_base__slice_u8 a_workbuf, + wuffs_base__decode_frame_options* a_opts) { + wuffs_base__status status = wuffs_base__make_status(NULL); + uint8_t v_c8 = 0; uint32_t v_has_more = 0; uint32_t v_width = 0; @@ -89920,30 +90420,13 @@ io2_a_src = io0_a_src + a_src->meta.wi; } - uint32_t coro_susp_point = self->private_impl.p_do_decode_frame; + uint32_t coro_susp_point = self->private_impl.p_do_decode_frame_vp8l; if (coro_susp_point) { - v_width = self->private_data.s_do_decode_frame.v_width; + v_width = self->private_data.s_do_decode_frame_vp8l.v_width; } switch (coro_susp_point) { WUFFS_BASE__COROUTINE_SUSPENSION_POINT_0; - if (self->private_impl.f_call_sequence == 64u) { - } else if (self->private_impl.f_call_sequence < 64u) { - if (a_src) { - a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr)); - } - WUFFS_BASE__COROUTINE_SUSPENSION_POINT(1); - status = wuffs_webp__decoder__do_decode_frame_config(self, NULL, a_src); - if (a_src) { - iop_a_src = a_src->data.ptr + a_src->meta.ri; - } - if (status.repr) { - goto suspend; - } - } else { - status = wuffs_base__make_status(wuffs_base__note__end_of_data); - goto ok; - } self->private_impl.f_seen_transform[0u] = false; self->private_impl.f_seen_transform[1u] = false; self->private_impl.f_seen_transform[2u] = false; @@ -89952,7 +90435,7 @@ while (true) { if (self->private_impl.f_n_bits < 1u) { { - WUFFS_BASE__COROUTINE_SUSPENSION_POINT(2); + 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; @@ -89972,7 +90455,7 @@ if (a_src) { a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr)); } - WUFFS_BASE__COROUTINE_SUSPENSION_POINT(3); + WUFFS_BASE__COROUTINE_SUSPENSION_POINT(2); status = wuffs_webp__decoder__decode_transform(self, a_src, a_workbuf); if (a_src) { iop_a_src = a_src->data.ptr + a_src->meta.ri; @@ -89988,7 +90471,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(3); status = wuffs_webp__decoder__decode_color_cache_parameters(self, a_src); if (a_src) { iop_a_src = a_src->data.ptr + a_src->meta.ri; @@ -90000,7 +90483,7 @@ if (a_src) { a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr)); } - WUFFS_BASE__COROUTINE_SUSPENSION_POINT(5); + WUFFS_BASE__COROUTINE_SUSPENSION_POINT(4); status = wuffs_webp__decoder__decode_hg_table(self, a_src, v_width, a_workbuf); if (a_src) { iop_a_src = a_src->data.ptr + a_src->meta.ri; @@ -90012,7 +90495,7 @@ if (a_src) { a_src->meta.ri = ((size_t)(iop_a_src - a_src->data.ptr)); } - WUFFS_BASE__COROUTINE_SUSPENSION_POINT(6); + WUFFS_BASE__COROUTINE_SUSPENSION_POINT(5); status = wuffs_webp__decoder__decode_huffman_groups(self, a_src, self->private_impl.f_overall_n_huffman_groups); if (a_src) { iop_a_src = a_src->data.ptr + a_src->meta.ri; @@ -90051,7 +90534,7 @@ break; } status = v_status; - WUFFS_BASE__COROUTINE_SUSPENSION_POINT_MAYBE_SUSPEND(7); + WUFFS_BASE__COROUTINE_SUSPENSION_POINT_MAYBE_SUSPEND(6); } if (((uint64_t)(self->private_impl.f_workbuf_offset_for_transform[0u])) > ((uint64_t)(a_workbuf.len))) { status = wuffs_base__make_status(wuffs_base__error__bad_workbuf_length); @@ -90101,14 +90584,14 @@ self->private_impl.f_call_sequence = 96u; ok: - self->private_impl.p_do_decode_frame = 0; + self->private_impl.p_do_decode_frame_vp8l = 0; goto exit; } goto suspend; suspend: - self->private_impl.p_do_decode_frame = wuffs_base__status__is_suspension(&status) ? coro_susp_point : 0; - self->private_data.s_do_decode_frame.v_width = v_width; + self->private_impl.p_do_decode_frame_vp8l = wuffs_base__status__is_suspension(&status) ? coro_susp_point : 0; + self->private_data.s_do_decode_frame_vp8l.v_width = v_width; goto exit; exit: @@ -90775,7 +91258,7 @@ return wuffs_base__utility__empty_rect_ie_u32(); } - if (self->private_impl.f_is_vp8_lossy) { + if (self->private_impl.f_variant == 1u) { return wuffs_vp8__decoder__frame_dirty_rect(&self->private_data.f_vp8); } return wuffs_base__utility__make_rect_ie_u32( @@ -90816,7 +91299,7 @@ return 0; } - if (self->private_impl.f_is_vp8_lossy) { + if (self->private_impl.f_variant == 1u) { return wuffs_vp8__decoder__num_decoded_frame_configs(&self->private_data.f_vp8); } if (self->private_impl.f_call_sequence > 32u) { @@ -90839,7 +91322,7 @@ return 0; } - if (self->private_impl.f_is_vp8_lossy) { + if (self->private_impl.f_variant == 1u) { return wuffs_vp8__decoder__num_decoded_frames(&self->private_data.f_vp8); } if (self->private_impl.f_call_sequence > 64u) { @@ -90868,7 +91351,7 @@ wuffs_base__status v_status = wuffs_base__make_status(NULL); - if (self->private_impl.f_is_vp8_lossy) { + if (self->private_impl.f_variant == 1u) { v_status = wuffs_vp8__decoder__restart_frame(&self->private_data.f_vp8, a_index, a_io_position); return wuffs_private_impl__status__ensure_not_a_suspension(v_status); } @@ -90950,10 +91433,18 @@ return wuffs_base__utility__empty_range_ii_u64(); } - if (self->private_impl.f_is_vp8_lossy) { + wuffs_base__range_ii_u64 v_r = {0}; + wuffs_base__range_ii_u64 v_s = {0}; + + if (self->private_impl.f_variant == 1u) { return wuffs_vp8__decoder__workbuf_len(&self->private_data.f_vp8); } - return wuffs_base__utility__make_range_ii_u64(((uint64_t)(self->private_impl.f_workbuf_offset_for_transform[3u])), ((uint64_t)(self->private_impl.f_workbuf_offset_for_transform[3u]))); + v_r = wuffs_base__utility__make_range_ii_u64(((uint64_t)(self->private_impl.f_workbuf_offset_for_transform[3u])), ((uint64_t)(self->private_impl.f_workbuf_offset_for_transform[3u]))); + if (self->private_impl.f_variant != 2u) { + v_s = wuffs_vp8__decoder__workbuf_len(&self->private_data.f_vp8); + v_r = wuffs_base__utility__make_range_ii_u64(wuffs_base__u64__max(wuffs_private_impl__range_ii_u64__get_min_incl(&v_s), wuffs_private_impl__range_ii_u64__get_min_incl(&v_r)), wuffs_base__u64__max(wuffs_private_impl__range_ii_u64__get_max_incl(&v_s), wuffs_private_impl__range_ii_u64__get_max_incl(&v_r))); + } + return v_r; } #endif // !defined(WUFFS_CONFIG__MODULES) || defined(WUFFS_CONFIG__MODULE__WEBP)
diff --git a/std/vp8/decode_quirks.wuffs b/std/vp8/decode_quirks.wuffs new file mode 100644 index 0000000..ed13f79 --- /dev/null +++ b/std/vp8/decode_quirks.wuffs
@@ -0,0 +1,38 @@ +// Copyright 2026 The Wuffs Authors. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// -------- + +// Quirks are discussed in (/doc/note/quirks.md). +// +// The base38 encoding of "vp8." is 0x1B_5EFE. Left shifting by 10 gives +// 0x6D7B_F800. +pri const QUIRKS_BASE : base.u32 = 0x6D7B_F800 + +// -------- + +// Gives the image width and height out-of-band, in the u64 quirk value. The +// low 32 bits are the width and the high 32 bits are the height. Both +// dimensions should range within 0x0001 ..= 0x3FFF. +// +// A simple WebP file could contain one chunk, a VP8 chunk, that gives the +// image width and height. A complex WebP file could contain three chunks: +// VP8X, ALPH and VP8. The image dimensions are given in the VP8X chunk and are +// then repeated in the VP8 chunk. +// +// After a webp.decoder decode_image_config call, the caller might next want to +// query the workbuf length, to know how much memory to dynamically allocate, +// especially before attempting to decode the ALPH chunk. The outer +// webp.decoder workbuf_len method can delegate to the inner vp8.decoder +// workbuf_len method, but the latter needs to know the image dimensions. +// +// This quirk mechanism lets the outer webp decoder tell the inner vp8 decoder +// those dimensions, after parsing the VP8X chunk but before ALPH or VP8. +pub const QUIRK_WIDTH_AND_HEIGHT : base.u32 = 0x6D7B_F800 | 0x00
diff --git a/std/vp8/decode_vp8.wuffs b/std/vp8/decode_vp8.wuffs index 026010a..cdc0d1d 100644 --- a/std/vp8/decode_vp8.wuffs +++ b/std/vp8/decode_vp8.wuffs
@@ -9,7 +9,9 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT pub status "#bad QUIRK_SOURCE_LENGTH" +pub status "#bad QUIRK_WIDTH_AND_HEIGHT" pub status "#bad header" +pub status "#inconsistent width and height" pub status "#missing QUIRK_SOURCE_LENGTH" pub status "#truncated input" pub status "#unsupported QUIRK_SOURCE_LENGTH" @@ -37,6 +39,8 @@ // ((value_as_u63 << 1) | has_value_as_u1). quirk_source_length : base.u64, + quirk_width_and_height : base.u64, + // Just under 144 MiB, for 9 partitions each up to 0xFF_FFFF bytes. partitioned_data_length : base.u32[..= 0x08FF_FFF7], @@ -205,15 +209,46 @@ pub func decoder.get_quirk(key: base.u32) base.u64 { if args.key == base.QUIRK_SOURCE_LENGTH { return this.quirk_source_length + } else if args.key == QUIRK_WIDTH_AND_HEIGHT { + return this.quirk_width_and_height } return 0 } pub func decoder.set_quirk!(key: base.u32, value: base.u64) base.status { + var w : base.u32 + var h : base.u32 + if args.key == base.QUIRK_SOURCE_LENGTH { - this.quirk_source_length = args.value + if (args.value & 1) == 0 { + if args.value <> 0 { + return "#bad QUIRK_SOURCE_LENGTH" + } + this.quirk_source_length = 0 + this.partitioned_data_length = 0 + } else if (args.value >> 1) < 10 { + return "#bad QUIRK_SOURCE_LENGTH" + } else if (args.value >> 1) > (0x08FF_FFF7 + 10) { + return "#unsupported QUIRK_SOURCE_LENGTH" + } else { + this.quirk_source_length = args.value + this.partitioned_data_length = ((args.value >> 1) as base.u32) - 10 + } + return ok + + } else if args.key == QUIRK_WIDTH_AND_HEIGHT { + w = (args.value & 0xFFFF_FFFF) as base.u32 + h = (args.value & 0xFFFF_FFFF) as base.u32 + if (w < 1) or (0x3FFF < w) or (h < 1) or (0x3FFF < h) { + return "#bad QUIRK_WIDTH_AND_HEIGHT" + } + this.quirk_width_and_height = args.value + this.width = w + this.height = h + this.calculate_mbw_mbh!() return ok } + return base."#unsupported option" } @@ -236,14 +271,9 @@ return base."#bad call sequence" } - if (this.quirk_source_length & 1) == 0 { + if this.quirk_source_length == 0 { return "#missing QUIRK_SOURCE_LENGTH" - } else if (this.quirk_source_length >> 1) < 10 { - return "#bad QUIRK_SOURCE_LENGTH" - } else if (this.quirk_source_length >> 1) > (0x08FF_FFF7 + 10) { - return "#unsupported QUIRK_SOURCE_LENGTH" } - this.partitioned_data_length = ((this.quirk_source_length >> 1) as base.u32) - 10 // RFC 6386 Section 9. Frame Header, up to and including 9.1. Uncompressed // Data Chunk. decode_header_partition handles the rest of section 9. @@ -267,17 +297,13 @@ c32 = args.src.read_u32le?() this.width = 0x3FFF & (c32 >> 0) this.height = 0x3FFF & (c32 >> 16) - this.mbw = (this.width + 15) / 16 - this.mbh = (this.height + 15) / 16 - - this.workbuf_yuv_y_stride = this.mbw * 16 - this.workbuf_yuv_uv_stride = this.mbw * 8 - this.workbuf_yuv_y_end = - ((this.workbuf_yuv_y_stride as base.u64) * ((this.mbh * 16) as base.u64)) - this.workbuf_yuv_u_end = this.workbuf_yuv_y_end + - ((this.workbuf_yuv_uv_stride as base.u64) * ((this.mbh * 8) as base.u64)) - this.workbuf_yuv_v_end = this.workbuf_yuv_u_end + - ((this.workbuf_yuv_uv_stride as base.u64) * ((this.mbh * 8) as base.u64)) + if (this.width == 0) or (this.height == 0) { + return "#bad header" + } else if (this.quirk_width_and_height <> 0) and + (this.quirk_width_and_height <> ((this.width as base.u64) | ((this.height as base.u64) << 32))) { + return "#inconsistent width and height" + } + this.calculate_mbw_mbh!() this.frame_config_io_position = args.src.position() @@ -294,6 +320,20 @@ this.call_sequence = 0x20 } +pri func decoder.calculate_mbw_mbh!() { + this.mbw = (this.width + 15) / 16 + this.mbh = (this.height + 15) / 16 + + this.workbuf_yuv_y_stride = this.mbw * 16 + this.workbuf_yuv_uv_stride = this.mbw * 8 + this.workbuf_yuv_y_end = + ((this.workbuf_yuv_y_stride as base.u64) * ((this.mbh * 16) as base.u64)) + this.workbuf_yuv_u_end = this.workbuf_yuv_y_end + + ((this.workbuf_yuv_uv_stride as base.u64) * ((this.mbh * 8) as base.u64)) + this.workbuf_yuv_v_end = this.workbuf_yuv_u_end + + ((this.workbuf_yuv_uv_stride as base.u64) * ((this.mbh * 8) as base.u64)) +} + pub func decoder.decode_frame_config?(dst: nptr base.frame_config, src: base.io_reader) { var status : base.status
diff --git a/std/webp/decode_webp.wuffs b/std/webp/decode_webp.wuffs index 781b773..187bad3 100644 --- a/std/webp/decode_webp.wuffs +++ b/std/webp/decode_webp.wuffs
@@ -10,17 +10,21 @@ use "std/vp8" +pub status "#bad ALPH+VP8L combination" pub status "#bad Huffman code (over-subscribed)" pub status "#bad Huffman code (under-subscribed)" pub status "#bad Huffman code" +pub status "#bad VP8X chunk" pub status "#bad back-reference" +pub status "#bad chunk order" pub status "#bad color cache" pub status "#bad header" pub status "#bad transform" pub status "#short chunk" pub status "#truncated input" -pub status "#unsupported number of Huffman groups" +pub status "#unsupported VP8X/VP8 chunk length" pub status "#unsupported WebP file" +pub status "#unsupported number of Huffman groups" pri status "#internal error: inconsistent Huffman code" pri status "#internal error: inconsistent dst buffer" @@ -30,6 +34,63 @@ // worst case for VP8L is bigger than for VP8. pub const DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE : base.u64 = 0x4C00_0000 +// The default value of the largest supported VP8-inside-VP8X chunk length. +// This limitation is a consequence of combining Wuffs' "decode an image" API +// with WebP/VP8X/VP8's file format design. +// +// Wuffs' API only assumes sequential I/O access, not random I/O access, to the +// encoded image's bytes. It also is incapable (in the security capability +// sense) of dynamically allocating memory, so any O(width × height) pixel +// buffers, or any other buffers that aren't O(1) in terms of width and height, +// must be allocated by the caller, not the callee (Wuffs code). +// +// "How much dynamic memory does the caller need to allocate?" is answered by +// the decoder.workbuf_len method, which is valid to call as soon as +// decoder.decode_image_config returns (and decode_image_config, unlike +// decode_frame, does not take a variable-length slice-of-bytes workbuf +// argument). workbuf_len must return a worst case estimate, as it has only one +// shot at answering "how much dynamic memory?" +// +// On the other hand, an VP8 image file can have multiple "partitions", which +// means that a VP8 decoder must support random I/O access. Wuffs supports this +// by, in the worst case, copying the VP8's chunk payload (the encoded image +// data) into the workbuf. (Copying into a contiguous buffer is the same as +// what the libwebp C library's WebPIAppend "incremental API" does.) But doing +// so requires knowing how large the VP8 chunk is, so that the caller can +// allocate a sufficiently large workbuf. +// +// We cannot answer that question precisely until we parse the VP8 chunk, but +// in the WebP/VP8X file format, the VP8 chunk must occur after any ALPH chunk. +// For Wuffs to parse an ALPH chunk, it must have already "allocated" enough +// memory, more than O(1), by answering "how much dynamic memory?" +// +// As we cannot answer that question precisely (whilst still satisfying Wuffs' +// "decode an image" API, which works across numerous image file formats, not +// just WebP), we use a made-up upper bound, which is this number. +// +// Its value is arbitrary, but there is a trade-off. The larger it is, the more +// workbuf memory Wuffs will say it needs (and callers will allocate), even if +// it later discovers that it over-budgeted. The smaller it is, Wuffs will +// reject (as unsupported) more large-but-valid WebP/VP8X/VP8 files. +// +// This default value is 8 MiB. +pub const VP8X_MAX_INCL_VP8_CHUNK_LENGTH_DEFAULT : base.u64 = 8_388608 + +// TODO: in the future, regarding VP8X_MAX_INCL_VP8_CHUNK_LENGTH_DEFAULT, we +// could make this set by a quirk instead of hard-coded. +// +// Or, we could change the semantics of the wuffs_base__image_decoder API +// (which applies to multiple image formats, not just WebP). The API is that +// you can call workbuf_len as soon as decode_image_config returns. workbuf_len +// returns a range and any value within that range should be a valid length for +// the "dynamically allocated, not O(1) in terms of width and height" workbuf +// memory passed to decode_frame. +// +// We could relax this so that decode_frame could return a "$short workbuf" +// suspension, if the workbuf length is unknown at the time of the first +// decode_frame call, and the caller is expected to essentially realloc the +// workbuf, keeping any existing contents when growing it. + pub struct decoder? implements base.image_decoder( width : base.u32[..= 0x4000], height : base.u32[..= 0x4000], @@ -42,7 +103,35 @@ sub_chunk_has_padding : base.bool, - is_vp8_lossy : base.bool, + // 0x01 = VP8 lossy + // 0x02 = VP8L lossless + // 0x04 = VP8X and seen VP8X chunks + // 0x08 = VP8X and seen VP8X, ICCP chunks + // 0x10 = VP8X and seen VP8X, ICCP†, ANIM chunks + // 0x20 = VP8X and seen VP8X, ICCP†, ANIM†, ANMF chunks + // 0x40 = VP8X and seen VP8X, ICCP†, ANIM†, ANMF†, ALPH chunks + // 0x81 = VP8X and seen VP8X, ICCP†, ANIM†, ANMF†, ALPH†, VP8 chunks + // 0x82 = VP8X and seen VP8X, ICCP†, ANIM†, ANMF†, ALPH†, VP8L chunks + // + // Chunks marked with a † are optional. + variant : base.u8, + + // The "Rsv|I|L|E|X|A|R" bits from + // https://developers.google.com/speed/webp/docs/riff_container#extended_file_format + // + // We also re-purpose the otherwise-unused 0x01 bit to mean VP8X-ness, + // so that (vp8x_header_flags <> 0) is an easy check for VP8X versus + // not-VP8X. Similarly, ((vp8x_header_flags & 0x11) == 0x01) is an easy + // check for "VP8X without an ALPH chunk". + // + // See also libwebp's src/webp/mux_types.h: + // + // ANIMATION_FLAG = 0x00000002, + // XMP_FLAG = 0x00000004, + // EXIF_FLAG = 0x00000008, + // ALPHA_FLAG = 0x00000010, + // ICCP_FLAG = 0x00000020, + vp8x_header_flags : base.u8, frame_config_io_position : base.u64, @@ -199,7 +288,7 @@ this.frame_config_io_position = args.src.position() - if (not this.is_vp8_lossy) and (args.dst <> nullptr) { + if (this.variant <> 0x01) and (args.dst <> nullptr) { args.dst.set!( pixfmt: base.PIXEL_FORMAT__BGRA_NONPREMUL, pixsub: 0, @@ -224,11 +313,11 @@ c32 = args.src.read_u32le?() if c32 == 'VP8 'le { - this.is_vp8_lossy = true + this.variant = 0x01 } else if c32 == 'VP8L'le { - // No-op. + this.variant = 0x02 } else if c32 == 'VP8X'le { - return "#unsupported WebP file" + this.variant = 0x04 } else { return "#bad header" } @@ -239,20 +328,29 @@ } this.sub_chunk_has_padding = (this.sub_chunk_length & 1) <> 0 - if this.is_vp8_lossy { + if this.variant == 0x01 { this.vp8.set_quirk!( key: base.QUIRK_SOURCE_LENGTH, value: ((this.sub_chunk_length as base.u64) << 1) | 1) - + } else if this.variant == 0x02 { + // No-op. + } else if this.sub_chunk_length <> 10 { + return "#bad header" + } else { + this.vp8.set_quirk!( + key: base.QUIRK_SOURCE_LENGTH, + value: (VP8X_MAX_INCL_VP8_CHUNK_LENGTH_DEFAULT << 1) | 1) } while true { io_limit (io: args.src, limit: this.sub_chunk_length as base.u64) { r_mark = args.src.mark() - if this.is_vp8_lossy { + if this.variant == 0x01 { status =? this.vp8.decode_image_config?(dst: args.dst, src: args.src) - } else { + } else if this.variant == 0x02 { status =? this.do_decode_image_config_limited_vp8l?(src: args.src) + } else { + status =? this.do_decode_image_config_limited_vp8x?(src: args.src) } this.sub_chunk_length ~sat-= (args.src.count_since(mark: r_mark) & 0xFFFF_FFFF) as base.u32 @@ -298,11 +396,65 @@ this.workbuf_offset_for_transform[3] = (4 * this.width * this.height) + (3 * transform_size) } +pri func decoder.do_decode_image_config_limited_vp8x?(src: base.io_reader) { + var c32 : base.u32 + var transform_size : base.u32[..= 0x400_0000] + var status : base.status + + c32 = args.src.read_u32le?() + if (c32 & 0xFFFF_FFC1) <> 0 { + return "#unsupported WebP file" + } + this.vp8x_header_flags = ((c32 | 0x01) & 0xFF) as base.u8 + + c32 = args.src.read_u24le_as_u32?() + if c32 >= 0x3FFF { + return "#unsupported WebP file" + } + this.width = (c32 & 0x3FFF) + 1 + + c32 = args.src.read_u24le_as_u32?() + if c32 >= 0x3FFF { + return "#unsupported WebP file" + } + this.height = (c32 & 0x3FFF) + 1 + + // It's valid to call this.workbuf_len after this.decode_image_config + // returns, for the caller to know how much dynamically-sized memory + // (called workbuf memory) is needed to decode the image file. + // + // Unfortunately, having only seen the VP8X chunk, we don't know yet + // whether it's ultimately a VP8 or VP8L image, but we wouldn't know until + // after we've processed the ALPH chunk (if present), and processing the + // ALPH chunk requires workbuf memory (as the amount of memory depends on + // this.width and this.height). + // + // Wuffs' "decode an image" API assumes that the caller, not callee, is + // responsible for dynamically allocating memory. But also, it only assumes + // sequential access (not random access) to the io_reader. + // + // So, here, we have to cater for the worst case and set aside workbuf + // memory just like plain VP8L, even if it might end up being unused, for + // example: VP8X+VP8 instead of VP8X+VP8L or VP8X+ALPH+VP8. + transform_size = 4 * ((this.width + 3) >> 2) * ((this.height + 3) >> 2) + this.workbuf_offset_for_transform[0] = (4 * this.width * this.height) + (0 * transform_size) + this.workbuf_offset_for_transform[1] = (4 * this.width * this.height) + (1 * transform_size) + this.workbuf_offset_for_transform[2] = (4 * this.width * this.height) + (2 * transform_size) + this.workbuf_offset_for_transform[3] = (4 * this.width * this.height) + (3 * transform_size) + + status = this.vp8.set_quirk!( + key: vp8.QUIRK_WIDTH_AND_HEIGHT, + value: ((this.width as base.u64) | ((this.height as base.u64) << 32))) + if not status.is_ok() { + return "#unsupported WebP file" + } +} + pub func decoder.decode_frame_config?(dst: nptr base.frame_config, src: base.io_reader) { var status : base.status while true { - if this.is_vp8_lossy { + if this.variant == 0x01 { status =? this.vp8.decode_frame_config?(dst: args.dst, src: args.src) } else { status =? this.do_decode_frame_config?(dst: args.dst, src: args.src) @@ -330,7 +482,7 @@ return base."@end of data" } - if args.dst <> nullptr { + if (this.variant <> 0x01) and (args.dst <> nullptr) { args.dst.set!(bounds: this.util.make_rect_ie_u32( min_incl_x: 0, min_incl_y: 0, @@ -352,11 +504,7 @@ var status : base.status while true { - if this.is_vp8_lossy { - status =? this.vp8.decode_frame?(dst: args.dst, src: args.src, blend: args.blend, workbuf: args.workbuf, opts: args.opts) - } else { - status =? this.do_decode_frame?(dst: args.dst, src: args.src, blend: args.blend, workbuf: args.workbuf, opts: args.opts) - } + status =? this.do_decode_frame?(dst: args.dst, src: args.src, blend: args.blend, workbuf: args.workbuf, opts: args.opts) if (status == base."$short read") and args.src.is_closed() { return "#truncated input" } @@ -365,6 +513,97 @@ } 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 chunk_type : base.u32 + var chunk_length : base.u32 + + if this.call_sequence == 0x40 { + // No-op. + } else if this.call_sequence < 0x40 { + this.do_decode_frame_config?(dst: nullptr, src: args.src) + } else { + return base."@end of data" + } + + while (this.variant & 0x03) == 0 { + chunk_type = args.src.read_u32le?() + chunk_length = args.src.read_u32le?() + + if chunk_type == 'ICCP'le { + if this.variant > 0x04 { + return "#bad chunk order" + } else if (this.vp8x_header_flags & 0x20) == 0 { + return "#bad VP8X chunk" + } + this.variant = 0x08 + + } else if chunk_type == 'ANIM'le { + if this.variant > 0x08 { + return "#bad chunk order" + } else if (this.vp8x_header_flags & 0x02) == 0 { + return "#bad VP8X chunk" + } + this.variant = 0x10 + + } else if chunk_type == 'ANMF'le { + if this.variant > 0x10 { + return "#bad chunk order" + } else if (this.vp8x_header_flags & 0x02) == 0 { + return "#bad VP8X chunk" + } + this.variant = 0x20 + + } else if chunk_type == 'ALPH'le { + if this.variant > 0x20 { + return "#bad chunk order" + } else if (this.vp8x_header_flags & 0x10) == 0 { + return "#bad VP8X chunk" + } + this.variant = 0x40 + + } else if chunk_type == 'VP8 'le { + if (chunk_length as base.u64) > VP8X_MAX_INCL_VP8_CHUNK_LENGTH_DEFAULT { + return "#unsupported VP8X/VP8 chunk length" + } + this.vp8.set_quirk!( + key: base.QUIRK_SOURCE_LENGTH, + value: ((chunk_length as base.u64) << 1) | 1) + this.variant = 0x81 + break + + } else if chunk_type == 'VP8L'le { + if (this.vp8x_header_flags & 0x10) <> 0 { + return "#bad ALPH+VP8L combination" + } + this.variant = 0x82 + break + + } else if chunk_type == 'XMP 'le { + if (this.vp8x_header_flags & 0x04) == 0 { + return "#bad VP8X chunk" + } + + } else if chunk_type == 'EXIF'le { + if (this.vp8x_header_flags & 0x08) == 0 { + return "#bad VP8X chunk" + } + } + + if chunk_length > 0x7FFF_FFFF { + return "#unsupported WebP file" + } else if (chunk_length & 1) <> 0 { + chunk_length += 1 + } + args.src.skip_u32?(n: chunk_length) + } + + if (this.variant & 0x01) <> 0 { + this.vp8.decode_frame?(dst: args.dst, src: args.src, blend: args.blend, workbuf: args.workbuf, opts: args.opts) + } else { + this.do_decode_frame_vp8l?(dst: args.dst, src: args.src, blend: args.blend, workbuf: args.workbuf, opts: args.opts) + } +} + +pri func decoder.do_decode_frame_vp8l?(dst: ptr base.pixel_buffer, src: base.io_reader, blend: base.pixel_blend, workbuf: slice base.u8, opts: nptr base.decode_frame_options) { var c8 : base.u8 var has_more : base.u32[..= 1] var width : base.u32[..= 0x4000] @@ -377,14 +616,6 @@ var ti : base.u64 var tj : base.u64 - if this.call_sequence == 0x40 { - // No-op. - } else if this.call_sequence < 0x40 { - this.do_decode_frame_config?(dst: nullptr, src: args.src) - } else { - return base."@end of data" - } - this.seen_transform[0] = false this.seen_transform[1] = false this.seen_transform[2] = false @@ -844,7 +1075,7 @@ } pub func decoder.frame_dirty_rect() base.rect_ie_u32 { - if this.is_vp8_lossy { + if this.variant == 0x01 { return this.vp8.frame_dirty_rect() } return this.util.make_rect_ie_u32( @@ -859,7 +1090,7 @@ } pub func decoder.num_decoded_frame_configs() base.u64 { - if this.is_vp8_lossy { + if this.variant == 0x01 { return this.vp8.num_decoded_frame_configs() } if this.call_sequence > 0x20 { @@ -869,7 +1100,7 @@ } pub func decoder.num_decoded_frames() base.u64 { - if this.is_vp8_lossy { + if this.variant == 0x01 { return this.vp8.num_decoded_frames() } if this.call_sequence > 0x40 { @@ -881,7 +1112,7 @@ pub func decoder.restart_frame!(index: base.u64, io_position: base.u64) base.status { var status : base.status - if this.is_vp8_lossy { + if this.variant == 0x01 { status = this.vp8.restart_frame!(index: args.index, io_position: args.io_position) return status } @@ -904,12 +1135,25 @@ } pub func decoder.workbuf_len() base.range_ii_u64 { - if this.is_vp8_lossy { + var r : base.range_ii_u64 + var s : base.range_ii_u64 + + if this.variant == 0x01 { return this.vp8.workbuf_len() } - return this.util.make_range_ii_u64( + + r = this.util.make_range_ii_u64( min_incl: this.workbuf_offset_for_transform[3] as base.u64, max_incl: this.workbuf_offset_for_transform[3] as base.u64) + + if this.variant <> 0x02 { + s = this.vp8.workbuf_len() + r = this.util.make_range_ii_u64( + min_incl: s.get_min_incl().max(no_less_than: r.get_min_incl()), + max_incl: s.get_max_incl().max(no_less_than: r.get_max_incl())) + } + + return r } pri const DISTANCE_MAP : roarray[120] base.u8 = [
diff --git a/test/3pdata/nia-checksums-of-blinksuite.txt b/test/3pdata/nia-checksums-of-blinksuite.txt index eb72b89..a69a1c5 100644 --- a/test/3pdata/nia-checksums-of-blinksuite.txt +++ b/test/3pdata/nia-checksums-of-blinksuite.txt
@@ -30,6 +30,7 @@ OK. 267741e0 test/3pdata/blinksuite/blue-100.png OK. 89449c01 test/3pdata/blinksuite/blue-wheel-srgb-color-profile.jpg OK. 8fce70a9 test/3pdata/blinksuite/blue-wheel-srgb-color-profile.png +OK. 426a5623 test/3pdata/blinksuite/blue-wheel-srgb-color-profile.webp OK. 2d7bad9f test/3pdata/blinksuite/border-image-srgb-color-profile.png OK. 9ca73f21 test/3pdata/blinksuite/border-image.png OK. 277600f9 test/3pdata/blinksuite/boston.gif @@ -42,6 +43,8 @@ OK. b3389858 test/3pdata/blinksuite/color-checker-srgb-color-profile.png OK. 10721531 test/3pdata/blinksuite/count-down-color-test.gif OK. 1abc4dc6 test/3pdata/blinksuite/count-down-color-test.png +BAD d663b0e6 test/3pdata/blinksuite/count-down-color-test.webp +OK. 83ea9bce test/3pdata/blinksuite/crbug.364830.webp OK. 24ab1efd test/3pdata/blinksuite/crbug779261.gif OK. ae034abe test/3pdata/blinksuite/crbug807324.png OK. 4f50fdb0 test/3pdata/blinksuite/crbug827754.png @@ -66,6 +69,7 @@ OK. a2a940bc test/3pdata/blinksuite/cs-uma-ycck.jpg OK. b6000be6 test/3pdata/blinksuite/dice.png BAD 1023c20a test/3pdata/blinksuite/empty-frame.png +OK. 4a495d9f test/3pdata/blinksuite/flip.webp OK. eff36075 test/3pdata/blinksuite/flowchart.jpg OK. 20ff1df8 test/3pdata/blinksuite/full2loop.gif OK. dc814e29 test/3pdata/blinksuite/gif-loop-count.gif @@ -89,6 +93,11 @@ OK. 93f21c2a test/3pdata/blinksuite/icc-v2-gbr-420-width-not-whole-mcu.jpg OK. f37cea44 test/3pdata/blinksuite/icc-v2-gbr-422-whole-mcus.jpg OK. 1de5a906 test/3pdata/blinksuite/icc-v2-gbr.jpg +BAD 7c80a001 test/3pdata/blinksuite/invalid-animated-webp.webp +BAD 1e37d000 test/3pdata/blinksuite/invalid-animated-webp2.webp +BAD 97b2b706 test/3pdata/blinksuite/invalid-animated-webp3.webp +BAD 145962c5 test/3pdata/blinksuite/invalid-animated-webp4.webp +BAD 67a2ece0 test/3pdata/blinksuite/invalid_vp8_vp8x.webp OK. 0da5f37e test/3pdata/blinksuite/jpeg-height-exif-orientation.jpg OK. 4409dca6 test/3pdata/blinksuite/large-gif-checkerboard.gif OK. d67648b6 test/3pdata/blinksuite/large.webp @@ -171,9 +180,24 @@ OK. 6b85cdb9 test/3pdata/blinksuite/stripes-small.png OK. 16c2c0fe test/3pdata/blinksuite/test-load.jpg OK. 6fbda804 test/3pdata/blinksuite/test.webp +OK. b7690880 test/3pdata/blinksuite/test2.webp OK. 19d3a207 test/3pdata/blinksuite/test3.webp +OK. 17bda738 test/3pdata/blinksuite/truncated.webp BAD 1dfa35c6 test/3pdata/blinksuite/truncated2.webp +BAD b16eb1a2 test/3pdata/blinksuite/webp-animated-icc-xmp.webp +BAD 45030792 test/3pdata/blinksuite/webp-animated-large.webp +BAD 4519c83d test/3pdata/blinksuite/webp-animated-no-blend.webp +BAD 4519c83d test/3pdata/blinksuite/webp-animated-opaque.webp +BAD d26b2bd9 test/3pdata/blinksuite/webp-animated-semitransparent1.webp +BAD 73e0b22f test/3pdata/blinksuite/webp-animated-semitransparent2.webp +BAD d26b2bd9 test/3pdata/blinksuite/webp-animated-semitransparent3.webp +BAD 0dba213a test/3pdata/blinksuite/webp-animated-semitransparent4.webp +BAD ae7f1fcf test/3pdata/blinksuite/webp-animated.webp OK. eb191e45 test/3pdata/blinksuite/webp-color-no-profile-lossy.webp +OK. 60116c2c test/3pdata/blinksuite/webp-color-profile-crash.webp +BAD f234aa8a test/3pdata/blinksuite/webp-color-profile-lossless.webp +OK. b861d3c2 test/3pdata/blinksuite/webp-color-profile-lossy-alpha.webp +OK. 684e3635 test/3pdata/blinksuite/webp-color-profile-lossy.webp BAD ed67a9a5 test/3pdata/blinksuite/wrong-block-length.gif OK. 577c5c4c test/3pdata/blinksuite/ycbcr-420-fast-int-progressive.jpg OK. bf4cd8e2 test/3pdata/blinksuite/ycbcr-420-float-progressive.jpg
diff --git a/test/3pdata/nia-checksums-of-webpsuite.txt b/test/3pdata/nia-checksums-of-webpsuite.txt index b55a527..5257a89 100644 --- a/test/3pdata/nia-checksums-of-webpsuite.txt +++ b/test/3pdata/nia-checksums-of-webpsuite.txt
@@ -1,5 +1,19 @@ # Generated by script/print-nia-checksums.sh +OK. 651f0caa test/3pdata/webpsuite/alpha_color_cache.webp +OK. 935f9fe1 test/3pdata/webpsuite/alpha_filter_0_method_0.webp +OK. 935f9fe1 test/3pdata/webpsuite/alpha_filter_0_method_1.webp +OK. 28779e98 test/3pdata/webpsuite/alpha_filter_1.webp +OK. 935f9fe1 test/3pdata/webpsuite/alpha_filter_1_method_0.webp +OK. 935f9fe1 test/3pdata/webpsuite/alpha_filter_1_method_1.webp +OK. 28779e98 test/3pdata/webpsuite/alpha_filter_2.webp +OK. 935f9fe1 test/3pdata/webpsuite/alpha_filter_2_method_0.webp +OK. 935f9fe1 test/3pdata/webpsuite/alpha_filter_2_method_1.webp +OK. 28779e98 test/3pdata/webpsuite/alpha_filter_3.webp +OK. 935f9fe1 test/3pdata/webpsuite/alpha_filter_3_method_0.webp +OK. 935f9fe1 test/3pdata/webpsuite/alpha_filter_3_method_1.webp +OK. 28779e98 test/3pdata/webpsuite/alpha_no_compression.webp OK. 0ecfd841 test/3pdata/webpsuite/bad_palette_index.webp +OK. 4cb35952 test/3pdata/webpsuite/big_endian_bug_393.webp OK. 3dd065ab test/3pdata/webpsuite/bryce.webp OK. 7794c8b2 test/3pdata/webpsuite/bug3.webp OK. 6fbda804 test/3pdata/webpsuite/color_cache_bits_11.webp @@ -49,6 +63,10 @@ OK. 6706e719 test/3pdata/webpsuite/lossless_vec_2_7.webp OK. 6706e719 test/3pdata/webpsuite/lossless_vec_2_8.webp OK. 6706e719 test/3pdata/webpsuite/lossless_vec_2_9.webp +OK. f553cdb9 test/3pdata/webpsuite/lossy_alpha1.webp +OK. 2707a535 test/3pdata/webpsuite/lossy_alpha2.webp +OK. be4ce0da test/3pdata/webpsuite/lossy_alpha3.webp +OK. 5dce2606 test/3pdata/webpsuite/lossy_alpha4.webp OK. ac62b1e6 test/3pdata/webpsuite/lossy_extreme_probabilities.webp OK. 84ff1fbf test/3pdata/webpsuite/lossy_q0_f100.webp OK. 6957defa test/3pdata/webpsuite/near_lossless_75.webp
diff --git a/test/c/std/webp.c b/test/c/std/webp.c index be4f25f..6889c23 100644 --- a/test/c/std/webp.c +++ b/test/c/std/webp.c
@@ -115,6 +115,20 @@ } const char* // +test_wuffs_webp_decode_interface_vp8x_alpha_lossy() { + CHECK_FOCUS(__func__); + wuffs_webp__decoder* dec = &g_webp_decoder; + CHECK_STATUS("initialize", + wuffs_webp__decoder__initialize( + dec, sizeof *dec, WUFFS_VERSION, + WUFFS_INITIALIZE__LEAVE_INTERNAL_BUFFERS_UNINITIALIZED)); + return do_test__wuffs_base__image_decoder( + wuffs_webp__decoder__upcast_as__wuffs_base__image_decoder(dec), + "test/data/hippopotamus.masked-with-muybridge.lossy.webp", 0, SIZE_MAX, + 36, 28, 0xFFF5F5F5); // TODO: apply the alpha channel. +} + +const char* // test_wuffs_webp_decode_many_small_reads() { CHECK_FOCUS(__func__); wuffs_webp__decoder* dec = &g_webp_decoder; @@ -520,6 +534,7 @@ test_wuffs_webp_decode_interface_lossless, test_wuffs_webp_decode_interface_lossy, + test_wuffs_webp_decode_interface_vp8x_alpha_lossy, test_wuffs_webp_decode_many_small_reads, #ifdef WUFFS_MIMIC
diff --git a/test/nia-checksums-of-data.txt b/test/nia-checksums-of-data.txt index 6ddb13d..8da570e 100644 --- a/test/nia-checksums-of-data.txt +++ b/test/nia-checksums-of-data.txt
@@ -134,6 +134,7 @@ OK. 2535637e test/data/hippopotamus.masked-with-muybridge.etc2.bgra-binary.pkm OK. 720180ee test/data/hippopotamus.masked-with-muybridge.etc2.bgra-nonpremul.pkm OK. d3bbed27 test/data/hippopotamus.masked-with-muybridge.gif +OK. c41410be test/data/hippopotamus.masked-with-muybridge.lossy.webp OK. 7e6acf01 test/data/hippopotamus.masked-with-muybridge.png OK. dcbb225a test/data/hippopotamus.nie OK. 57d9a4dd test/data/hippopotamus.pgm