std/thumbhash: implement do_decode_frame
diff --git a/internal/cgen/base/fundamental-private.h b/internal/cgen/base/fundamental-private.h
index fb72c8a..204e56c 100644
--- a/internal/cgen/base/fundamental-private.h
+++ b/internal/cgen/base/fundamental-private.h
@@ -173,12 +173,24 @@
// ---------------- Numeric Types (Utility)
+#define wuffs_base__utility__i64_divide(a, b) \
+ ((uint64_t)(((int64_t)(a)) / ((int64_t)(b))))
+
#define wuffs_base__utility__sign_extend_convert_u8_u32(a) \
((uint32_t)(int32_t)(int8_t)(a))
+#define wuffs_base__utility__sign_extend_convert_u8_u64(a) \
+ ((uint64_t)(int64_t)(int8_t)(a))
+
#define wuffs_base__utility__sign_extend_convert_u16_u32(a) \
((uint32_t)(int32_t)(int16_t)(a))
+#define wuffs_base__utility__sign_extend_convert_u16_u64(a) \
+ ((uint64_t)(int64_t)(int16_t)(a))
+
+#define wuffs_base__utility__sign_extend_convert_u32_u64(a) \
+ ((uint64_t)(int64_t)(int32_t)(a))
+
#define wuffs_base__utility__sign_extend_rshift_u32(a, n) \
((uint32_t)(((int32_t)(a)) >> (n)))
diff --git a/lang/builtin/builtin.go b/lang/builtin/builtin.go
index 7ce3152..976fff2 100644
--- a/lang/builtin/builtin.go
+++ b/lang/builtin/builtin.go
@@ -389,6 +389,7 @@
"utility.empty_rect_ii_u32() rect_ii_u32",
"utility.empty_rect_ie_u32() rect_ie_u32",
"utility.empty_slice_u8() slice u8",
+ "utility.i64_divide(a: u64, b: u64[1 ..=]) u64",
"utility.make_bitvec256(e00: u64, e01: u64, e02: u64, e03: u64) bitvec256",
"utility.make_optional_u63(has_value: bool, value: u64[..= 0x7FFF_FFFF_FFFF_FFFF]) optional_u63",
"utility.make_pixel_format(repr: u32) pixel_format",
@@ -401,7 +402,10 @@
"utility.make_rect_ie_u32(" +
"min_incl_x: u32, min_incl_y: u32, max_excl_x: u32, max_excl_y: u32) rect_ie_u32",
"utility.sign_extend_convert_u8_u32(a: u8) u32",
+ "utility.sign_extend_convert_u8_u64(a: u8) u64",
"utility.sign_extend_convert_u16_u32(a: u16) u32",
+ "utility.sign_extend_convert_u16_u64(a: u16) u64",
+ "utility.sign_extend_convert_u32_u64(a: u32) u64",
"utility.sign_extend_rshift_u32(a: u32, n: u32[..= 31]) u32",
"utility.sign_extend_rshift_u64(a: u64, n: u32[..= 63]) u64",
diff --git a/release/c/wuffs-unsupported-snapshot.c b/release/c/wuffs-unsupported-snapshot.c
index f4a39bc..b31ec0e 100644
--- a/release/c/wuffs-unsupported-snapshot.c
+++ b/release/c/wuffs-unsupported-snapshot.c
@@ -14273,9 +14273,23 @@
wuffs_base__vtable null_vtable;
uint32_t f_pixfmt;
- uint32_t f_width;
- uint32_t f_height;
+ uint8_t f_w_dimension_code;
+ uint8_t f_h_dimension_code;
uint8_t f_call_sequence;
+ uint8_t f_frame_config_io_position;
+ uint64_t f_l_dc;
+ uint64_t f_p_dc;
+ uint64_t f_q_dc;
+ uint64_t f_a_dc;
+ uint8_t f_l_scale;
+ uint8_t f_p_scale;
+ uint8_t f_q_scale;
+ uint8_t f_a_scale;
+ uint8_t f_has_alpha;
+ uint8_t f_l_count;
+ uint8_t f_is_landscape;
+ uint32_t f_lx;
+ uint32_t f_ly;
wuffs_base__pixel_swizzler f_swizzler;
uint32_t p_decode_image_config;
@@ -14284,14 +14298,25 @@
uint32_t p_do_decode_frame_config;
uint32_t p_decode_frame;
uint32_t p_do_decode_frame;
+ uint32_t p_from_src_to_coeffs;
} private_impl;
struct {
+ uint32_t f_lac[32];
+ uint32_t f_pac[8];
+ uint32_t f_qac[8];
+ uint32_t f_aac[16];
uint8_t f_pixels[32][128];
struct {
uint64_t scratch;
} s_do_decode_image_config;
+ struct {
+ uint32_t v_cy;
+ uint32_t v_cx;
+ uint32_t v_i;
+ bool v_has_bits;
+ } s_from_src_to_coeffs;
} private_data;
#ifdef __cplusplus
@@ -17014,12 +17039,24 @@
// ---------------- Numeric Types (Utility)
+#define wuffs_base__utility__i64_divide(a, b) \
+ ((uint64_t)(((int64_t)(a)) / ((int64_t)(b))))
+
#define wuffs_base__utility__sign_extend_convert_u8_u32(a) \
((uint32_t)(int32_t)(int8_t)(a))
+#define wuffs_base__utility__sign_extend_convert_u8_u64(a) \
+ ((uint64_t)(int64_t)(int8_t)(a))
+
#define wuffs_base__utility__sign_extend_convert_u16_u32(a) \
((uint32_t)(int32_t)(int16_t)(a))
+#define wuffs_base__utility__sign_extend_convert_u16_u64(a) \
+ ((uint64_t)(int64_t)(int16_t)(a))
+
+#define wuffs_base__utility__sign_extend_convert_u32_u64(a) \
+ ((uint64_t)(int64_t)(int32_t)(a))
+
#define wuffs_base__utility__sign_extend_rshift_u32(a, n) \
((uint32_t)(((int32_t)(a)) >> (n)))
@@ -73276,6 +73313,379 @@
// ---------------- Private Consts
+static const uint8_t
+WUFFS_THUMBHASH__DIMENSIONS_FROM_DIMENSION_CODES[8] WUFFS_BASE__POTENTIALLY_UNUSED = {
+ 0u, 14u, 18u, 19u, 23u, 26u, 27u, 32u,
+};
+
+static const uint8_t
+WUFFS_THUMBHASH__CUMULATIVE_DIMENSIONS[8] WUFFS_BASE__POTENTIALLY_UNUSED = {
+ 0u, 0u, 14u, 32u, 51u, 74u, 100u, 127u,
+};
+
+static const uint8_t
+WUFFS_THUMBHASH__DIMENSION_CODES_FROM_HAS_ALPHA_AND_L_COUNT[2][8] WUFFS_BASE__POTENTIALLY_UNUSED = {
+ {
+ 23u, 23u, 23u, 23u, 39u, 71u, 103u, 119u,
+ }, {
+ 55u, 55u, 55u, 55u, 87u, 119u, 118u, 116u,
+ },
+};
+
+static const uint16_t
+WUFFS_THUMBHASH__FROM_4_BITS_TO_PLUS_MINUS_1_00[16] WUFFS_BASE__POTENTIALLY_UNUSED = {
+ 49152u, 51337u, 53521u, 55706u, 57890u, 60075u, 62259u, 64444u,
+ 1092u, 3277u, 5461u, 7646u, 9830u, 12015u, 14199u, 16384u,
+};
+
+static const uint16_t
+WUFFS_THUMBHASH__FROM_4_BITS_TO_PLUS_MINUS_1_25[16] WUFFS_BASE__POTENTIALLY_UNUSED = {
+ 45056u, 47787u, 50517u, 53248u, 55979u, 58709u, 61440u, 64171u,
+ 1365u, 4096u, 6827u, 9557u, 12288u, 15019u, 17749u, 20480u,
+};
+
+static const uint16_t
+WUFFS_THUMBHASH__COSINES[159][6] WUFFS_BASE__POTENTIALLY_UNUSED = {
+ {
+ 16281u, 15973u, 15465u, 14761u, 13873u, 12810u,
+ }, {
+ 15465u, 12810u, 8717u, 3646u, 63702u, 58427u,
+ }, {
+ 13873u, 7109u, 63702u, 55321u, 50071u, 49563u,
+ }, {
+ 11585u, 0u, 53951u, 49152u, 53951u, 0u,
+ }, {
+ 8717u, 58427u, 49255u, 55321u, 5411u, 15973u,
+ }, {
+ 5411u, 52726u, 51663u, 3646u, 16281u, 7109u,
+ }, {
+ 1834u, 49563u, 60125u, 14761u, 8717u, 52726u,
+ }, {
+ 63702u, 49563u, 5411u, 14761u, 56819u, 52726u,
+ },
+ {
+ 60125u, 52726u, 13873u, 3646u, 49255u, 7109u,
+ }, {
+ 56819u, 58427u, 16281u, 55321u, 60125u, 15973u,
+ }, {
+ 53951u, 0u, 11585u, 49152u, 11585u, 0u,
+ }, {
+ 51663u, 7109u, 1834u, 55321u, 15465u, 49563u,
+ }, {
+ 50071u, 12810u, 56819u, 3646u, 1834u, 58427u,
+ }, {
+ 49255u, 15973u, 50071u, 14761u, 51663u, 12810u,
+ }, {
+ 16322u, 16135u, 15826u, 15396u, 14849u, 14189u,
+ }, {
+ 15826u, 14189u, 11585u, 8192u, 4240u, 0u,
+ },
+ {
+ 14849u, 10531u, 4240u, 62691u, 56139u, 51347u,
+ }, {
+ 13421u, 5604u, 61296u, 52985u, 49214u, 51347u,
+ }, {
+ 11585u, 0u, 53951u, 49152u, 53951u, 0u,
+ }, {
+ 9397u, 59932u, 49710u, 52985u, 1428u, 14189u,
+ }, {
+ 6924u, 55005u, 49710u, 62691u, 13421u, 14189u,
+ }, {
+ 4240u, 51347u, 53951u, 8192u, 15826u, 0u,
+ }, {
+ 1428u, 49401u, 61296u, 15396u, 6924u, 51347u,
+ }, {
+ 64108u, 49401u, 4240u, 15396u, 58612u, 51347u,
+ },
+ {
+ 61296u, 51347u, 11585u, 8192u, 49710u, 0u,
+ }, {
+ 58612u, 55005u, 15826u, 62691u, 52115u, 14189u,
+ }, {
+ 56139u, 59932u, 15826u, 52985u, 64108u, 14189u,
+ }, {
+ 53951u, 0u, 11585u, 49152u, 11585u, 0u,
+ }, {
+ 52115u, 5604u, 4240u, 52985u, 16322u, 51347u,
+ }, {
+ 50687u, 10531u, 61296u, 62691u, 9397u, 51347u,
+ }, {
+ 49710u, 14189u, 53951u, 8192u, 61296u, 0u,
+ }, {
+ 49214u, 16135u, 49710u, 15396u, 50687u, 14189u,
+ },
+ {
+ 16328u, 16161u, 15883u, 15496u, 15004u, 14409u,
+ }, {
+ 15883u, 14409u, 12054u, 8961u, 5320u, 1353u,
+ }, {
+ 15004u, 11097u, 5320u, 64183u, 57738u, 52607u,
+ }, {
+ 13716u, 6581u, 62839u, 54439u, 49653u, 50040u,
+ }, {
+ 12054u, 1353u, 55473u, 49375u, 51820u, 61514u,
+ }, {
+ 10063u, 61514u, 50532u, 51127u, 62839u, 11097u,
+ }, {
+ 7798u, 56575u, 49208u, 58955u, 10063u, 16161u,
+ }, {
+ 5320u, 52607u, 51820u, 4022u, 16328u, 6581u,
+ },
+ {
+ 2697u, 50040u, 57738u, 12929u, 12054u, 56575u,
+ }, {
+ 0u, 49152u, 0u, 16384u, 0u, 49152u,
+ }, {
+ 62839u, 50040u, 7798u, 12929u, 53482u, 56575u,
+ }, {
+ 60216u, 52607u, 13716u, 4022u, 49208u, 6581u,
+ }, {
+ 57738u, 56575u, 16328u, 58955u, 55473u, 16161u,
+ }, {
+ 55473u, 61514u, 15004u, 51127u, 2697u, 11097u,
+ }, {
+ 53482u, 1353u, 10063u, 49375u, 13716u, 61514u,
+ }, {
+ 51820u, 6581u, 2697u, 54439u, 15883u, 50040u,
+ },
+ {
+ 50532u, 11097u, 60216u, 64183u, 7798u, 52607u,
+ }, {
+ 49653u, 14409u, 53482u, 8961u, 60216u, 1353u,
+ }, {
+ 49208u, 16161u, 49653u, 15496u, 50532u, 14409u,
+ }, {
+ 16346u, 16231u, 16041u, 15776u, 15438u, 15028u,
+ }, {
+ 16041u, 15028u, 13385u, 11183u, 8513u, 5487u,
+ }, {
+ 15438u, 12709u, 8513u, 3333u, 63305u, 57998u,
+ }, {
+ 14547u, 9448u, 2231u, 60049u, 53562u, 49760u,
+ }, {
+ 13385u, 5487u, 61116u, 52827u, 49190u, 51537u,
+ },
+ {
+ 11974u, 1118u, 55196u, 49305u, 52151u, 62203u,
+ }, {
+ 10340u, 62203u, 50989u, 50508u, 61116u, 9448u,
+ }, {
+ 8513u, 57998u, 49190u, 56088u, 6527u, 16231u,
+ }, {
+ 6527u, 54353u, 50098u, 64418u, 14547u, 12709u,
+ }, {
+ 4420u, 51537u, 53562u, 7538u, 16041u, 1118u,
+ }, {
+ 2231u, 49760u, 59009u, 13999u, 10340u, 54353u,
+ }, {
+ 0u, 49152u, 0u, 16384u, 0u, 49152u,
+ }, {
+ 63305u, 49760u, 6527u, 13999u, 55196u, 54353u,
+ },
+ {
+ 61116u, 51537u, 11974u, 7538u, 49495u, 1118u,
+ }, {
+ 59009u, 54353u, 15438u, 64418u, 50989u, 12709u,
+ }, {
+ 57023u, 57998u, 16346u, 56088u, 59009u, 16231u,
+ }, {
+ 55196u, 62203u, 14547u, 50508u, 4420u, 9448u,
+ }, {
+ 53562u, 1118u, 10340u, 49305u, 13385u, 62203u,
+ }, {
+ 52151u, 5487u, 4420u, 52827u, 16346u, 51537u,
+ }, {
+ 50989u, 9448u, 63305u, 60049u, 11974u, 49760u,
+ }, {
+ 50098u, 12709u, 57023u, 3333u, 2231u, 57998u,
+ },
+ {
+ 49495u, 15028u, 52151u, 11183u, 57023u, 5487u,
+ }, {
+ 49190u, 16231u, 49495u, 15776u, 50098u, 15028u,
+ }, {
+ 16354u, 16265u, 16116u, 15908u, 15642u, 15319u,
+ }, {
+ 16116u, 15319u, 14021u, 12264u, 10104u, 7614u,
+ }, {
+ 15642u, 13484u, 10104u, 5810u, 989u, 61615u,
+ }, {
+ 14941u, 10865u, 4874u, 63561u, 57060u, 52052u,
+ }, {
+ 14021u, 7614u, 64547u, 56229u, 50595u, 49271u,
+ }, {
+ 12897u, 3921u, 58812u, 51029u, 49420u, 54671u,
+ },
+ {
+ 11585u, 0u, 53951u, 49152u, 53951u, 0u,
+ }, {
+ 10104u, 61615u, 50595u, 51029u, 62583u, 10865u,
+ }, {
+ 8476u, 57922u, 49182u, 56229u, 6724u, 16265u,
+ }, {
+ 6724u, 54671u, 49894u, 63561u, 14021u, 13484u,
+ }, {
+ 4874u, 52052u, 52639u, 5810u, 16354u, 3921u,
+ }, {
+ 2953u, 50217u, 57060u, 12264u, 12897u, 57922u,
+ }, {
+ 989u, 49271u, 62583u, 15908u, 4874u, 50217u,
+ }, {
+ 64547u, 49271u, 2953u, 15908u, 60662u, 50217u,
+ },
+ {
+ 62583u, 50217u, 8476u, 12264u, 52639u, 57922u,
+ }, {
+ 60662u, 52052u, 12897u, 5810u, 49182u, 3921u,
+ }, {
+ 58812u, 54671u, 15642u, 63561u, 51515u, 13484u,
+ }, {
+ 57060u, 57922u, 16354u, 56229u, 58812u, 16265u,
+ }, {
+ 55432u, 61615u, 14941u, 51029u, 2953u, 10865u,
+ }, {
+ 53951u, 0u, 11585u, 49152u, 11585u, 0u,
+ }, {
+ 52639u, 3921u, 6724u, 51029u, 16116u, 54671u,
+ }, {
+ 51515u, 7614u, 989u, 56229u, 14941u, 49271u,
+ },
+ {
+ 50595u, 10865u, 60662u, 63561u, 8476u, 52052u,
+ }, {
+ 49894u, 13484u, 55432u, 5810u, 64547u, 61615u,
+ }, {
+ 49420u, 15319u, 51515u, 12264u, 55432u, 7614u,
+ }, {
+ 49182u, 16265u, 49420u, 15908u, 49894u, 15319u,
+ }, {
+ 16356u, 16273u, 16135u, 15942u, 15696u, 15396u,
+ }, {
+ 16135u, 15396u, 14189u, 12551u, 10531u, 8192u,
+ }, {
+ 15696u, 13689u, 10531u, 6489u, 1902u, 62691u,
+ }, {
+ 15044u, 11243u, 5604u, 64583u, 58183u, 52985u,
+ },
+ {
+ 14189u, 8192u, 0u, 57344u, 51347u, 49152u,
+ }, {
+ 13142u, 4699u, 59932u, 51847u, 49180u, 52985u,
+ }, {
+ 11917u, 953u, 55005u, 49263u, 52394u, 62691u,
+ }, {
+ 10531u, 62691u, 51347u, 50140u, 59932u, 8192u,
+ }, {
+ 9003u, 59047u, 49401u, 54293u, 3778u, 15396u,
+ }, {
+ 7353u, 55752u, 49401u, 60837u, 11917u, 15396u,
+ }, {
+ 5604u, 52985u, 51347u, 2845u, 16135u, 8192u,
+ }, {
+ 3778u, 50895u, 55005u, 9784u, 15044u, 62691u,
+ },
+ {
+ 1902u, 49594u, 59932u, 14641u, 9003u, 52985u,
+ }, {
+ 0u, 49152u, 0u, 16384u, 0u, 49152u,
+ }, {
+ 63634u, 49594u, 5604u, 14641u, 56533u, 52985u,
+ }, {
+ 61758u, 50895u, 10531u, 9784u, 50492u, 62691u,
+ }, {
+ 59932u, 52985u, 14189u, 2845u, 49401u, 8192u,
+ }, {
+ 58183u, 55752u, 16135u, 60837u, 53619u, 15396u,
+ }, {
+ 56533u, 59047u, 16135u, 54293u, 61758u, 15396u,
+ }, {
+ 55005u, 62691u, 14189u, 50140u, 5604u, 8192u,
+ },
+ {
+ 53619u, 953u, 10531u, 49263u, 13142u, 62691u,
+ }, {
+ 52394u, 4699u, 5604u, 51847u, 16356u, 52985u,
+ }, {
+ 51347u, 8192u, 0u, 57344u, 14189u, 49152u,
+ }, {
+ 50492u, 11243u, 59932u, 64583u, 7353u, 52985u,
+ }, {
+ 49840u, 13689u, 55005u, 6489u, 63634u, 62691u,
+ }, {
+ 49401u, 15396u, 51347u, 12551u, 55005u, 8192u,
+ }, {
+ 49180u, 16273u, 49401u, 15942u, 49840u, 15396u,
+ }, {
+ 16364u, 16305u, 16207u, 16069u, 15893u, 15679u,
+ },
+ {
+ 16207u, 15679u, 14811u, 13623u, 12140u, 10394u,
+ }, {
+ 15893u, 14449u, 12140u, 9102u, 5520u, 1606u,
+ }, {
+ 15426u, 12665u, 8423u, 3196u, 63132u, 57813u,
+ }, {
+ 14811u, 10394u, 3981u, 62340u, 55776u, 51087u,
+ }, {
+ 14053u, 7723u, 64732u, 56434u, 50725u, 49231u,
+ }, {
+ 13160u, 4756u, 60016u, 51913u, 49172u, 52871u,
+ }, {
+ 12140u, 1606u, 55776u, 49467u, 51483u, 60780u,
+ }, {
+ 11003u, 63930u, 52376u, 49467u, 57113u, 4756u,
+ },
+ {
+ 9760u, 60780u, 50110u, 51913u, 64732u, 12665u,
+ }, {
+ 8423u, 57813u, 49172u, 56434u, 7005u, 16305u,
+ }, {
+ 7005u, 55142u, 49643u, 62340u, 13160u, 14449u,
+ }, {
+ 5520u, 52871u, 51483u, 3196u, 16207u, 7723u,
+ }, {
+ 3981u, 51087u, 54533u, 9102u, 15426u, 63930u,
+ }, {
+ 2404u, 49857u, 58531u, 13623u, 11003u, 55142u,
+ }, {
+ 804u, 49231u, 63132u, 16069u, 3981u, 49857u,
+ }, {
+ 64732u, 49231u, 2404u, 16069u, 61555u, 49857u,
+ },
+ {
+ 63132u, 49857u, 7005u, 13623u, 54533u, 55142u,
+ }, {
+ 61555u, 51087u, 11003u, 9102u, 50110u, 63930u,
+ }, {
+ 60016u, 52871u, 14053u, 3196u, 49329u, 7723u,
+ }, {
+ 58531u, 55142u, 15893u, 62340u, 52376u, 14449u,
+ }, {
+ 57113u, 57813u, 16364u, 56434u, 58531u, 16305u,
+ }, {
+ 55776u, 60780u, 15426u, 51913u, 804u, 12665u,
+ }, {
+ 54533u, 63930u, 13160u, 49467u, 8423u, 4756u,
+ }, {
+ 53396u, 1606u, 9760u, 49467u, 14053u, 60780u,
+ },
+ {
+ 52376u, 4756u, 5520u, 51913u, 16364u, 52871u,
+ }, {
+ 51483u, 7723u, 804u, 56434u, 14811u, 49231u,
+ }, {
+ 50725u, 10394u, 61555u, 62340u, 9760u, 51087u,
+ }, {
+ 50110u, 12665u, 57113u, 3196u, 2404u, 57813u,
+ }, {
+ 49643u, 14449u, 53396u, 9102u, 60016u, 1606u,
+ }, {
+ 49329u, 15679u, 50725u, 13623u, 53396u, 10394u,
+ }, {
+ 49172u, 16305u, 49329u, 16069u, 49643u, 15679u,
+ },
+};
+
// ---------------- Private Initializer Prototypes
// ---------------- Private Function Prototypes
@@ -73306,11 +73716,16 @@
WUFFS_BASE__GENERATED_C_CODE
static wuffs_base__status
-wuffs_thumbhash__decoder__from_src_to_pixels(
+wuffs_thumbhash__decoder__from_src_to_coeffs(
wuffs_thumbhash__decoder* self,
wuffs_base__io_buffer* a_src);
WUFFS_BASE__GENERATED_C_CODE
+static wuffs_base__empty_struct
+wuffs_thumbhash__decoder__from_coeffs_to_pixels(
+ wuffs_thumbhash__decoder* self);
+
+WUFFS_BASE__GENERATED_C_CODE
static wuffs_base__status
wuffs_thumbhash__decoder__from_pixels_to_dst(
wuffs_thumbhash__decoder* self,
@@ -73541,6 +73956,7 @@
wuffs_base__status status = wuffs_base__make_status(NULL);
uint32_t v_c32 = 0;
+ uint8_t v_swap = 0;
const uint8_t* iop_a_src = NULL;
const uint8_t* io0_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
@@ -73594,18 +74010,115 @@
status = wuffs_base__make_status(wuffs_thumbhash__error__bad_header);
goto exit;
}
+ {
+ 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.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.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;
+ }
+ self->private_impl.f_l_dc = (((uint64_t)(((v_c32 >> 0u) & 63u))) * 136339441844224u);
+ self->private_impl.f_p_dc = ((uint64_t)((((uint64_t)(((v_c32 >> 6u) & 63u))) * 272678883688448u) - 8589384836186112u));
+ self->private_impl.f_q_dc = ((uint64_t)((((uint64_t)(((v_c32 >> 12u) & 63u))) * 272678883688448u) - 8589384836186112u));
+ self->private_impl.f_l_scale = ((uint8_t)(((v_c32 >> 18u) & 31u)));
+ self->private_impl.f_has_alpha = ((uint8_t)(((v_c32 >> 23u) & 1u)));
+ {
+ WUFFS_BASE__COROUTINE_SUSPENSION_POINT(5);
+ uint32_t t_2;
+ if (WUFFS_BASE__LIKELY(io2_a_src - iop_a_src >= 2)) {
+ t_2 = ((uint32_t)(wuffs_base__peek_u16le__no_bounds_check(iop_a_src)));
+ iop_a_src += 2;
+ } else {
+ self->private_data.s_do_decode_image_config.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.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 == 8) {
+ t_2 = ((uint32_t)(*scratch));
+ break;
+ }
+ num_bits_2 += 8u;
+ *scratch |= ((uint64_t)(num_bits_2)) << 56;
+ }
+ }
+ v_c32 = t_2;
+ }
+ self->private_impl.f_l_count = ((uint8_t)(((v_c32 >> 0u) & 7u)));
+ self->private_impl.f_p_scale = ((uint8_t)(((v_c32 >> 3u) & 63u)));
+ self->private_impl.f_q_scale = ((uint8_t)(((v_c32 >> 9u) & 63u)));
+ self->private_impl.f_is_landscape = ((uint8_t)(((v_c32 >> 15u) & 1u)));
+ self->private_impl.f_w_dimension_code = ((uint8_t)(((uint8_t)(WUFFS_THUMBHASH__DIMENSION_CODES_FROM_HAS_ALPHA_AND_L_COUNT[self->private_impl.f_has_alpha][self->private_impl.f_l_count] >> 4u)) & 7u));
+ self->private_impl.f_h_dimension_code = ((uint8_t)(((uint8_t)(WUFFS_THUMBHASH__DIMENSION_CODES_FROM_HAS_ALPHA_AND_L_COUNT[self->private_impl.f_has_alpha][self->private_impl.f_l_count] >> 0u)) & 7u));
+ if (self->private_impl.f_is_landscape != 0u) {
+ v_swap = self->private_impl.f_w_dimension_code;
+ self->private_impl.f_w_dimension_code = self->private_impl.f_h_dimension_code;
+ self->private_impl.f_h_dimension_code = v_swap;
+ }
+ if (self->private_impl.f_is_landscape != 0u) {
+ self->private_impl.f_lx = ((uint32_t)(((uint8_t)(7u - ((uint8_t)(2u * self->private_impl.f_has_alpha))))));
+ self->private_impl.f_ly = ((uint32_t)(wuffs_base__u8__max(self->private_impl.f_l_count, 3u)));
+ } else {
+ self->private_impl.f_lx = ((uint32_t)(wuffs_base__u8__max(self->private_impl.f_l_count, 3u)));
+ self->private_impl.f_ly = ((uint32_t)(((uint8_t)(7u - ((uint8_t)(2u * self->private_impl.f_has_alpha))))));
+ }
+ self->private_impl.f_frame_config_io_position = 8u;
+ if (self->private_impl.f_has_alpha != 0u) {
+ self->private_impl.f_frame_config_io_position = 9u;
+ {
+ WUFFS_BASE__COROUTINE_SUSPENSION_POINT(7);
+ if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
+ status = wuffs_base__make_status(wuffs_base__suspension__short_read);
+ goto suspend;
+ }
+ uint32_t t_3 = *iop_a_src++;
+ v_c32 = t_3;
+ }
+ self->private_impl.f_a_dc = (((uint64_t)(((v_c32 >> 0u) & 15u))) << 42u);
+ self->private_impl.f_a_scale = ((uint8_t)(((v_c32 >> 4u) & 15u)));
+ }
self->private_impl.f_pixfmt = 2415954056u;
- self->private_impl.f_width = 32u;
- self->private_impl.f_height = 32u;
+ if (self->private_impl.f_has_alpha != 0u) {
+ self->private_impl.f_pixfmt = 2164295816u;
+ }
if (a_dst != NULL) {
wuffs_base__image_config__set(
a_dst,
self->private_impl.f_pixfmt,
0u,
- self->private_impl.f_width,
- self->private_impl.f_height,
- 3u,
- (self->private_impl.f_pixfmt == 2415954056u));
+ ((uint32_t)(WUFFS_THUMBHASH__DIMENSIONS_FROM_DIMENSION_CODES[self->private_impl.f_w_dimension_code])),
+ ((uint32_t)(WUFFS_THUMBHASH__DIMENSIONS_FROM_DIMENSION_CODES[self->private_impl.f_h_dimension_code])),
+ ((uint64_t)(self->private_impl.f_frame_config_io_position)),
+ (self->private_impl.f_has_alpha == 0u));
}
self->private_impl.f_call_sequence = 32u;
@@ -73733,7 +74246,7 @@
goto suspend;
}
} else if (self->private_impl.f_call_sequence == 40u) {
- if (3u != wuffs_base__u64__sat_add((a_src ? a_src->meta.pos : 0), ((uint64_t)(iop_a_src - io0_a_src)))) {
+ if (((uint64_t)(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)))) {
status = wuffs_base__make_status(wuffs_base__error__bad_restart);
goto exit;
}
@@ -73751,13 +74264,13 @@
wuffs_base__utility__make_rect_ie_u32(
0u,
0u,
- self->private_impl.f_width,
- self->private_impl.f_height),
+ ((uint32_t)(WUFFS_THUMBHASH__DIMENSIONS_FROM_DIMENSION_CODES[self->private_impl.f_w_dimension_code])),
+ ((uint32_t)(WUFFS_THUMBHASH__DIMENSIONS_FROM_DIMENSION_CODES[self->private_impl.f_h_dimension_code]))),
((wuffs_base__flicks)(0u)),
0u,
- 3u,
+ ((uint64_t)(self->private_impl.f_frame_config_io_position)),
0u,
- (self->private_impl.f_pixfmt == 2415954056u),
+ (self->private_impl.f_has_alpha == 0u),
false,
0u);
}
@@ -73902,10 +74415,11 @@
goto ok;
}
WUFFS_BASE__COROUTINE_SUSPENSION_POINT(2);
- status = wuffs_thumbhash__decoder__from_src_to_pixels(self, a_src);
+ status = wuffs_thumbhash__decoder__from_src_to_coeffs(self, a_src);
if (status.repr) {
goto suspend;
}
+ wuffs_thumbhash__decoder__from_coeffs_to_pixels(self);
v_status = wuffs_thumbhash__decoder__from_pixels_to_dst(self, a_dst);
if ( ! wuffs_base__status__is_ok(&v_status)) {
status = v_status;
@@ -73933,36 +74447,333 @@
return status;
}
-// -------- func thumbhash.decoder.from_src_to_pixels
+// -------- func thumbhash.decoder.from_src_to_coeffs
WUFFS_BASE__GENERATED_C_CODE
static wuffs_base__status
-wuffs_thumbhash__decoder__from_src_to_pixels(
+wuffs_thumbhash__decoder__from_src_to_coeffs(
wuffs_thumbhash__decoder* self,
wuffs_base__io_buffer* a_src) {
wuffs_base__status status = wuffs_base__make_status(NULL);
+ uint8_t v_c8 = 0;
+ uint32_t v_cy = 0;
+ uint32_t v_cx = 0;
+ uint32_t v_i = 0;
+ bool v_has_bits = false;
+
+ 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_from_src_to_coeffs;
+ if (coro_susp_point) {
+ v_cy = self->private_data.s_from_src_to_coeffs.v_cy;
+ v_cx = self->private_data.s_from_src_to_coeffs.v_cx;
+ v_i = self->private_data.s_from_src_to_coeffs.v_i;
+ v_has_bits = self->private_data.s_from_src_to_coeffs.v_has_bits;
+ }
+ switch (coro_susp_point) {
+ WUFFS_BASE__COROUTINE_SUSPENSION_POINT_0;
+
+ v_i = 0u;
+ v_cy = 0u;
+ while (v_cy < self->private_impl.f_ly) {
+ v_cx = 0u;
+ if (v_cy == 0u) {
+ v_cx = 1u;
+ }
+ while (((uint32_t)(v_cx * self->private_impl.f_ly)) < ((uint32_t)(self->private_impl.f_lx * ((uint32_t)(self->private_impl.f_ly - v_cy))))) {
+ if (v_has_bits) {
+ v_has_bits = false;
+#if defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wconversion"
+#endif
+ v_c8 >>= 4u;
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
+ } else {
+ v_has_bits = true;
+ {
+ WUFFS_BASE__COROUTINE_SUSPENSION_POINT(1);
+ if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
+ status = wuffs_base__make_status(wuffs_base__suspension__short_read);
+ goto suspend;
+ }
+ uint8_t t_0 = *iop_a_src++;
+ v_c8 = t_0;
+ }
+ }
+ self->private_data.f_lac[(v_i & 31u)] = ((uint32_t)(((uint32_t)(((uint32_t)(self->private_impl.f_l_scale)) * 126u)) * wuffs_base__utility__sign_extend_convert_u16_u32(WUFFS_THUMBHASH__FROM_4_BITS_TO_PLUS_MINUS_1_00[((uint8_t)(v_c8 & 15u))])));
+ v_i += 1u;
+ v_cx += 1u;
+ }
+ v_cy += 1u;
+ }
+ v_i = 0u;
+ v_cx = 0u;
+ while (v_cx < 5u) {
+ if (v_has_bits) {
+ v_has_bits = false;
+#if defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wconversion"
+#endif
+ v_c8 >>= 4u;
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
+ } else {
+ v_has_bits = true;
+ {
+ WUFFS_BASE__COROUTINE_SUSPENSION_POINT(2);
+ if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
+ status = wuffs_base__make_status(wuffs_base__suspension__short_read);
+ goto suspend;
+ }
+ uint8_t t_1 = *iop_a_src++;
+ v_c8 = t_1;
+ }
+ }
+ self->private_data.f_pac[(v_i & 7u)] = ((uint32_t)(((uint32_t)(((uint32_t)(self->private_impl.f_p_scale)) * 62u)) * wuffs_base__utility__sign_extend_convert_u16_u32(WUFFS_THUMBHASH__FROM_4_BITS_TO_PLUS_MINUS_1_25[((uint8_t)(v_c8 & 15u))])));
+ v_i += 1u;
+ v_cx += 1u;
+ }
+ v_i = 0u;
+ v_cx = 0u;
+ while (v_cx < 5u) {
+ if (v_has_bits) {
+ v_has_bits = false;
+#if defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wconversion"
+#endif
+ v_c8 >>= 4u;
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
+ } else {
+ v_has_bits = true;
+ {
+ WUFFS_BASE__COROUTINE_SUSPENSION_POINT(3);
+ if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
+ status = wuffs_base__make_status(wuffs_base__suspension__short_read);
+ goto suspend;
+ }
+ uint8_t t_2 = *iop_a_src++;
+ v_c8 = t_2;
+ }
+ }
+ self->private_data.f_qac[(v_i & 7u)] = ((uint32_t)(((uint32_t)(((uint32_t)(self->private_impl.f_q_scale)) * 62u)) * wuffs_base__utility__sign_extend_convert_u16_u32(WUFFS_THUMBHASH__FROM_4_BITS_TO_PLUS_MINUS_1_25[((uint8_t)(v_c8 & 15u))])));
+ v_i += 1u;
+ v_cx += 1u;
+ }
+ if (self->private_impl.f_has_alpha == 0u) {
+ status = wuffs_base__make_status(NULL);
+ goto ok;
+ }
+ v_i = 0u;
+ v_cx = 0u;
+ while (v_cx < 14u) {
+ if (v_has_bits) {
+ v_has_bits = false;
+#if defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wconversion"
+#endif
+ v_c8 >>= 4u;
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
+ } else {
+ v_has_bits = true;
+ {
+ WUFFS_BASE__COROUTINE_SUSPENSION_POINT(4);
+ if (WUFFS_BASE__UNLIKELY(iop_a_src == io2_a_src)) {
+ status = wuffs_base__make_status(wuffs_base__suspension__short_read);
+ goto suspend;
+ }
+ uint8_t t_3 = *iop_a_src++;
+ v_c8 = t_3;
+ }
+ }
+ self->private_data.f_aac[(v_i & 15u)] = ((uint32_t)(((uint32_t)(((uint32_t)(self->private_impl.f_a_scale)) * 2u)) * wuffs_base__utility__sign_extend_convert_u16_u32(WUFFS_THUMBHASH__FROM_4_BITS_TO_PLUS_MINUS_1_00[((uint8_t)(v_c8 & 15u))])));
+ v_i += 1u;
+ v_cx += 1u;
+ }
+
+ ok:
+ self->private_impl.p_from_src_to_coeffs = 0;
+ goto exit;
+ }
+
+ goto suspend;
+ suspend:
+ self->private_impl.p_from_src_to_coeffs = wuffs_base__status__is_suspension(&status) ? coro_susp_point : 0;
+ self->private_data.s_from_src_to_coeffs.v_cy = v_cy;
+ self->private_data.s_from_src_to_coeffs.v_cx = v_cx;
+ self->private_data.s_from_src_to_coeffs.v_i = v_i;
+ self->private_data.s_from_src_to_coeffs.v_has_bits = v_has_bits;
+
+ 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 thumbhash.decoder.from_coeffs_to_pixels
+
+WUFFS_BASE__GENERATED_C_CODE
+static wuffs_base__empty_struct
+wuffs_thumbhash__decoder__from_coeffs_to_pixels(
+ wuffs_thumbhash__decoder* self) {
+ uint32_t v_h = 0;
+ uint32_t v_w = 0;
+ uint32_t v_fy[8] = {0};
+ uint32_t v_fx[8] = {0};
+ uint32_t v_cosines_base_y = 0;
+ uint32_t v_cosines_base_x = 0;
uint32_t v_y = 0;
uint32_t v_x = 0;
+ uint32_t v_f = 0;
+ uint64_t v_l = 0;
+ uint64_t v_p = 0;
+ uint64_t v_q = 0;
+ uint64_t v_b = 0;
+ uint64_t v_g = 0;
+ uint64_t v_r = 0;
+ uint64_t v_a = 0;
+ uint32_t v_i = 0;
+ uint32_t v_cy = 0;
+ uint32_t v_cx = 0;
+ v_h = ((uint32_t)(WUFFS_THUMBHASH__DIMENSIONS_FROM_DIMENSION_CODES[self->private_impl.f_h_dimension_code]));
+ v_w = ((uint32_t)(WUFFS_THUMBHASH__DIMENSIONS_FROM_DIMENSION_CODES[self->private_impl.f_w_dimension_code]));
+ v_fy[0u] = 16384u;
+ v_fx[0u] = 16384u;
+ v_a = 255u;
v_y = 0u;
- while (v_y < 32u) {
+ while (v_y < v_h) {
+ v_cosines_base_y = ((uint32_t)(WUFFS_THUMBHASH__CUMULATIVE_DIMENSIONS[self->private_impl.f_h_dimension_code]));
+ v_fy[1u] = wuffs_base__utility__sign_extend_convert_u16_u32(WUFFS_THUMBHASH__COSINES[(v_cosines_base_y + v_y)][0u]);
+ v_fy[2u] = wuffs_base__utility__sign_extend_convert_u16_u32(WUFFS_THUMBHASH__COSINES[(v_cosines_base_y + v_y)][1u]);
+ v_fy[3u] = wuffs_base__utility__sign_extend_convert_u16_u32(WUFFS_THUMBHASH__COSINES[(v_cosines_base_y + v_y)][2u]);
+ v_fy[4u] = wuffs_base__utility__sign_extend_convert_u16_u32(WUFFS_THUMBHASH__COSINES[(v_cosines_base_y + v_y)][3u]);
+ v_fy[5u] = wuffs_base__utility__sign_extend_convert_u16_u32(WUFFS_THUMBHASH__COSINES[(v_cosines_base_y + v_y)][4u]);
+ v_fy[6u] = wuffs_base__utility__sign_extend_convert_u16_u32(WUFFS_THUMBHASH__COSINES[(v_cosines_base_y + v_y)][5u]);
v_x = 0u;
- while (v_x < 32u) {
- self->private_data.f_pixels[v_y][((4u * v_x) + 0u)] = ((uint8_t)((v_x * 8u)));
- self->private_data.f_pixels[v_y][((4u * v_x) + 1u)] = ((uint8_t)((v_y * 8u)));
- self->private_data.f_pixels[v_y][((4u * v_x) + 2u)] = 0u;
- self->private_data.f_pixels[v_y][((4u * v_x) + 3u)] = 255u;
+ while (v_x < v_w) {
+ v_cosines_base_x = ((uint32_t)(WUFFS_THUMBHASH__CUMULATIVE_DIMENSIONS[self->private_impl.f_w_dimension_code]));
+ v_fx[1u] = wuffs_base__utility__sign_extend_convert_u16_u32(WUFFS_THUMBHASH__COSINES[(v_cosines_base_x + v_x)][0u]);
+ v_fx[2u] = wuffs_base__utility__sign_extend_convert_u16_u32(WUFFS_THUMBHASH__COSINES[(v_cosines_base_x + v_x)][1u]);
+ v_fx[3u] = wuffs_base__utility__sign_extend_convert_u16_u32(WUFFS_THUMBHASH__COSINES[(v_cosines_base_x + v_x)][2u]);
+ v_fx[4u] = wuffs_base__utility__sign_extend_convert_u16_u32(WUFFS_THUMBHASH__COSINES[(v_cosines_base_x + v_x)][3u]);
+ v_fx[5u] = wuffs_base__utility__sign_extend_convert_u16_u32(WUFFS_THUMBHASH__COSINES[(v_cosines_base_x + v_x)][4u]);
+ v_fx[6u] = wuffs_base__utility__sign_extend_convert_u16_u32(WUFFS_THUMBHASH__COSINES[(v_cosines_base_x + v_x)][5u]);
+ v_l = self->private_impl.f_l_dc;
+ v_i = 0u;
+ v_cy = 0u;
+ while (v_cy < self->private_impl.f_ly) {
+ v_cx = 0u;
+ if (v_cy == 0u) {
+ v_cx = 1u;
+ }
+ while (((uint32_t)(v_cx * self->private_impl.f_ly)) < ((uint32_t)(self->private_impl.f_lx * ((uint32_t)(self->private_impl.f_ly - v_cy))))) {
+ v_f = ((uint32_t)(v_fx[(v_cx & 7u)] * v_fy[(v_cy & 7u)]));
+ v_l += ((uint64_t)(wuffs_base__utility__sign_extend_convert_u32_u64(v_f) * wuffs_base__utility__sign_extend_convert_u32_u64(self->private_data.f_lac[(v_i & 31u)])));
+ v_i += 1u;
+ v_cx += 1u;
+ }
+ v_cy += 1u;
+ }
+ v_p = self->private_impl.f_p_dc;
+ v_q = self->private_impl.f_q_dc;
+ v_i = 0u;
+ v_cy = 0u;
+ while (v_cy < 3u) {
+ v_cx = 0u;
+ if (v_cy == 0u) {
+ v_cx = 1u;
+ }
+ while (v_cx < (3u - v_cy)) {
+ v_f = ((uint32_t)(v_fx[v_cx] * v_fy[v_cy]));
+ v_p += ((uint64_t)(wuffs_base__utility__sign_extend_convert_u32_u64(v_f) * wuffs_base__utility__sign_extend_convert_u32_u64(self->private_data.f_pac[(v_i & 7u)])));
+ v_q += ((uint64_t)(wuffs_base__utility__sign_extend_convert_u32_u64(v_f) * wuffs_base__utility__sign_extend_convert_u32_u64(self->private_data.f_qac[(v_i & 7u)])));
+ v_i += 1u;
+ v_cx += 1u;
+ }
+ v_cy += 1u;
+ }
+ v_b = ((uint64_t)(v_l - wuffs_base__utility__i64_divide(((uint64_t)(2u * v_p)), 3u)));
+ v_r = wuffs_base__utility__sign_extend_rshift_u64(((uint64_t)(((uint64_t)(((uint64_t)(3u * v_l)) + v_q)) - v_b)), 1u);
+ v_g = ((uint64_t)(v_r - v_q));
+ if ((v_b >> 63u) != 0u) {
+ v_b = 0u;
+ } else if (v_b >= 8589384836185950u) {
+ v_b = 255u;
+ } else {
+ v_b /= 33683862102690u;
+ }
+ if ((v_g >> 63u) != 0u) {
+ v_g = 0u;
+ } else if (v_g >= 8589384836185950u) {
+ v_g = 255u;
+ } else {
+ v_g /= 33683862102690u;
+ }
+ if ((v_r >> 63u) != 0u) {
+ v_r = 0u;
+ } else if (v_r >= 8589384836185950u) {
+ v_r = 255u;
+ } else {
+ v_r /= 33683862102690u;
+ }
+ if (self->private_impl.f_has_alpha != 0u) {
+ v_a = self->private_impl.f_a_dc;
+ v_i = 0u;
+ v_cy = 0u;
+ while (v_cy < 5u) {
+ v_cx = 0u;
+ if (v_cy == 0u) {
+ v_cx = 1u;
+ }
+ while (v_cx < (5u - v_cy)) {
+ v_f = ((uint32_t)(v_fx[v_cx] * v_fy[v_cy]));
+ v_a += ((uint64_t)(wuffs_base__utility__sign_extend_convert_u32_u64(v_f) * wuffs_base__utility__sign_extend_convert_u32_u64(self->private_data.f_aac[(v_i & 15u)])));
+ v_i += 1u;
+ v_cx += 1u;
+ }
+ v_cy += 1u;
+ }
+ if ((v_a >> 63u) != 0u) {
+ v_a = 0u;
+ } else if (v_a >= 65970697666500u) {
+ v_a = 255u;
+ } else {
+ v_a /= 258708618300u;
+ }
+ }
+ self->private_data.f_pixels[v_y][((4u * v_x) + 0u)] = ((uint8_t)(v_b));
+ self->private_data.f_pixels[v_y][((4u * v_x) + 1u)] = ((uint8_t)(v_g));
+ self->private_data.f_pixels[v_y][((4u * v_x) + 2u)] = ((uint8_t)(v_r));
+ self->private_data.f_pixels[v_y][((4u * v_x) + 3u)] = ((uint8_t)(v_a));
v_x += 1u;
}
v_y += 1u;
}
-
- goto ok;
- ok:
- goto exit;
- exit:
- return status;
+ return wuffs_base__make_empty_struct();
}
// -------- func thumbhash.decoder.from_pixels_to_dst
@@ -73972,6 +74783,8 @@
wuffs_thumbhash__decoder__from_pixels_to_dst(
wuffs_thumbhash__decoder* self,
wuffs_base__pixel_buffer* a_dst) {
+ uint32_t v_h = 0;
+ uint32_t v_w = 0;
wuffs_base__pixel_format v_dst_pixfmt = {0};
uint32_t v_dst_bits_per_pixel = 0;
uint32_t v_dst_bytes_per_pixel = 0;
@@ -73981,16 +74794,18 @@
wuffs_base__slice_u8 v_dst = {0};
wuffs_base__slice_u8 v_src = {0};
+ v_h = ((uint32_t)(WUFFS_THUMBHASH__DIMENSIONS_FROM_DIMENSION_CODES[self->private_impl.f_h_dimension_code]));
+ v_w = ((uint32_t)(WUFFS_THUMBHASH__DIMENSIONS_FROM_DIMENSION_CODES[self->private_impl.f_w_dimension_code]));
v_dst_pixfmt = wuffs_base__pixel_buffer__pixel_format(a_dst);
v_dst_bits_per_pixel = wuffs_base__pixel_format__bits_per_pixel(&v_dst_pixfmt);
if ((v_dst_bits_per_pixel & 7u) != 0u) {
return wuffs_base__make_status(wuffs_base__error__unsupported_option);
}
v_dst_bytes_per_pixel = (v_dst_bits_per_pixel / 8u);
- v_dst_bytes_per_row = ((uint64_t)((self->private_impl.f_width * v_dst_bytes_per_pixel)));
+ v_dst_bytes_per_row = ((uint64_t)((v_w * v_dst_bytes_per_pixel)));
v_tab = wuffs_base__pixel_buffer__plane(a_dst, 0u);
- while (v_y < self->private_impl.f_height) {
- v_src = wuffs_base__make_slice_u8(self->private_data.f_pixels[v_y], (self->private_impl.f_width * 4u));
+ while (v_y < v_h) {
+ v_src = wuffs_base__make_slice_u8(self->private_data.f_pixels[v_y], (v_w * 4u));
v_dst = wuffs_private_impl__table_u8__row_u32(v_tab, v_y);
if (v_dst_bytes_per_row < ((uint64_t)(v_dst.len))) {
v_dst = wuffs_base__slice_u8__subslice_j(v_dst, v_dst_bytes_per_row);
@@ -74018,8 +74833,8 @@
return wuffs_base__utility__make_rect_ie_u32(
0u,
0u,
- self->private_impl.f_width,
- self->private_impl.f_height);
+ ((uint32_t)(WUFFS_THUMBHASH__DIMENSIONS_FROM_DIMENSION_CODES[self->private_impl.f_w_dimension_code])),
+ ((uint32_t)(WUFFS_THUMBHASH__DIMENSIONS_FROM_DIMENSION_CODES[self->private_impl.f_h_dimension_code])));
}
// -------- func thumbhash.decoder.num_animation_loops
@@ -74100,7 +74915,7 @@
if (self->private_impl.f_call_sequence < 32u) {
return wuffs_base__make_status(wuffs_base__error__bad_call_sequence);
}
- if ((a_index != 0u) || (a_io_position != 3u)) {
+ if ((a_index != 0u) || (a_io_position != ((uint64_t)(self->private_impl.f_frame_config_io_position)))) {
return wuffs_base__make_status(wuffs_base__error__bad_argument);
}
self->private_impl.f_call_sequence = 40u;
diff --git a/script/print-thumbhash-tables.go b/script/print-thumbhash-tables.go
new file mode 100644
index 0000000..1976334
--- /dev/null
+++ b/script/print-thumbhash-tables.go
@@ -0,0 +1,144 @@
+// Copyright 2024 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
+
+//go:build ignore
+// +build ignore
+
+package main
+
+// print-thumbhash-tables.go prints the std/thumbhash tables.
+//
+// Usage: go run print-thumbhash-tables.go
+
+import (
+ "fmt"
+ "math"
+ "os"
+)
+
+func main() {
+ if err := main1(); err != nil {
+ os.Stderr.WriteString(err.Error() + "\n")
+ os.Exit(1)
+ }
+}
+
+func main1() error {
+ printLxLy()
+ fmt.Println()
+ printFrom4Bits(1.00)
+ fmt.Println()
+ printFrom4Bits(1.25)
+ fmt.Println()
+ printCosines()
+ return nil
+}
+
+func printLxLy() {
+ for bits := 0; bits < 16; bits++ {
+ lCount := bits & 7
+ if lCount < 3 {
+ continue
+ } else if lCount == 3 {
+ fmt.Println()
+ }
+ hasAlpha := (bits >> 3) & 1
+ isLandscape := (bits >> 4) & 1
+
+ lx := lCount
+ ly := 7
+ if hasAlpha != 0 {
+ ly = 5
+ }
+ if isLandscape != 0 {
+ lx, ly = ly, lx
+ }
+ ratio := float64(lx) / float64(ly)
+
+ w, h := 32, 32
+ if ratio > 1 {
+ h = int(math.Round(32 / ratio))
+ } else {
+ w = int(math.Round(32 * ratio))
+ }
+
+ lenlac := 0
+ for cy := 0; cy < ly; cy++ {
+ for cx := ifElse(cy, 0, 1); (cx * ly) < (lx * (ly - cy)); cx++ {
+ lenlac++
+ }
+ }
+
+ lenpac := 0
+ for cy := 0; cy < 3; cy++ {
+ for cx := ifElse(cy, 0, 1); (cx * 3) < (3 * (3 - cy)); cx++ {
+ lenpac++
+ }
+ }
+
+ lenaac := 0
+ if hasAlpha != 0 {
+ for cy := 0; cy < 5; cy++ {
+ for cx := ifElse(cy, 0, 1); (cx * 5) < (5 * (5 - cy)); cx++ {
+ lenaac++
+ }
+ }
+ }
+
+ fileSize := 5 + hasAlpha + (lenlac+lenpac+lenpac+lenaac+1)/2
+
+ fmt.Printf("l_count: %d lx / ly = %d / %d w = %2d h = %2d #lac = %2d #pac = #qac = %d #aac = %2d file_size = 3 + %2d\n",
+ lCount, lx, ly, w, h, lenlac, lenpac, lenaac, fileSize)
+ }
+}
+
+func printFrom4Bits(scale float64) {
+ for i := 0; i < 16; i++ {
+ x := (float64(i)/7.5 - 1) * scale
+ y := (x * (1 << 14))
+ fmt.Printf("0x%04X, // %s%0.3f\n", 0xFFFF&int(math.Round(y)), plusSign(y), x)
+ }
+}
+
+func printCosines() {
+ widths := []int{
+ 0, 14, 18, 19, 23, 26, 27, 32,
+ }
+
+ for _, w := range widths {
+ fmt.Printf("\n// w = %2d\n", w)
+ for x := 0; x < w; x++ {
+ fmt.Printf("[")
+ for cx := 1; cx < 7; cx++ {
+ u := math.Cos(math.Pi * float64(cx) * (float64(x) + 0.5) / float64(w))
+ v := u * (1 << 14)
+ fmt.Printf("0x%04X", 0xFFFF&int(math.Round(v)))
+ if cx < 6 {
+ fmt.Printf(",")
+ }
+ }
+ fmt.Printf("], // x = %2d\n", x)
+ }
+ }
+}
+
+func ifElse(c int, x int, y int) int {
+ if c != 0 {
+ return x
+ }
+ return y
+}
+
+func plusSign(x float64) string {
+ if x >= 0 {
+ return "+"
+ }
+ return ""
+}
diff --git a/std/thumbhash/decode_thumbhash.wuffs b/std/thumbhash/decode_thumbhash.wuffs
index 72be5fc..7556ef6 100644
--- a/std/thumbhash/decode_thumbhash.wuffs
+++ b/std/thumbhash/decode_thumbhash.wuffs
@@ -33,17 +33,154 @@
pub const DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE : base.u64 = 0
pub struct decoder? implements base.image_decoder(
- pixfmt : base.u32,
- width : base.u32[..= 32],
- height : base.u32[..= 32],
+ pixfmt : base.u32,
+ w_dimension_code : base.u8[..= 7],
+ h_dimension_code : base.u8[..= 7],
// The call sequence state machine is discussed in
// (/doc/std/image-decoders-call-sequence.md).
call_sequence : base.u8,
+ frame_config_io_position : base.u8[..= 9],
+
+ // The L, P and Q DC values occupy 6 bits in the file format.
+ // Conceptually, they range from 0 to 1 (for L) or from -1 to +1 (for P
+ // and Q). That's what values are calculated in the original JavaScript
+ // reference implementation, which uses floating point.
+ //
+ // This implementation uses fixed point. The fx and fy cosine tables
+ // and the FROM_4_BITS_TO_PLUS_MINUS_ETC tables are all stored as 2.14
+ // signed fixed point (represented in this code as a base.u16, since
+ // Wuffs as of 2024 only speaks unsigned integers). Multiplying two of
+ // those uses 4.28 (as a base.u32). Multiplying three of those uses
+ // 22.42 (as a base.u64).
+ //
+ // We eventually combine L, P and Q values so we need them to use the
+ // same denominator. l_scale is out of 31 and p_scale and q_scale are
+ // out of 63, so the common denominator is ((63 * 31) << 42) =
+ // 8589_384836_186112 = 0x001E_8400_0000_0000.
+ //
+ // The A channel is not combined with L, P or Q and a_scale is out of
+ // 15, so we can keep the a_dc denominator at (15 << 42).
+ //
+ // Let LDENOM = ((63 * 31) << 14) = 0x01E8_4000.
+ //
+ // Let ADENOM = ( 15 << 14) = 0x0003_C000.
+ l_dc : base.u64, // Fixed-point denominator is (LDENOM << 28).
+ p_dc : base.u64, // Fixed-point denominator is (LDENOM << 28).
+ q_dc : base.u64, // Fixed-point denominator is (LDENOM << 28).
+ a_dc : base.u64, // Fixed-point denominator is (ADENOM << 28).
+
+ l_scale : base.u8[..= 31],
+ p_scale : base.u8[..= 63],
+ q_scale : base.u8[..= 63],
+ a_scale : base.u8[..= 15],
+
+ // The first three fields determine lx and ly: the exclusive max of the
+ // (cx, cy) pairs indexing two-dimensional AC coefficients. The (0, 0)
+ // pair is the DC coefficient. (lx / ly) is also the image's aspect
+ // ratio, after rounding width and height to a whole number of pixels
+ // such that the longest dimension is 32.
+ //
+ // Let lminor and lmajor alias lx and ly if is_landscape is 0 (false).
+ // If is_landscape is 1 (true) then these are swapped.
+ //
+ // lminor equals max(3, l_count). lmajor equals 7 or 5, depending on
+ // whether has_alpha is 0 or 1. For example, if has_alpha = 0, l_count
+ // = 5 and is_landscape = 1 then lx = 7 and ly = 5, so that width = 32
+ // and height = 23.
+ //
+ // lx and ly then detemine the number of L AC coefficients (the number
+ // of lac elements that are used), ranging in 10 ..= 27. The number for
+ // P and Q is always 5 and the number for A is 0 or 14, depending on
+ // has_alpha. Total file size is an optional 3-byte magic identifier
+ // plus (5 + has_alpha) bytes of header plus 4 bits per AC coefficient.
+ // As a table:
+ //
+ // When has_alpha = 0 and is_landscape = 0:
+ // l_count: 3 lx / ly = 3 / 7 w = 14 h = 32 #lac = 14 #pac = #qac = 5 #aac = 0 file_size = 3 + 17
+ // l_count: 4 lx / ly = 4 / 7 w = 18 h = 32 #lac = 18 #pac = #qac = 5 #aac = 0 file_size = 3 + 19
+ // l_count: 5 lx / ly = 5 / 7 w = 23 h = 32 #lac = 22 #pac = #qac = 5 #aac = 0 file_size = 3 + 21
+ // l_count: 6 lx / ly = 6 / 7 w = 27 h = 32 #lac = 26 #pac = #qac = 5 #aac = 0 file_size = 3 + 23
+ // l_count: 7 lx / ly = 7 / 7 w = 32 h = 32 #lac = 27 #pac = #qac = 5 #aac = 0 file_size = 3 + 24
+ //
+ // When has_alpha = 1 and is_landscape = 0:
+ // l_count: 3 lx / ly = 3 / 5 w = 19 h = 32 #lac = 10 #pac = #qac = 5 #aac = 14 file_size = 3 + 23
+ // l_count: 4 lx / ly = 4 / 5 w = 26 h = 32 #lac = 13 #pac = #qac = 5 #aac = 14 file_size = 3 + 25
+ // l_count: 5 lx / ly = 5 / 5 w = 32 h = 32 #lac = 14 #pac = #qac = 5 #aac = 14 file_size = 3 + 25
+ // l_count: 6 lx / ly = 6 / 5 w = 32 h = 27 #lac = 19 #pac = #qac = 5 #aac = 14 file_size = 3 + 28
+ // l_count: 7 lx / ly = 7 / 5 w = 32 h = 23 #lac = 22 #pac = #qac = 5 #aac = 14 file_size = 3 + 29
+ //
+ // A "(cx * ly) < (lx * (ly - cy))" constraint determines which L AC
+ // coefficients are used. Here are some visualizations, where 'D' is
+ // the DC coefficient. When has_alpha = 0:
+ // - the 14 '3' coefficients are always used,
+ // - the (18 - 14) '4' coefficients are also used when (l_count >= 4),
+ // - the (22 - 18) '5' coefficients are also used when (l_count >= 5),
+ // - the (26 - 22) '6' coefficients are also used when (l_count >= 6),
+ // - the (27 - 26) '7' coefficients are also used when (l_count >= 7),
+ //
+ // When has_alpha = 0:
+ // D334567
+ // 333456
+ // 33356
+ // 3346
+ // 335
+ // 34
+ // 3
+ //
+ // When has_alpha = 1:
+ // D334567
+ // 333467
+ // 33467
+ // 336
+ // 36
+ //
+ // The same visualization for P and Q:
+ // D33
+ // 33
+ // 3
+ //
+ // The same visualization for A (when has_alpha = 1):
+ // D3333
+ // 3333
+ // 333
+ // 33
+ // 3
+ has_alpha : base.u8[..= 1],
+ l_count : base.u8[..= 7],
+ is_landscape : base.u8[..= 1],
+ lx : base.u32[..= 7],
+ ly : base.u32[..= 7],
+
swizzler : base.pixel_swizzler,
util : base.utility,
) + (
+ // AC coefficients, doubled (†) when stored here to simplify the later
+ // (fx[cx] * fy[cy]) calculation in from_coeffs_to_pixels.
+ //
+ // Before doubling, these conceptually range from -1 to +1 (for L and
+ // A) or from -1.25 to +1.25 (for P and Q, boosted "to compensate for
+ // quantization" in the original JavaScript reference implementation).
+ //
+ // These AC values will eventually be multiplied by fx[cx] and fy[cy],
+ // both of which are 2.14 signed fixed point, so these fixed point
+ // values use the DC value denominators right-shifted by 28: LDENOM =
+ // ((63 * 31) << 14) for L, P and Q and ADENOM = (15 << 14) for A.
+ //
+ // For example, if lac[10] was 23998464 as a base.u32 here in Wuffs
+ // code, the corresponding undoubled floating point value in JavaScript
+ // code would be (23998464.0 / (2 * LDENOM)) = 0.375.
+ //
+ // Not every element is used, only the first up-to-27 (lac), 5 (pac and
+ // qac) or 14 (aac). But we round up the array sizes up to a power of 2
+ // (and bitwise-and the array indexes) to simplify bounds checking.
+ lac : array[32] base.u32, // Fixed-point denominator is LDENOM.
+ pac : array[8] base.u32, // Fixed-point denominator is LDENOM.
+ qac : array[8] base.u32, // Fixed-point denominator is LDENOM.
+ aac : array[16] base.u32, // Fixed-point denominator is ADENOM.
+
+ // 32 rows, 32 BGRA pixels per row.
pixels : array[32] array[128] base.u8,
)
@@ -68,7 +205,8 @@
}
pri func decoder.do_decode_image_config?(dst: nptr base.image_config, src: base.io_reader) {
- var c32 : base.u32
+ var c32 : base.u32
+ var swap : base.u8[..= 7]
if this.call_sequence <> 0x00 {
return base."#bad call sequence"
@@ -80,18 +218,55 @@
return "#bad header"
}
+ c32 = args.src.read_u24le_as_u32?()
+ this.l_dc = (((c32 >> 0x00) & 63) as base.u64) * (31 << 42)
+ this.p_dc = ((((c32 >> 0x06) & 63) as base.u64) * (31 << 43)) ~mod- ((63 * 31) << 42)
+ this.q_dc = ((((c32 >> 0x0C) & 63) as base.u64) * (31 << 43)) ~mod- ((63 * 31) << 42)
+ this.l_scale = ((c32 >> 0x12) & 31) as base.u8
+ this.has_alpha = ((c32 >> 0x17) & 1) as base.u8
+
+ c32 = args.src.read_u16le_as_u32?()
+ this.l_count = ((c32 >> 0x00) & 7) as base.u8
+ this.p_scale = ((c32 >> 0x03) & 63) as base.u8
+ this.q_scale = ((c32 >> 0x09) & 63) as base.u8
+ this.is_landscape = ((c32 >> 0x0F) & 1) as base.u8
+ this.w_dimension_code = (DIMENSION_CODES_FROM_HAS_ALPHA_AND_L_COUNT[this.has_alpha][this.l_count] >> 4) & 7
+ this.h_dimension_code = (DIMENSION_CODES_FROM_HAS_ALPHA_AND_L_COUNT[this.has_alpha][this.l_count] >> 0) & 7
+ if this.is_landscape <> 0 {
+ swap = this.w_dimension_code
+ this.w_dimension_code = this.h_dimension_code
+ this.h_dimension_code = swap
+ }
+
+ if this.is_landscape <> 0 {
+ this.lx = (7 - (2 * this.has_alpha)) as base.u32
+ this.ly = (this.l_count.max(no_less_than: 3)) as base.u32
+ } else {
+ this.lx = (this.l_count.max(no_less_than: 3)) as base.u32
+ this.ly = (7 - (2 * this.has_alpha)) as base.u32
+ }
+
+ this.frame_config_io_position = 8
+ if this.has_alpha <> 0 {
+ this.frame_config_io_position = 9
+ c32 = args.src.read_u8_as_u32?()
+ this.a_dc = (((c32 >> 0x00) & 15) as base.u64) << 42
+ this.a_scale = ((c32 >> 0x04) & 15) as base.u8
+ }
+
this.pixfmt = base.PIXEL_FORMAT__BGRX
- this.width = 32
- this.height = 32
+ if this.has_alpha <> 0 {
+ this.pixfmt = base.PIXEL_FORMAT__BGRA_NONPREMUL
+ }
if args.dst <> nullptr {
args.dst.set!(
pixfmt: this.pixfmt,
pixsub: 0,
- width: this.width,
- height: this.height,
- first_frame_io_position: 3,
- first_frame_is_opaque: this.pixfmt == base.PIXEL_FORMAT__BGRX)
+ width: DIMENSIONS_FROM_DIMENSION_CODES[this.w_dimension_code] as base.u32,
+ height: DIMENSIONS_FROM_DIMENSION_CODES[this.h_dimension_code] as base.u32,
+ first_frame_io_position: this.frame_config_io_position as base.u64,
+ first_frame_is_opaque: this.has_alpha == 0)
}
this.call_sequence = 0x20
@@ -115,7 +290,7 @@
} else if this.call_sequence < 0x20 {
this.do_decode_image_config?(dst: nullptr, src: args.src)
} else if this.call_sequence == 0x28 {
- if 3 <> args.src.position() {
+ if (this.frame_config_io_position as base.u64) <> args.src.position() {
return base."#bad restart"
}
} else if this.call_sequence == 0x40 {
@@ -129,13 +304,13 @@
args.dst.set!(bounds: this.util.make_rect_ie_u32(
min_incl_x: 0,
min_incl_y: 0,
- max_excl_x: this.width,
- max_excl_y: this.height),
+ max_excl_x: DIMENSIONS_FROM_DIMENSION_CODES[this.w_dimension_code] as base.u32,
+ max_excl_y: DIMENSIONS_FROM_DIMENSION_CODES[this.h_dimension_code] as base.u32),
duration: 0,
index: 0,
- io_position: 3,
+ io_position: this.frame_config_io_position as base.u64,
disposal: 0,
- opaque_within_bounds: this.pixfmt == base.PIXEL_FORMAT__BGRX,
+ opaque_within_bounds: this.has_alpha == 0,
overwrite_instead_of_blend: false,
background_color: 0x0000_0000)
}
@@ -176,7 +351,8 @@
return status
}
- this.from_src_to_pixels?(src: args.src)
+ this.from_src_to_coeffs?(src: args.src)
+ this.from_coeffs_to_pixels!()
status = this.from_pixels_to_dst!(dst: args.dst)
if not status.is_ok() {
return status
@@ -185,23 +361,304 @@
this.call_sequence = 0x60
}
-pri func decoder.from_src_to_pixels?(src: base.io_reader) {
+pri func decoder.from_src_to_coeffs?(src: base.io_reader) {
+ var c8 : base.u8
+ var cy : base.u32
+ var cx : base.u32
+ var i : base.u32
+ var has_bits : base.bool
+
+ // Read L AC coefficients.
+ i = 0
+ cy = 0
+ while cy < this.ly {
+ cx = 0
+ if cy == 0 {
+ cx = 1
+ }
+ while (cx ~mod* this.ly) < (this.lx ~mod* (this.ly ~mod- cy)) {
+ if has_bits {
+ has_bits = false
+ c8 >>= 4
+ } else {
+ has_bits = true
+ c8 = args.src.read_u8?()
+ }
+
+ // Multiply by (63 * 2) to double (†) and get to LDENOM.
+ this.lac[i & 31] = ((this.l_scale as base.u32) ~mod* 126) ~mod*
+ this.util.sign_extend_convert_u16_u32(a: FROM_4_BITS_TO_PLUS_MINUS_1_00[c8 & 15])
+
+ i ~mod+= 1
+ cx ~mod+= 1
+ }
+ cy ~mod+= 1
+ }
+
+ // Read P AC coefficients.
+ i = 0
+ cx = 0
+ while cx < 5 {
+ if has_bits {
+ has_bits = false
+ c8 >>= 4
+ } else {
+ has_bits = true
+ c8 = args.src.read_u8?()
+ }
+
+ // Multiply by (31 * 2) to double (†) and get to LDENOM.
+ this.pac[i & 7] = ((this.p_scale as base.u32) ~mod* 62) ~mod*
+ this.util.sign_extend_convert_u16_u32(a: FROM_4_BITS_TO_PLUS_MINUS_1_25[c8 & 15])
+
+ i ~mod+= 1
+ cx ~mod+= 1
+ }
+
+ // Read Q AC coefficients.
+ i = 0
+ cx = 0
+ while cx < 5 {
+ if has_bits {
+ has_bits = false
+ c8 >>= 4
+ } else {
+ has_bits = true
+ c8 = args.src.read_u8?()
+ }
+
+ // Multiply by (31 * 2) to double (†) and get to LDENOM.
+ this.qac[i & 7] = ((this.q_scale as base.u32) ~mod* 62) ~mod*
+ this.util.sign_extend_convert_u16_u32(a: FROM_4_BITS_TO_PLUS_MINUS_1_25[c8 & 15])
+
+ i ~mod+= 1
+ cx ~mod+= 1
+ }
+
+ // Read A AC coefficients.
+ if this.has_alpha == 0 {
+ return ok
+ }
+ i = 0
+ cx = 0
+ while cx < 14 {
+ if has_bits {
+ has_bits = false
+ c8 >>= 4
+ } else {
+ has_bits = true
+ c8 = args.src.read_u8?()
+ }
+
+ // Multiply by 2 to double (†). We're already at ADENOM.
+ this.aac[i & 15] = ((this.a_scale as base.u32) ~mod* 2) ~mod*
+ this.util.sign_extend_convert_u16_u32(a: FROM_4_BITS_TO_PLUS_MINUS_1_00[c8 & 15])
+
+ i ~mod+= 1
+ cx ~mod+= 1
+ }
+}
+
+pri func decoder.from_coeffs_to_pixels!() {
+ var h : base.u32[..= 32]
+ var w : base.u32[..= 32]
+
+ // fx[7] and fy[7] are unused but we round up the array sizes up to a power
+ // of 2 (and bitwise-and the array indexes) to simplify bounds checking.
+ var fy : array[8] base.u32
+ var fx : array[8] base.u32
+
+ var cosines_base_y : base.u32[..= 127]
+ var cosines_base_x : base.u32[..= 127]
+
var y : base.u32
var x : base.u32
- // TODO: actually implement the format. For now, fill in a placeholder
- // blue-green gradient.
+ var f : base.u32
+ var l : base.u64
+ var p : base.u64
+ var q : base.u64
+ var b : base.u64
+ var g : base.u64
+ var r : base.u64
+ var a : base.u64
+
+ var i : base.u32
+ var cy : base.u32
+ var cx : base.u32
+
+ h = DIMENSIONS_FROM_DIMENSION_CODES[this.h_dimension_code] as base.u32
+ w = DIMENSIONS_FROM_DIMENSION_CODES[this.w_dimension_code] as base.u32
+ fy[0] = 0x4000 // This is always cos(0), scaled by 0x4000 = (1 << 14).
+ fx[0] = 0x4000 // This is always cos(0), scaled by 0x4000 = (1 << 14).
+ a = 0xFF
y = 0
- while y < 32 {
+ while y < h {
+ assert y < 32 via "a < b: a < c; c <= b"(c: h)
+
+ cosines_base_y = CUMULATIVE_DIMENSIONS[this.h_dimension_code] as base.u32
+ fy[1] = this.util.sign_extend_convert_u16_u32(a: COSINES[cosines_base_y + y][0])
+ fy[2] = this.util.sign_extend_convert_u16_u32(a: COSINES[cosines_base_y + y][1])
+ fy[3] = this.util.sign_extend_convert_u16_u32(a: COSINES[cosines_base_y + y][2])
+ fy[4] = this.util.sign_extend_convert_u16_u32(a: COSINES[cosines_base_y + y][3])
+ fy[5] = this.util.sign_extend_convert_u16_u32(a: COSINES[cosines_base_y + y][4])
+ fy[6] = this.util.sign_extend_convert_u16_u32(a: COSINES[cosines_base_y + y][5])
+
+ // The original JavaScript reference implementation also multiplied
+ // fy[cy] by 2 but we have already adjusted for that (†).
+
x = 0
- while x < 32,
+ while x < w,
inv y < 32,
{
- this.pixels[y][(4 * x) + 0] = (x * 8) as base.u8
- this.pixels[y][(4 * x) + 1] = (y * 8) as base.u8
- this.pixels[y][(4 * x) + 2] = 0x00
- this.pixels[y][(4 * x) + 3] = 0xFF
+ assert x < 32 via "a < b: a < c; c <= b"(c: w)
+
+ cosines_base_x = CUMULATIVE_DIMENSIONS[this.w_dimension_code] as base.u32
+ fx[1] = this.util.sign_extend_convert_u16_u32(a: COSINES[cosines_base_x + x][0])
+ fx[2] = this.util.sign_extend_convert_u16_u32(a: COSINES[cosines_base_x + x][1])
+ fx[3] = this.util.sign_extend_convert_u16_u32(a: COSINES[cosines_base_x + x][2])
+ fx[4] = this.util.sign_extend_convert_u16_u32(a: COSINES[cosines_base_x + x][3])
+ fx[5] = this.util.sign_extend_convert_u16_u32(a: COSINES[cosines_base_x + x][4])
+ fx[6] = this.util.sign_extend_convert_u16_u32(a: COSINES[cosines_base_x + x][5])
+
+ // Accumulate L.
+ l = this.l_dc
+ i = 0
+ cy = 0
+ while cy < this.ly,
+ inv y < 32,
+ inv x < 32,
+ {
+ cx = 0
+ if cy == 0 {
+ cx = 1
+ }
+ while (cx ~mod* this.ly) < (this.lx ~mod* (this.ly ~mod- cy)),
+ inv y < 32,
+ inv x < 32,
+ {
+ f = fx[cx & 7] ~mod* fy[cy & 7]
+ l ~mod+= this.util.sign_extend_convert_u32_u64(a: f) ~mod*
+ this.util.sign_extend_convert_u32_u64(a: this.lac[i & 31])
+ i ~mod+= 1
+ cx ~mod+= 1
+ }
+ cy ~mod+= 1
+ }
+
+ // Accumulate P and Q.
+ p = this.p_dc
+ q = this.q_dc
+ i = 0
+ cy = 0
+ while cy < 3,
+ inv y < 32,
+ inv x < 32,
+ {
+ cx = 0
+ if cy == 0 {
+ cx = 1
+ }
+ while cx < (3 - cy),
+ inv y < 32,
+ inv x < 32,
+ inv cy < 3,
+ {
+ assert cx < 3 via "a < b: a < c; c <= b"(c: 3 - cy)
+ f = fx[cx] ~mod* fy[cy]
+ p ~mod+= this.util.sign_extend_convert_u32_u64(a: f) ~mod*
+ this.util.sign_extend_convert_u32_u64(a: this.pac[i & 7])
+ q ~mod+= this.util.sign_extend_convert_u32_u64(a: f) ~mod*
+ this.util.sign_extend_convert_u32_u64(a: this.qac[i & 7])
+ i ~mod+= 1
+ cx ~mod+= 1
+ }
+ cy ~mod+= 1
+ }
+
+ // Convert LPQ to RGB. The code is the i64 equivalents of:
+ // b = l - ((2 * p) / 3)
+ // r = (((3 * l) + q) - b) / 2
+ // g = r - q
+ b = l ~mod- this.util.i64_divide(a: 2 ~mod* p, b: 3)
+ r = this.util.sign_extend_rshift_u64(a: ((3 ~mod* l) ~mod+ q) ~mod- b, n: 1)
+ g = r ~mod- q
+
+ // Rescale from (LDENOM << 28) to 255 and clamp.
+ //
+ // (LDENOM << 28) = 8589_384836_186112 = (33_683862_102690 * 255) + 162
+ //
+ // Rounding down (instead of rounding to nearest) more closely
+ // matches the original JavaScript reference implementation. The
+ // two implementations aren't a 100% match, due to floating point
+ // versus fixed point rounding errors, but they're pretty close.
+ if (b >> 63) <> 0 {
+ b = 0
+ } else if b >= (33_683862_102690 * 255) {
+ b = 255
+ } else {
+ b /= 33_683862_102690
+ }
+ if (g >> 63) <> 0 {
+ g = 0
+ } else if g >= (33_683862_102690 * 255) {
+ g = 255
+ } else {
+ g /= 33_683862_102690
+ }
+ if (r >> 63) <> 0 {
+ r = 0
+ } else if r >= (33_683862_102690 * 255) {
+ r = 255
+ } else {
+ r /= 33_683862_102690
+ }
+
+ // Accumulate, rescale from (ADENOM << 28) and clamp A.
+ if this.has_alpha <> 0 {
+ a = this.a_dc
+ i = 0
+ cy = 0
+ while cy < 5,
+ inv y < 32,
+ inv x < 32,
+ {
+ cx = 0
+ if cy == 0 {
+ cx = 1
+ }
+ while cx < (5 - cy),
+ inv y < 32,
+ inv x < 32,
+ inv cy < 5,
+ {
+ assert cx < 5 via "a < b: a < c; c <= b"(c: 5 - cy)
+ f = fx[cx] ~mod* fy[cy]
+ a ~mod+= this.util.sign_extend_convert_u32_u64(a: f) ~mod*
+ this.util.sign_extend_convert_u32_u64(a: this.aac[i & 15])
+ i ~mod+= 1
+ cx ~mod+= 1
+ }
+ cy ~mod+= 1
+ }
+
+ // (ADENOM << 28) = 65_970697_666560 = (258708_618300 * 255) + 60
+ if (a >> 63) <> 0 {
+ a = 0
+ } else if a >= (258708_618300 * 255) {
+ a = 255
+ } else {
+ a /= 258708_618300
+ }
+ }
+
+ // Store the BGRA pixel.
+ this.pixels[y][(4 * x) + 0] = (b & 0xFF) as base.u8
+ this.pixels[y][(4 * x) + 1] = (g & 0xFF) as base.u8
+ this.pixels[y][(4 * x) + 2] = (r & 0xFF) as base.u8
+ this.pixels[y][(4 * x) + 3] = (a & 0xFF) as base.u8
+
x += 1
}
y += 1
@@ -209,6 +666,9 @@
}
pri func decoder.from_pixels_to_dst!(dst: ptr base.pixel_buffer) base.status {
+ var h : base.u32[..= 32]
+ var w : base.u32[..= 32]
+
var dst_pixfmt : base.pixel_format
var dst_bits_per_pixel : base.u32[..= 256]
var dst_bytes_per_pixel : base.u32[..= 32]
@@ -218,6 +678,9 @@
var dst : slice base.u8
var src : slice base.u8
+ h = DIMENSIONS_FROM_DIMENSION_CODES[this.h_dimension_code] as base.u32
+ w = DIMENSIONS_FROM_DIMENSION_CODES[this.w_dimension_code] as base.u32
+
// TODO: the dst_pixfmt variable shouldn't be necessary. We should be able
// to chain the two calls: "args.dst.pixel_format().bits_per_pixel()".
dst_pixfmt = args.dst.pixel_format()
@@ -226,12 +689,12 @@
return base."#unsupported option"
}
dst_bytes_per_pixel = dst_bits_per_pixel / 8
- dst_bytes_per_row = (this.width * dst_bytes_per_pixel) as base.u64
+ dst_bytes_per_row = (w * dst_bytes_per_pixel) as base.u64
tab = args.dst.plane(p: 0)
- while y < this.height {
- assert y < 32 via "a < b: a < c; c <= b"(c: this.height)
- src = this.pixels[y][.. this.width * 4]
+ while y < h {
+ assert y < 32 via "a < b: a < c; c <= b"(c: h)
+ src = this.pixels[y][.. w * 4]
dst = tab.row_u32(y: y)
if dst_bytes_per_row < dst.length() {
@@ -253,8 +716,8 @@
return this.util.make_rect_ie_u32(
min_incl_x: 0,
min_incl_y: 0,
- max_excl_x: this.width,
- max_excl_y: this.height)
+ max_excl_x: DIMENSIONS_FROM_DIMENSION_CODES[this.w_dimension_code] as base.u32,
+ max_excl_y: DIMENSIONS_FROM_DIMENSION_CODES[this.h_dimension_code] as base.u32)
}
pub func decoder.num_animation_loops() base.u32 {
@@ -279,7 +742,7 @@
if this.call_sequence < 0x20 {
return base."#bad call sequence"
}
- if (args.index <> 0) or (args.io_position <> 3) {
+ if (args.index <> 0) or (args.io_position <> (this.frame_config_io_position as base.u64)) {
return base."#bad argument"
}
this.call_sequence = 0x28
@@ -297,3 +760,248 @@
pub func decoder.workbuf_len() base.range_ii_u64 {
return this.util.make_range_ii_u64(min_incl: 0, max_incl: 0)
}
+
+// DIMENSIONS_FROM_DIMENSION_CODES enumerates all possible widths and heights.
+pri const DIMENSIONS_FROM_DIMENSION_CODES : roarray[8] base.u8[..= 32] = [
+ 0, 14, 18, 19, 23, 26, 27, 32,
+]
+
+// CUMULATIVE_DIMENSIONS is the sum-table of DIMENSIONS_FROM_DIMENSION_CODES.
+pri const CUMULATIVE_DIMENSIONS : roarray[8] base.u8[..= 127] = [
+ 0, 0, 14, 32, 51, 74, 100, 127,
+]
+
+// DIMENSION_CODES_FROM_HAS_ALPHA_AND_L_COUNT holds the width (high 4 bits) and
+// height (low 4 bits) codes. For example, if has_alpha is 0 and l_count is 5
+// then the 0x47 value means that the image is 23×32 (so the aspect ratio is
+// 0.71875), because:
+// - the width is DIMENSIONS_FROM_DIMENSION_CODES[4] = 23
+// - the height is DIMENSIONS_FROM_DIMENSION_CODES[7] = 32
+//
+// If is_landscape is 1 (true) then the width and height are swapped.
+pri const DIMENSION_CODES_FROM_HAS_ALPHA_AND_L_COUNT : roarray[2] roarray[8] base.u8 = [
+ [0x17, 0x17, 0x17, 0x17, 0x27, 0x47, 0x67, 0x77],
+ [0x37, 0x37, 0x37, 0x37, 0x57, 0x77, 0x76, 0x74],
+]
+
+// FROM_4_BITS_TO_PLUS_MINUS_1_00 converts AC coefficients to 2.14 fixed point.
+pri const FROM_4_BITS_TO_PLUS_MINUS_1_00 : roarray[16] base.u16 = [
+ 0xC000, // -1.000
+ 0xC889, // -0.867
+ 0xD111, // -0.733
+ 0xD99A, // -0.600
+ 0xE222, // -0.467
+ 0xEAAB, // -0.333
+ 0xF333, // -0.200
+ 0xFBBC, // -0.067
+ 0x0444, // +0.067
+ 0x0CCD, // +0.200
+ 0x1555, // +0.333
+ 0x1DDE, // +0.467
+ 0x2666, // +0.600
+ 0x2EEF, // +0.733
+ 0x3777, // +0.867
+ 0x4000, // +1.000
+]
+
+// FROM_4_BITS_TO_PLUS_MINUS_1_25 converts AC coefficients to 2.14 fixed point.
+pri const FROM_4_BITS_TO_PLUS_MINUS_1_25 : roarray[16] base.u16 = [
+ 0xB000, // -1.250
+ 0xBAAB, // -1.083
+ 0xC555, // -0.917
+ 0xD000, // -0.750
+ 0xDAAB, // -0.583
+ 0xE555, // -0.417
+ 0xF000, // -0.250
+ 0xFAAB, // -0.083
+ 0x0555, // +0.083
+ 0x1000, // +0.250
+ 0x1AAB, // +0.417
+ 0x2555, // +0.583
+ 0x3000, // +0.750
+ 0x3AAB, // +0.917
+ 0x4555, // +1.083
+ 0x5000, // +1.250
+]
+
+// COSINES[etc][cx - 1] holds cos(π * cx * (x + 0.5) / w) as 2.14 fixed point.
+// Each 6-element row is copied to fx[1 .. 7] and fy[1 .. 7]. fx[0] and fy[0]
+// are always equal to 0x4000, which is cos(0) as 2.14 fixed point. fx[7] and
+// fy[7] are unused.
+//
+// (x + 0.5) ranges from (0 + 0.5) to (w - 0.5). Valid w values are given by
+// the positive elements of DIMENSIONS_FROM_DIMENSION_CODES.
+pri const COSINES : roarray[159] roarray[6] base.u16 = [
+ // w = 14
+ [0x3F99, 0x3E65, 0x3C69, 0x39A9, 0x3631, 0x320A], // x = 0
+ [0x3C69, 0x320A, 0x220D, 0x0E3E, 0xF8D6, 0xE43B], // x = 1
+ [0x3631, 0x1BC5, 0xF8D6, 0xD819, 0xC397, 0xC19B], // x = 2
+ [0x2D41, 0x0000, 0xD2BF, 0xC000, 0xD2BF, 0x0000], // x = 3
+ [0x220D, 0xE43B, 0xC067, 0xD819, 0x1523, 0x3E65], // x = 4
+ [0x1523, 0xCDF6, 0xC9CF, 0x0E3E, 0x3F99, 0x1BC5], // x = 5
+ [0x072A, 0xC19B, 0xEADD, 0x39A9, 0x220D, 0xCDF6], // x = 6
+ [0xF8D6, 0xC19B, 0x1523, 0x39A9, 0xDDF3, 0xCDF6], // x = 7
+ [0xEADD, 0xCDF6, 0x3631, 0x0E3E, 0xC067, 0x1BC5], // x = 8
+ [0xDDF3, 0xE43B, 0x3F99, 0xD819, 0xEADD, 0x3E65], // x = 9
+ [0xD2BF, 0x0000, 0x2D41, 0xC000, 0x2D41, 0x0000], // x = 10
+ [0xC9CF, 0x1BC5, 0x072A, 0xD819, 0x3C69, 0xC19B], // x = 11
+ [0xC397, 0x320A, 0xDDF3, 0x0E3E, 0x072A, 0xE43B], // x = 12
+ [0xC067, 0x3E65, 0xC397, 0x39A9, 0xC9CF, 0x320A], // x = 13
+
+ // w = 18
+ [0x3FC2, 0x3F07, 0x3DD2, 0x3C24, 0x3A01, 0x376D], // x = 0
+ [0x3DD2, 0x376D, 0x2D41, 0x2000, 0x1090, 0x0000], // x = 1
+ [0x3A01, 0x2923, 0x1090, 0xF4E3, 0xDB4B, 0xC893], // x = 2
+ [0x346D, 0x15E4, 0xEF70, 0xCEF9, 0xC03E, 0xC893], // x = 3
+ [0x2D41, 0x0000, 0xD2BF, 0xC000, 0xD2BF, 0x0000], // x = 4
+ [0x24B5, 0xEA1C, 0xC22E, 0xCEF9, 0x0594, 0x376D], // x = 5
+ [0x1B0C, 0xD6DD, 0xC22E, 0xF4E3, 0x346D, 0x376D], // x = 6
+ [0x1090, 0xC893, 0xD2BF, 0x2000, 0x3DD2, 0x0000], // x = 7
+ [0x0594, 0xC0F9, 0xEF70, 0x3C24, 0x1B0C, 0xC893], // x = 8
+ [0xFA6C, 0xC0F9, 0x1090, 0x3C24, 0xE4F4, 0xC893], // x = 9
+ [0xEF70, 0xC893, 0x2D41, 0x2000, 0xC22E, 0x0000], // x = 10
+ [0xE4F4, 0xD6DD, 0x3DD2, 0xF4E3, 0xCB93, 0x376D], // x = 11
+ [0xDB4B, 0xEA1C, 0x3DD2, 0xCEF9, 0xFA6C, 0x376D], // x = 12
+ [0xD2BF, 0x0000, 0x2D41, 0xC000, 0x2D41, 0x0000], // x = 13
+ [0xCB93, 0x15E4, 0x1090, 0xCEF9, 0x3FC2, 0xC893], // x = 14
+ [0xC5FF, 0x2923, 0xEF70, 0xF4E3, 0x24B5, 0xC893], // x = 15
+ [0xC22E, 0x376D, 0xD2BF, 0x2000, 0xEF70, 0x0000], // x = 16
+ [0xC03E, 0x3F07, 0xC22E, 0x3C24, 0xC5FF, 0x376D], // x = 17
+
+ // w = 19
+ [0x3FC8, 0x3F21, 0x3E0B, 0x3C88, 0x3A9C, 0x3849], // x = 0
+ [0x3E0B, 0x3849, 0x2F16, 0x2301, 0x14C8, 0x0549], // x = 1
+ [0x3A9C, 0x2B59, 0x14C8, 0xFAB7, 0xE18A, 0xCD7F], // x = 2
+ [0x3594, 0x19B5, 0xF577, 0xD4A7, 0xC1F5, 0xC378], // x = 3
+ [0x2F16, 0x0549, 0xD8B1, 0xC0DF, 0xCA6C, 0xF04A], // x = 4
+ [0x274F, 0xF04A, 0xC564, 0xC7B7, 0xF577, 0x2B59], // x = 5
+ [0x1E76, 0xDCFF, 0xC038, 0xE64B, 0x274F, 0x3F21], // x = 6
+ [0x14C8, 0xCD7F, 0xCA6C, 0x0FB6, 0x3FC8, 0x19B5], // x = 7
+ [0x0A89, 0xC378, 0xE18A, 0x3281, 0x2F16, 0xDCFF], // x = 8
+ [0x0000, 0xC000, 0x0000, 0x4000, 0x0000, 0xC000], // x = 9
+ [0xF577, 0xC378, 0x1E76, 0x3281, 0xD0EA, 0xDCFF], // x = 10
+ [0xEB38, 0xCD7F, 0x3594, 0x0FB6, 0xC038, 0x19B5], // x = 11
+ [0xE18A, 0xDCFF, 0x3FC8, 0xE64B, 0xD8B1, 0x3F21], // x = 12
+ [0xD8B1, 0xF04A, 0x3A9C, 0xC7B7, 0x0A89, 0x2B59], // x = 13
+ [0xD0EA, 0x0549, 0x274F, 0xC0DF, 0x3594, 0xF04A], // x = 14
+ [0xCA6C, 0x19B5, 0x0A89, 0xD4A7, 0x3E0B, 0xC378], // x = 15
+ [0xC564, 0x2B59, 0xEB38, 0xFAB7, 0x1E76, 0xCD7F], // x = 16
+ [0xC1F5, 0x3849, 0xD0EA, 0x2301, 0xEB38, 0x0549], // x = 17
+ [0xC038, 0x3F21, 0xC1F5, 0x3C88, 0xC564, 0x3849], // x = 18
+
+ // w = 23
+ [0x3FDA, 0x3F67, 0x3EA9, 0x3DA0, 0x3C4E, 0x3AB4], // x = 0
+ [0x3EA9, 0x3AB4, 0x3449, 0x2BAF, 0x2141, 0x156F], // x = 1
+ [0x3C4E, 0x31A5, 0x2141, 0x0D05, 0xF749, 0xE28E], // x = 2
+ [0x38D3, 0x24E8, 0x08B7, 0xEA91, 0xD13A, 0xC260], // x = 3
+ [0x3449, 0x156F, 0xEEBC, 0xCE5B, 0xC026, 0xC951], // x = 4
+ [0x2EC6, 0x045E, 0xD79C, 0xC099, 0xCBB7, 0xF2FB], // x = 5
+ [0x2864, 0xF2FB, 0xC72D, 0xC54C, 0xEEBC, 0x24E8], // x = 6
+ [0x2141, 0xE28E, 0xC026, 0xDB18, 0x197F, 0x3F67], // x = 7
+ [0x197F, 0xD451, 0xC3B2, 0xFBA2, 0x38D3, 0x31A5], // x = 8
+ [0x1144, 0xC951, 0xD13A, 0x1D72, 0x3EA9, 0x045E], // x = 9
+ [0x08B7, 0xC260, 0xE681, 0x36AF, 0x2864, 0xD451], // x = 10
+ [0x0000, 0xC000, 0x0000, 0x4000, 0x0000, 0xC000], // x = 11
+ [0xF749, 0xC260, 0x197F, 0x36AF, 0xD79C, 0xD451], // x = 12
+ [0xEEBC, 0xC951, 0x2EC6, 0x1D72, 0xC157, 0x045E], // x = 13
+ [0xE681, 0xD451, 0x3C4E, 0xFBA2, 0xC72D, 0x31A5], // x = 14
+ [0xDEBF, 0xE28E, 0x3FDA, 0xDB18, 0xE681, 0x3F67], // x = 15
+ [0xD79C, 0xF2FB, 0x38D3, 0xC54C, 0x1144, 0x24E8], // x = 16
+ [0xD13A, 0x045E, 0x2864, 0xC099, 0x3449, 0xF2FB], // x = 17
+ [0xCBB7, 0x156F, 0x1144, 0xCE5B, 0x3FDA, 0xC951], // x = 18
+ [0xC72D, 0x24E8, 0xF749, 0xEA91, 0x2EC6, 0xC260], // x = 19
+ [0xC3B2, 0x31A5, 0xDEBF, 0x0D05, 0x08B7, 0xE28E], // x = 20
+ [0xC157, 0x3AB4, 0xCBB7, 0x2BAF, 0xDEBF, 0x156F], // x = 21
+ [0xC026, 0x3F67, 0xC157, 0x3DA0, 0xC3B2, 0x3AB4], // x = 22
+
+ // w = 26
+ [0x3FE2, 0x3F89, 0x3EF4, 0x3E24, 0x3D1A, 0x3BD7], // x = 0
+ [0x3EF4, 0x3BD7, 0x36C5, 0x2FE8, 0x2778, 0x1DBE], // x = 1
+ [0x3D1A, 0x34AC, 0x2778, 0x16B2, 0x03DD, 0xF0AF], // x = 2
+ [0x3A5D, 0x2A71, 0x130A, 0xF849, 0xDEE4, 0xCB54], // x = 3
+ [0x36C5, 0x1DBE, 0xFC23, 0xDBA5, 0xC5A3, 0xC077], // x = 4
+ [0x3261, 0x0F51, 0xE5BC, 0xC755, 0xC10C, 0xD58F], // x = 5
+ [0x2D41, 0x0000, 0xD2BF, 0xC000, 0xD2BF, 0x0000], // x = 6
+ [0x2778, 0xF0AF, 0xC5A3, 0xC755, 0xF477, 0x2A71], // x = 7
+ [0x211C, 0xE242, 0xC01E, 0xDBA5, 0x1A44, 0x3F89], // x = 8
+ [0x1A44, 0xD58F, 0xC2E6, 0xF849, 0x36C5, 0x34AC], // x = 9
+ [0x130A, 0xCB54, 0xCD9F, 0x16B2, 0x3FE2, 0x0F51], // x = 10
+ [0x0B89, 0xC429, 0xDEE4, 0x2FE8, 0x3261, 0xE242], // x = 11
+ [0x03DD, 0xC077, 0xF477, 0x3E24, 0x130A, 0xC429], // x = 12
+ [0xFC23, 0xC077, 0x0B89, 0x3E24, 0xECF6, 0xC429], // x = 13
+ [0xF477, 0xC429, 0x211C, 0x2FE8, 0xCD9F, 0xE242], // x = 14
+ [0xECF6, 0xCB54, 0x3261, 0x16B2, 0xC01E, 0x0F51], // x = 15
+ [0xE5BC, 0xD58F, 0x3D1A, 0xF849, 0xC93B, 0x34AC], // x = 16
+ [0xDEE4, 0xE242, 0x3FE2, 0xDBA5, 0xE5BC, 0x3F89], // x = 17
+ [0xD888, 0xF0AF, 0x3A5D, 0xC755, 0x0B89, 0x2A71], // x = 18
+ [0xD2BF, 0x0000, 0x2D41, 0xC000, 0x2D41, 0x0000], // x = 19
+ [0xCD9F, 0x0F51, 0x1A44, 0xC755, 0x3EF4, 0xD58F], // x = 20
+ [0xC93B, 0x1DBE, 0x03DD, 0xDBA5, 0x3A5D, 0xC077], // x = 21
+ [0xC5A3, 0x2A71, 0xECF6, 0xF849, 0x211C, 0xCB54], // x = 22
+ [0xC2E6, 0x34AC, 0xD888, 0x16B2, 0xFC23, 0xF0AF], // x = 23
+ [0xC10C, 0x3BD7, 0xC93B, 0x2FE8, 0xD888, 0x1DBE], // x = 24
+ [0xC01E, 0x3F89, 0xC10C, 0x3E24, 0xC2E6, 0x3BD7], // x = 25
+
+ // w = 27
+ [0x3FE4, 0x3F91, 0x3F07, 0x3E46, 0x3D50, 0x3C24], // x = 0
+ [0x3F07, 0x3C24, 0x376D, 0x3107, 0x2923, 0x2000], // x = 1
+ [0x3D50, 0x3579, 0x2923, 0x1959, 0x076E, 0xF4E3], // x = 2
+ [0x3AC4, 0x2BEB, 0x15E4, 0xFC47, 0xE347, 0xCEF9], // x = 3
+ [0x376D, 0x2000, 0x0000, 0xE000, 0xC893, 0xC000], // x = 4
+ [0x3356, 0x125B, 0xEA1C, 0xCA87, 0xC01C, 0xCEF9], // x = 5
+ [0x2E8D, 0x03B9, 0xD6DD, 0xC06F, 0xCCAA, 0xF4E3], // x = 6
+ [0x2923, 0xF4E3, 0xC893, 0xC3DC, 0xEA1C, 0x2000], // x = 7
+ [0x232B, 0xE6A7, 0xC0F9, 0xD415, 0x0EC2, 0x3C24], // x = 8
+ [0x1CB9, 0xD9C8, 0xC0F9, 0xEDA5, 0x2E8D, 0x3C24], // x = 9
+ [0x15E4, 0xCEF9, 0xC893, 0x0B1D, 0x3F07, 0x2000], // x = 10
+ [0x0EC2, 0xC6CF, 0xD6DD, 0x2638, 0x3AC4, 0xF4E3], // x = 11
+ [0x076E, 0xC1BA, 0xEA1C, 0x3931, 0x232B, 0xCEF9], // x = 12
+ [0x0000, 0xC000, 0x0000, 0x4000, 0x0000, 0xC000], // x = 13
+ [0xF892, 0xC1BA, 0x15E4, 0x3931, 0xDCD5, 0xCEF9], // x = 14
+ [0xF13E, 0xC6CF, 0x2923, 0x2638, 0xC53C, 0xF4E3], // x = 15
+ [0xEA1C, 0xCEF9, 0x376D, 0x0B1D, 0xC0F9, 0x2000], // x = 16
+ [0xE347, 0xD9C8, 0x3F07, 0xEDA5, 0xD173, 0x3C24], // x = 17
+ [0xDCD5, 0xE6A7, 0x3F07, 0xD415, 0xF13E, 0x3C24], // x = 18
+ [0xD6DD, 0xF4E3, 0x376D, 0xC3DC, 0x15E4, 0x2000], // x = 19
+ [0xD173, 0x03B9, 0x2923, 0xC06F, 0x3356, 0xF4E3], // x = 20
+ [0xCCAA, 0x125B, 0x15E4, 0xCA87, 0x3FE4, 0xCEF9], // x = 21
+ [0xC893, 0x2000, 0x0000, 0xE000, 0x376D, 0xC000], // x = 22
+ [0xC53C, 0x2BEB, 0xEA1C, 0xFC47, 0x1CB9, 0xCEF9], // x = 23
+ [0xC2B0, 0x3579, 0xD6DD, 0x1959, 0xF892, 0xF4E3], // x = 24
+ [0xC0F9, 0x3C24, 0xC893, 0x3107, 0xD6DD, 0x2000], // x = 25
+ [0xC01C, 0x3F91, 0xC0F9, 0x3E46, 0xC2B0, 0x3C24], // x = 26
+
+ // w = 32
+ [0x3FEC, 0x3FB1, 0x3F4F, 0x3EC5, 0x3E15, 0x3D3F], // x = 0
+ [0x3F4F, 0x3D3F, 0x39DB, 0x3537, 0x2F6C, 0x289A], // x = 1
+ [0x3E15, 0x3871, 0x2F6C, 0x238E, 0x1590, 0x0646], // x = 2
+ [0x3C42, 0x3179, 0x20E7, 0x0C7C, 0xF69C, 0xE1D5], // x = 3
+ [0x39DB, 0x289A, 0x0F8D, 0xF384, 0xD9E0, 0xC78F], // x = 4
+ [0x36E5, 0x1E2B, 0xFCDC, 0xDC72, 0xC625, 0xC04F], // x = 5
+ [0x3368, 0x1294, 0xEA70, 0xCAC9, 0xC014, 0xCE87], // x = 6
+ [0x2F6C, 0x0646, 0xD9E0, 0xC13B, 0xC91B, 0xED6C], // x = 7
+ [0x2AFB, 0xF9BA, 0xCC98, 0xC13B, 0xDF19, 0x1294], // x = 8
+ [0x2620, 0xED6C, 0xC3BE, 0xCAC9, 0xFCDC, 0x3179], // x = 9
+ [0x20E7, 0xE1D5, 0xC014, 0xDC72, 0x1B5D, 0x3FB1], // x = 10
+ [0x1B5D, 0xD766, 0xC1EB, 0xF384, 0x3368, 0x3871], // x = 11
+ [0x1590, 0xCE87, 0xC91B, 0x0C7C, 0x3F4F, 0x1E2B], // x = 12
+ [0x0F8D, 0xC78F, 0xD505, 0x238E, 0x3C42, 0xF9BA], // x = 13
+ [0x0964, 0xC2C1, 0xE4A3, 0x3537, 0x2AFB, 0xD766], // x = 14
+ [0x0324, 0xC04F, 0xF69C, 0x3EC5, 0x0F8D, 0xC2C1], // x = 15
+ [0xFCDC, 0xC04F, 0x0964, 0x3EC5, 0xF073, 0xC2C1], // x = 16
+ [0xF69C, 0xC2C1, 0x1B5D, 0x3537, 0xD505, 0xD766], // x = 17
+ [0xF073, 0xC78F, 0x2AFB, 0x238E, 0xC3BE, 0xF9BA], // x = 18
+ [0xEA70, 0xCE87, 0x36E5, 0x0C7C, 0xC0B1, 0x1E2B], // x = 19
+ [0xE4A3, 0xD766, 0x3E15, 0xF384, 0xCC98, 0x3871], // x = 20
+ [0xDF19, 0xE1D5, 0x3FEC, 0xDC72, 0xE4A3, 0x3FB1], // x = 21
+ [0xD9E0, 0xED6C, 0x3C42, 0xCAC9, 0x0324, 0x3179], // x = 22
+ [0xD505, 0xF9BA, 0x3368, 0xC13B, 0x20E7, 0x1294], // x = 23
+ [0xD094, 0x0646, 0x2620, 0xC13B, 0x36E5, 0xED6C], // x = 24
+ [0xCC98, 0x1294, 0x1590, 0xCAC9, 0x3FEC, 0xCE87], // x = 25
+ [0xC91B, 0x1E2B, 0x0324, 0xDC72, 0x39DB, 0xC04F], // x = 26
+ [0xC625, 0x289A, 0xF073, 0xF384, 0x2620, 0xC78F], // x = 27
+ [0xC3BE, 0x3179, 0xDF19, 0x0C7C, 0x0964, 0xE1D5], // x = 28
+ [0xC1EB, 0x3871, 0xD094, 0x238E, 0xEA70, 0x0646], // x = 29
+ [0xC0B1, 0x3D3F, 0xC625, 0x3537, 0xD094, 0x289A], // x = 30
+ [0xC014, 0x3FB1, 0xC0B1, 0x3EC5, 0xC1EB, 0x3D3F], // x = 31
+]
diff --git a/test/c/std/thumbhash.c b/test/c/std/thumbhash.c
index af8f484..9715dfc 100644
--- a/test/c/std/thumbhash.c
+++ b/test/c/std/thumbhash.c
@@ -76,7 +76,7 @@
return do_test__wuffs_base__image_decoder(
wuffs_thumbhash__decoder__upcast_as__wuffs_base__image_decoder(&dec),
"test/data/artificial-thumbhash/3OcRJYB4d3h_iIeHeEh3eIhw-j3A.th", 0,
- SIZE_MAX, 32, 32, 0xFF00F8F8);
+ SIZE_MAX, 32, 23, 0xFF56632E);
}
const char* //
diff --git a/test/nia-checksums-of-data.txt b/test/nia-checksums-of-data.txt
index 88fc72d..ac32f86 100644
--- a/test/nia-checksums-of-data.txt
+++ b/test/nia-checksums-of-data.txt
@@ -26,15 +26,15 @@
OK. 0564b364 test/data/artificial-png/apng-skip-idat.png
OK. e08a7cc8 test/data/artificial-png/exif.png
OK. e08a7cc8 test/data/artificial-png/key-value-pairs.png
-OK. 0508510a test/data/artificial-thumbhash/1QcSHQRnh493V4dIh4eXh1h4kJUI.th
-OK. 0508510a test/data/artificial-thumbhash/2IqDBQQnxnj0JoLYdM3f8ahpuDeHiHdwZw.th
-OK. 0508510a test/data/artificial-thumbhash/2fcZFIB3iId_h3iJh4aIYJ2V8g.th
-OK. 0508510a test/data/artificial-thumbhash/3OcRJYB4d3h_iIeHeEh3eIhw-j3A.th
-OK. 0508510a test/data/artificial-thumbhash/3PcNNYSFeXh_d3eld0iHZoZgVwh2.th
-OK. 0508510a test/data/artificial-thumbhash/HBkSHYSIeHiPiHh8eJd4eTN0EEQG.th
-OK. 0508510a test/data/artificial-thumbhash/IQgSLYZ6iHePh4h1eFeHh4dwgwg3.th
-OK. 0508510a test/data/artificial-thumbhash/VggKDYAW6lZvdYd6d2iZh_p4GE_k.th
-OK. 0508510a test/data/artificial-thumbhash/YJqGPQw7sFlslqhFafSE-Q6oJ1h2iHB2Rw.th
+OK. 2b606fe3 test/data/artificial-thumbhash/1QcSHQRnh493V4dIh4eXh1h4kJUI.th
+OK. 70203d96 test/data/artificial-thumbhash/2IqDBQQnxnj0JoLYdM3f8ahpuDeHiHdwZw.th
+OK. 025631eb test/data/artificial-thumbhash/2fcZFIB3iId_h3iJh4aIYJ2V8g.th
+OK. 4ab0d622 test/data/artificial-thumbhash/3OcRJYB4d3h_iIeHeEh3eIhw-j3A.th
+OK. 4ba04a51 test/data/artificial-thumbhash/3PcNNYSFeXh_d3eld0iHZoZgVwh2.th
+OK. 5b7fd9b7 test/data/artificial-thumbhash/HBkSHYSIeHiPiHh8eJd4eTN0EEQG.th
+OK. 69ad2c3d test/data/artificial-thumbhash/IQgSLYZ6iHePh4h1eFeHh4dwgwg3.th
+OK. 27a6e456 test/data/artificial-thumbhash/VggKDYAW6lZvdYd6d2iZh_p4GE_k.th
+OK. 4965134c test/data/artificial-thumbhash/YJqGPQw7sFlslqhFafSE-Q6oJ1h2iHB2Rw.th
OK. 076cb375 test/data/bricks-color.bmp
OK. bdbbfadb test/data/bricks-color.etc1.pkm
OK. 41e6110e test/data/bricks-color.etc1s.pkm