Prepare to decode BMP embedding a JPEG or PNG
diff --git a/example/imageviewer/imageviewer.c b/example/imageviewer/imageviewer.c
index d117cdc..41625b4 100644
--- a/example/imageviewer/imageviewer.c
+++ b/example/imageviewer/imageviewer.c
@@ -179,6 +179,7 @@
if (status.repr == NULL) {
break;
} else if (status.repr != wuffs_base__suspension__short_read) {
+ // TODO: handle wuffs_base__note__i_o_redirect.
printf("%s: %s\n", g_filename, wuffs_base__status__message(&status));
return false;
}
diff --git a/internal/cgen/base/strconv-impl.c b/internal/cgen/base/strconv-impl.c
index a6013ba..640fbc0 100644
--- a/internal/cgen/base/strconv-impl.c
+++ b/internal/cgen/base/strconv-impl.c
@@ -221,7 +221,7 @@
v &= 0x0F;
// UINT64_MAX is 18446744073709551615, which is ((10 * max10) + max1).
- const uint64_t max10 = 1844674407370955161;
+ const uint64_t max10 = 1844674407370955161u;
const uint8_t max1 = 5;
for (; p < q; p++) {
diff --git a/internal/cgen/cgen.go b/internal/cgen/cgen.go
index bf9fb32..d2f867b 100644
--- a/internal/cgen/cgen.go
+++ b/internal/cgen/cgen.go
@@ -44,6 +44,8 @@
mibi = big.NewInt(1 << 20)
+ maxInt64 = big.NewInt((1 << 63) - 1)
+
typeExprUtility = a.NewTypeExpr(0, t.IDBase, t.IDUtility, nil, nil, nil)
)
diff --git a/internal/cgen/data.go b/internal/cgen/data.go
index 6be82bf..abdef5f 100644
--- a/internal/cgen/data.go
+++ b/internal/cgen/data.go
@@ -37,9 +37,9 @@
"" +
"// --------\n\nWUFFS_BASE__MAYBE_STATIC wuffs_base__result_i64 //\nwuffs_base__parse_number_i64(wuffs_base__slice_u8 s) {\n uint8_t* p = s.ptr;\n uint8_t* q = s.ptr + s.len;\n\n for (; (p < q) && (*p == '_'); p++) {\n }\n\n bool negative = false;\n if (p >= q) {\n goto fail_bad_argument;\n } else if (*p == '-') {\n p++;\n negative = true;\n } else if (*p == '+') {\n p++;\n }\n\n do {\n wuffs_base__result_u64 r = wuffs_base__parse_number_u64(\n wuffs_base__make_slice_u8(p, (size_t)(q - p)));\n if (r.status.repr != NULL) {\n wuffs_base__result_i64 ret;\n ret.status.repr = r.status.repr;\n ret.value = 0;\n return ret;\n } else if (negative) {\n if (r.value > 0x8000000000000000) {\n goto fail_out_of_bounds;\n }\n wuffs_base__result_i64 ret;\n ret.status.repr = NULL;\n ret.value = -(int64_t)(r.value);\n return ret;\n } else if (r.value > 0x7FFFFFFFFFFFFFFF) {\n goto fail_out_of_bounds;\n } else {\n wuffs_base__result_i64 ret;\n ret.status" +
".repr = NULL;\n ret.value = +(int64_t)(r.value);\n return ret;\n }\n } while (0);\n\nfail_bad_argument:\n do {\n wuffs_base__result_i64 ret;\n ret.status.repr = wuffs_base__error__bad_argument;\n ret.value = 0;\n return ret;\n } while (0);\n\nfail_out_of_bounds:\n do {\n wuffs_base__result_i64 ret;\n ret.status.repr = wuffs_base__error__out_of_bounds;\n ret.value = 0;\n return ret;\n } while (0);\n}\n\nWUFFS_BASE__MAYBE_STATIC wuffs_base__result_u64 //\nwuffs_base__parse_number_u64(wuffs_base__slice_u8 s) {\n uint8_t* p = s.ptr;\n uint8_t* q = s.ptr + s.len;\n\n for (; (p < q) && (*p == '_'); p++) {\n }\n\n if (p >= q) {\n goto fail_bad_argument;\n\n } else if (*p == '0') {\n p++;\n if (p >= q) {\n goto ok_zero;\n }\n if (*p == '_') {\n p++;\n for (; p < q; p++) {\n if (*p != '_') {\n goto fail_bad_argument;\n }\n }\n goto ok_zero;\n }\n\n if ((*p == 'x') || (*p == 'X')) {\n p++;\n for (; (p < q) && (*p == '_'); p++) {\n }\n " +
- "if (p < q) {\n goto hexadecimal;\n }\n\n } else if ((*p == 'd') || (*p == 'D')) {\n p++;\n for (; (p < q) && (*p == '_'); p++) {\n }\n if (p < q) {\n goto decimal;\n }\n }\n\n goto fail_bad_argument;\n }\n\ndecimal:\n do {\n uint64_t v = wuffs_base__parse_number__decimal_digits[*p++];\n if (v == 0) {\n goto fail_bad_argument;\n }\n v &= 0x0F;\n\n // UINT64_MAX is 18446744073709551615, which is ((10 * max10) + max1).\n const uint64_t max10 = 1844674407370955161;\n const uint8_t max1 = 5;\n\n for (; p < q; p++) {\n if (*p == '_') {\n continue;\n }\n uint8_t digit = wuffs_base__parse_number__decimal_digits[*p];\n if (digit == 0) {\n goto fail_bad_argument;\n }\n digit &= 0x0F;\n if ((v > max10) || ((v == max10) && (digit > max1))) {\n goto fail_out_of_bounds;\n }\n v = (10 * v) + ((uint64_t)(digit));\n }\n\n wuffs_base__result_u64 ret;\n ret.status.repr = NULL;\n ret.value = v;\n return ret;\n }" +
- " while (0);\n\nhexadecimal:\n do {\n uint64_t v = wuffs_base__parse_number__hexadecimal_digits[*p++];\n if (v == 0) {\n goto fail_bad_argument;\n }\n v &= 0x0F;\n\n for (; p < q; p++) {\n if (*p == '_') {\n continue;\n }\n uint8_t digit = wuffs_base__parse_number__hexadecimal_digits[*p];\n if (digit == 0) {\n goto fail_bad_argument;\n }\n digit &= 0x0F;\n if ((v >> 60) != 0) {\n goto fail_out_of_bounds;\n }\n v = (v << 4) | ((uint64_t)(digit));\n }\n\n wuffs_base__result_u64 ret;\n ret.status.repr = NULL;\n ret.value = v;\n return ret;\n } while (0);\n\nok_zero:\n do {\n wuffs_base__result_u64 ret;\n ret.status.repr = NULL;\n ret.value = 0;\n return ret;\n } while (0);\n\nfail_bad_argument:\n do {\n wuffs_base__result_u64 ret;\n ret.status.repr = wuffs_base__error__bad_argument;\n ret.value = 0;\n return ret;\n } while (0);\n\nfail_out_of_bounds:\n do {\n wuffs_base__result_u64 ret;\n ret.status.repr = wuffs_base__error__o" +
- "ut_of_bounds;\n ret.value = 0;\n return ret;\n } while (0);\n}\n\n" +
+ "if (p < q) {\n goto hexadecimal;\n }\n\n } else if ((*p == 'd') || (*p == 'D')) {\n p++;\n for (; (p < q) && (*p == '_'); p++) {\n }\n if (p < q) {\n goto decimal;\n }\n }\n\n goto fail_bad_argument;\n }\n\ndecimal:\n do {\n uint64_t v = wuffs_base__parse_number__decimal_digits[*p++];\n if (v == 0) {\n goto fail_bad_argument;\n }\n v &= 0x0F;\n\n // UINT64_MAX is 18446744073709551615, which is ((10 * max10) + max1).\n const uint64_t max10 = 1844674407370955161u;\n const uint8_t max1 = 5;\n\n for (; p < q; p++) {\n if (*p == '_') {\n continue;\n }\n uint8_t digit = wuffs_base__parse_number__decimal_digits[*p];\n if (digit == 0) {\n goto fail_bad_argument;\n }\n digit &= 0x0F;\n if ((v > max10) || ((v == max10) && (digit > max1))) {\n goto fail_out_of_bounds;\n }\n v = (10 * v) + ((uint64_t)(digit));\n }\n\n wuffs_base__result_u64 ret;\n ret.status.repr = NULL;\n ret.value = v;\n return ret;\n " +
+ "} while (0);\n\nhexadecimal:\n do {\n uint64_t v = wuffs_base__parse_number__hexadecimal_digits[*p++];\n if (v == 0) {\n goto fail_bad_argument;\n }\n v &= 0x0F;\n\n for (; p < q; p++) {\n if (*p == '_') {\n continue;\n }\n uint8_t digit = wuffs_base__parse_number__hexadecimal_digits[*p];\n if (digit == 0) {\n goto fail_bad_argument;\n }\n digit &= 0x0F;\n if ((v >> 60) != 0) {\n goto fail_out_of_bounds;\n }\n v = (v << 4) | ((uint64_t)(digit));\n }\n\n wuffs_base__result_u64 ret;\n ret.status.repr = NULL;\n ret.value = v;\n return ret;\n } while (0);\n\nok_zero:\n do {\n wuffs_base__result_u64 ret;\n ret.status.repr = NULL;\n ret.value = 0;\n return ret;\n } while (0);\n\nfail_bad_argument:\n do {\n wuffs_base__result_u64 ret;\n ret.status.repr = wuffs_base__error__bad_argument;\n ret.value = 0;\n return ret;\n } while (0);\n\nfail_out_of_bounds:\n do {\n wuffs_base__result_u64 ret;\n ret.status.repr = wuffs_base__error__" +
+ "out_of_bounds;\n ret.value = 0;\n return ret;\n } while (0);\n}\n\n" +
"" +
"// ---------------- Hexadecimal\n\nWUFFS_BASE__MAYBE_STATIC size_t //\nwuffs_base__hexadecimal__decode2(wuffs_base__slice_u8 dst,\n wuffs_base__slice_u8 src) {\n size_t src_len2 = src.len / 2;\n size_t len = dst.len < src_len2 ? dst.len : src_len2;\n uint8_t* d = dst.ptr;\n uint8_t* s = src.ptr;\n size_t n = len;\n\n while (n--) {\n *d = (uint8_t)((wuffs_base__parse_number__hexadecimal_digits[s[0]] << 4) |\n (wuffs_base__parse_number__hexadecimal_digits[s[1]] & 0x0F));\n d += 1;\n s += 2;\n }\n\n return len;\n}\n\nWUFFS_BASE__MAYBE_STATIC size_t //\nwuffs_base__hexadecimal__decode4(wuffs_base__slice_u8 dst,\n wuffs_base__slice_u8 src) {\n size_t src_len4 = src.len / 4;\n size_t len = dst.len < src_len4 ? dst.len : src_len4;\n uint8_t* d = dst.ptr;\n uint8_t* s = src.ptr;\n size_t n = len;\n\n while (n--) {\n *d = (uint8_t)((wuffs_base__parse_number__hexadecimal_digits[s[2]] << 4) |\n (wuffs_base__parse_number__hexa" +
"decimal_digits[s[3]] & 0x0F));\n d += 1;\n s += 4;\n }\n\n return len;\n}\n\n" +
diff --git a/internal/cgen/expr.go b/internal/cgen/expr.go
index 7204fc2..300e726 100644
--- a/internal/cgen/expr.go
+++ b/internal/cgen/expr.go
@@ -30,6 +30,9 @@
if cv := n.ConstValue(); cv != nil {
if typ := n.MType(); typ.IsNumTypeOrIdeal() {
b.writes(cv.String())
+ if cv.Cmp(maxInt64) > 0 {
+ b.writeb('u')
+ }
} else if typ.IsNullptr() {
b.writes("NULL")
} else if typ.IsStatus() {
diff --git a/lang/builtin/builtin.go b/lang/builtin/builtin.go
index cab8fd8..b8edef9 100644
--- a/lang/builtin/builtin.go
+++ b/lang/builtin/builtin.go
@@ -27,11 +27,14 @@
var FourCCs = [...][2]string{
{"ICCP", "International Color Consortium Profile"},
+ {"JPEG", "Joint Photographic Experts Group"},
+ {"PNG ", "Portable Network Graphics"},
{"XMP ", "Extensible Metadata Platform"},
}
var Statuses = [...]string{
// Notes.
+ `"@I/O redirect"`,
`"@end of data"`,
`"@metadata reported"`,
diff --git a/release/c/wuffs-unsupported-snapshot.c b/release/c/wuffs-unsupported-snapshot.c
index fecdc96..8ec9186 100644
--- a/release/c/wuffs-unsupported-snapshot.c
+++ b/release/c/wuffs-unsupported-snapshot.c
@@ -189,6 +189,7 @@
} wuffs_base__status;
+extern const char* wuffs_base__note__i_o_redirect;
extern const char* wuffs_base__note__end_of_data;
extern const char* wuffs_base__note__metadata_reported;
extern const char* wuffs_base__suspension__even_more_information;
@@ -318,6 +319,12 @@
// International Color Consortium Profile.
#define WUFFS_BASE__FOURCC__ICCP 0x49434350
+// Joint Photographic Experts Group.
+#define WUFFS_BASE__FOURCC__JPEG 0x4A504547
+
+// Portable Network Graphics.
+#define WUFFS_BASE__FOURCC__PNG 0x504E4720
+
// Extensible Metadata Platform.
#define WUFFS_BASE__FOURCC__XMP 0x584D5020
@@ -4832,6 +4839,8 @@
uint64_t f_bytes_per_row;
uint64_t f_bytes_total;
wuffs_base__pixel_format f_pixfmt;
+ uint32_t f_io_redirect_fourcc;
+ uint64_t f_io_redirect_pos;
uint32_t f_padding;
uint32_t f_mask_r;
uint32_t f_mask_g;
@@ -4856,6 +4865,7 @@
struct {
struct {
uint32_t v_bitmap_info_len;
+ uint32_t v_bits_per_pixel;
uint32_t v_compression;
uint64_t scratch;
} s_decode_image_config[1];
@@ -7922,6 +7932,7 @@
0x08, 0x0A, 0x0C, 0x10, 0x18, 0x20, 0x30, 0x40,
};
+const char* wuffs_base__note__i_o_redirect = "@base: I/O redirect";
const char* wuffs_base__note__end_of_data = "@base: end of data";
const char* wuffs_base__note__metadata_reported = "@base: metadata reported";
const char* wuffs_base__suspension__even_more_information =
@@ -8183,7 +8194,7 @@
v &= 0x0F;
// UINT64_MAX is 18446744073709551615, which is ((10 * max10) + max1).
- const uint64_t max10 = 1844674407370955161;
+ const uint64_t max10 = 1844674407370955161u;
const uint8_t max1 = 5;
for (; p < q; p++) {
@@ -12127,14 +12138,20 @@
if (coro_susp_point) {
v_bitmap_info_len =
self->private_data.s_decode_image_config[0].v_bitmap_info_len;
+ v_bits_per_pixel =
+ self->private_data.s_decode_image_config[0].v_bits_per_pixel;
v_compression = self->private_data.s_decode_image_config[0].v_compression;
}
switch (coro_susp_point) {
WUFFS_BASE__COROUTINE_SUSPENSION_POINT_0;
- if (self->private_impl.f_call_sequence != 0) {
+ if ((self->private_impl.f_call_sequence != 0) ||
+ (self->private_impl.f_io_redirect_fourcc == 1)) {
status = wuffs_base__make_status(wuffs_base__error__bad_call_sequence);
goto exit;
+ } else if (self->private_impl.f_io_redirect_fourcc != 0) {
+ status = wuffs_base__make_status(wuffs_base__note__i_o_redirect);
+ goto ok;
}
{
WUFFS_BASE__COROUTINE_SUSPENSION_POINT(1);
@@ -12218,6 +12235,10 @@
goto exit;
}
self->private_impl.f_padding -= 14;
+ self->private_impl.f_io_redirect_pos = wuffs_base__u64__sat_add(
+ ((uint64_t)(self->private_impl.f_padding)),
+ wuffs_base__u64__sat_add(a_src->meta.pos,
+ ((uint64_t)(iop_a_src - io0_a_src))));
{
WUFFS_BASE__COROUTINE_SUSPENSION_POINT(6);
uint32_t t_2;
@@ -12401,27 +12422,6 @@
}
v_bits_per_pixel = t_6;
}
- if (v_bits_per_pixel == 24) {
- self->private_impl.f_bits_per_pixel = 24;
- self->private_impl.f_bytes_per_row =
- ((((((uint64_t)(self->private_impl.f_width)) * 3) + 3) >> 2) << 2);
- self->private_impl.f_pad_per_row = (self->private_impl.f_width & 3);
- self->private_impl.f_pixfmt =
- wuffs_base__utility__make_pixel_format(2147485832);
- } else if (v_bits_per_pixel == 32) {
- self->private_impl.f_bits_per_pixel = 32;
- self->private_impl.f_bytes_per_row =
- (((uint64_t)(self->private_impl.f_width)) * 4);
- self->private_impl.f_pad_per_row = 0;
- self->private_impl.f_pixfmt =
- wuffs_base__utility__make_pixel_format(2164295816);
- } else {
- status = wuffs_base__make_status(wuffs_bmp__error__unsupported_bmp_file);
- goto exit;
- }
- self->private_impl.f_bytes_total =
- (self->private_impl.f_bytes_per_row *
- ((uint64_t)(self->private_impl.f_height)));
{
WUFFS_BASE__COROUTINE_SUSPENSION_POINT(16);
uint32_t t_7;
@@ -12453,6 +12453,39 @@
}
v_compression = t_7;
}
+ if (v_bits_per_pixel == 0) {
+ if (v_compression == 4) {
+ self->private_impl.f_io_redirect_fourcc = 1246774599;
+ status = wuffs_base__make_status(wuffs_base__note__i_o_redirect);
+ goto ok;
+ } else if (v_compression == 5) {
+ self->private_impl.f_io_redirect_fourcc = 1347307296;
+ status = wuffs_base__make_status(wuffs_base__note__i_o_redirect);
+ goto ok;
+ }
+ status = wuffs_base__make_status(wuffs_bmp__error__unsupported_bmp_file);
+ goto exit;
+ } else if (v_bits_per_pixel == 24) {
+ self->private_impl.f_bits_per_pixel = 24;
+ self->private_impl.f_bytes_per_row =
+ ((((((uint64_t)(self->private_impl.f_width)) * 3) + 3) >> 2) << 2);
+ self->private_impl.f_pad_per_row = (self->private_impl.f_width & 3);
+ self->private_impl.f_pixfmt =
+ wuffs_base__utility__make_pixel_format(2147485832);
+ } else if (v_bits_per_pixel == 32) {
+ self->private_impl.f_bits_per_pixel = 32;
+ self->private_impl.f_bytes_per_row =
+ (((uint64_t)(self->private_impl.f_width)) * 4);
+ self->private_impl.f_pad_per_row = 0;
+ self->private_impl.f_pixfmt =
+ wuffs_base__utility__make_pixel_format(2164295816);
+ } else {
+ status = wuffs_base__make_status(wuffs_bmp__error__unsupported_bmp_file);
+ goto exit;
+ }
+ self->private_impl.f_bytes_total =
+ (self->private_impl.f_bytes_per_row *
+ ((uint64_t)(self->private_impl.f_height)));
self->private_data.s_decode_image_config[0].scratch = 20;
WUFFS_BASE__COROUTINE_SUSPENSION_POINT(18);
if (self->private_data.s_decode_image_config[0].scratch >
@@ -12636,6 +12669,8 @@
wuffs_base__status__is_suspension(&status) ? 1 : 0;
self->private_data.s_decode_image_config[0].v_bitmap_info_len =
v_bitmap_info_len;
+ self->private_data.s_decode_image_config[0].v_bits_per_pixel =
+ v_bits_per_pixel;
self->private_data.s_decode_image_config[0].v_compression = v_compression;
goto exit;
@@ -13268,8 +13303,17 @@
self->private_impl.active_coroutine = 0;
wuffs_base__status status = wuffs_base__make_status(NULL);
- status = wuffs_base__make_status(wuffs_base__error__no_more_information);
- goto exit;
+ if (self->private_impl.f_io_redirect_fourcc <= 1) {
+ status = wuffs_base__make_status(wuffs_base__error__no_more_information);
+ goto exit;
+ }
+ if (a_minfo != NULL) {
+ wuffs_base__more_information__set(
+ a_minfo, 1, self->private_impl.f_io_redirect_fourcc, 0,
+ self->private_impl.f_io_redirect_pos, 18446744073709551615u);
+ }
+ self->private_impl.f_io_redirect_fourcc = 1;
+
goto ok;
ok:
goto exit;
diff --git a/std/bmp/decode_bmp.wuffs b/std/bmp/decode_bmp.wuffs
index 5af01c6..61b5852 100644
--- a/std/bmp/decode_bmp.wuffs
+++ b/std/bmp/decode_bmp.wuffs
@@ -32,6 +32,9 @@
bytes_total : base.u64[..= 0xFFFF_FFFC_0000_0004], // 4 * 0x7FFF_FFFF * 0x7FFF_FFFF
pixfmt : base.pixel_format,
+ io_redirect_fourcc : base.u32,
+ io_redirect_pos : base.u64,
+
padding : base.u32,
mask_r : base.u32,
@@ -66,8 +69,10 @@
var bits_per_pixel : base.u32
var compression : base.u32
- if this.call_sequence <> 0 {
+ if (this.call_sequence <> 0) or (this.io_redirect_fourcc == 1) {
return base."#bad call sequence"
+ } else if this.io_redirect_fourcc <> 0 {
+ return base."@I/O redirect"
}
// Read the BITMAPFILEHEADER (14 bytes).
@@ -84,6 +89,7 @@
return "#bad header"
}
this.padding -= 14
+ this.io_redirect_pos = (this.padding as base.u64) ~sat+ args.src.position()
// Read the BITMAPINFOHEADER (version 3 / 4 / 5 is 40 / 108 / 124 bytes).
@@ -120,7 +126,17 @@
}
bits_per_pixel = args.src.read_u16le_as_u32?()
- if bits_per_pixel == 24 {
+ compression = args.src.read_u32le?()
+ if bits_per_pixel == 0 {
+ if compression == 4 {
+ this.io_redirect_fourcc = 'JPEG'be
+ return base."@I/O redirect"
+ } else if compression == 5 {
+ this.io_redirect_fourcc = 'PNG 'be
+ return base."@I/O redirect"
+ }
+ return "#unsupported BMP file"
+ } else if bits_per_pixel == 24 {
this.bits_per_pixel = 24
// 3 bytes per pixel, but row lengths are rounded up to multiples of 4.
// The "((x + 3) >> 2) << 2" dance rounds x up.
@@ -142,8 +158,6 @@
}
this.bytes_total = this.bytes_per_row * (this.height as base.u64)
- compression = args.src.read_u32le?()
-
// We've already read 20 bytes from the BITMAPINFOHEADER: size (4), width
// (4), height (4), planes (2), bpp (2), compression (4). Skip the rest of
// the version 3 BITMAPINFOHEADER (whose total size is 40).
@@ -454,7 +468,20 @@
}
pub func decoder.tell_me_more?(dst: base.io_writer, minfo: nptr base.more_information, src: base.io_reader) {
- return base."#no more information"
+ if this.io_redirect_fourcc <= 1 {
+ return base."#no more information"
+ }
+ if args.minfo <> nullptr {
+ args.minfo.set!(
+ flavor: 1, // WUFFS_BASE__MORE_INFORMATION__FLAVOR__IO_REDIRECT
+ w: this.io_redirect_fourcc,
+ x: 0,
+ y: this.io_redirect_pos,
+ z: 0xFFFF_FFFF_FFFF_FFFF)
+ }
+ // Setting io_redirect_fourcc to a dummy value of 1 will cause future calls
+ // to return an error.
+ this.io_redirect_fourcc = 1
}
pub func decoder.workbuf_len() base.range_ii_u64 {
diff --git a/test/c/std/bmp.c b/test/c/std/bmp.c
index bb6a6e9..1e20e2f 100644
--- a/test/c/std/bmp.c
+++ b/test/c/std/bmp.c
@@ -112,6 +112,58 @@
return NULL;
}
+const char* //
+test_wuffs_bmp_decode_io_redirect() {
+ CHECK_FOCUS(__func__);
+ wuffs_bmp__decoder dec;
+ CHECK_STATUS("initialize",
+ wuffs_bmp__decoder__initialize(
+ &dec, sizeof dec, WUFFS_VERSION,
+ WUFFS_INITIALIZE__LEAVE_INTERNAL_BUFFERS_UNINITIALIZED));
+
+ wuffs_base__io_buffer src = ((wuffs_base__io_buffer){
+ .data = g_src_slice_u8,
+ });
+ CHECK_STRING(read_file(&src, "test/data/rgb24png.bmp"));
+ if (src.meta.wi != 1210) {
+ RETURN_FAIL("file size: have %zu, want 1210", src.meta.wi);
+ }
+
+ wuffs_base__status status =
+ wuffs_bmp__decoder__decode_image_config(&dec, NULL, &src);
+ if (status.repr != wuffs_base__note__i_o_redirect) {
+ RETURN_FAIL("decode_image_config: have \"%s\", want \"%s\"", status.repr,
+ wuffs_base__note__i_o_redirect);
+ }
+
+ wuffs_base__io_buffer empty = wuffs_base__empty_io_buffer();
+ wuffs_base__more_information minfo = wuffs_base__empty_more_information();
+ CHECK_STATUS("tell_me_more",
+ wuffs_bmp__decoder__tell_me_more(&dec, &empty, &minfo, &src));
+ if (minfo.flavor != WUFFS_BASE__MORE_INFORMATION__FLAVOR__IO_REDIRECT) {
+ RETURN_FAIL("flavor: have %" PRIu32 ", want %" PRIu32, minfo.flavor,
+ WUFFS_BASE__MORE_INFORMATION__FLAVOR__IO_REDIRECT);
+ }
+
+ uint32_t have_fourcc =
+ wuffs_base__more_information__io_redirect__fourcc(&minfo);
+ if (have_fourcc != WUFFS_BASE__FOURCC__PNG) {
+ RETURN_FAIL("fourcc: have 0x%08" PRIX32 ", want 0x%08" PRIX32, have_fourcc,
+ WUFFS_BASE__FOURCC__PNG);
+ }
+
+ wuffs_base__range_ie_u64 have_range =
+ wuffs_base__more_information__io_redirect__range(&minfo);
+ if (have_range.min_incl != 138) {
+ RETURN_FAIL("range.min_incl: have %" PRIu64 ", want 138",
+ have_range.min_incl);
+ } else if (have_range.max_excl < 1210) {
+ RETURN_FAIL("range.max_excl: have %" PRIu64 ", want >= 1210",
+ have_range.max_excl);
+ }
+ return NULL;
+}
+
// ---------------- Mimic Tests
#ifdef WUFFS_MIMIC
@@ -138,6 +190,7 @@
test_wuffs_bmp_decode_frame_config,
test_wuffs_bmp_decode_interface,
+ test_wuffs_bmp_decode_io_redirect,
#ifdef WUFFS_MIMIC
diff --git a/test/data/README.md b/test/data/README.md
index 156c9d7..94dc070 100644
--- a/test/data/README.md
+++ b/test/data/README.md
@@ -101,6 +101,9 @@
6901 "JavaScript Object Notation (JSON)
Pointer"](https://tools.ietf.org/rfc/rfc6901.txt) specification.
+`rgb24png.bmp` comes from [BMP Suite](https://github.com/jsummers/bmpsuite),
+which states that its generated images "are in the public domain".
+
`romeo.txt` is an excerpt of Shakespeare's "Romeo and Juliet", copied from
[shakespeare.mit.edu](http://shakespeare.mit.edu/romeo_juliet/romeo_juliet.2.2.html).
diff --git a/test/data/rgb24png.bmp b/test/data/rgb24png.bmp
new file mode 100644
index 0000000..e87ec7a
--- /dev/null
+++ b/test/data/rgb24png.bmp
Binary files differ