Add std/png filter_and_swizzle
diff --git a/release/c/wuffs-unsupported-snapshot.c b/release/c/wuffs-unsupported-snapshot.c
index a198a72..8481c4a 100644
--- a/release/c/wuffs-unsupported-snapshot.c
+++ b/release/c/wuffs-unsupported-snapshot.c
@@ -8359,6 +8359,8 @@
struct {
wuffs_crc32__ieee_hasher f_crc;
wuffs_zlib__decoder f_zlib;
+ uint8_t f_dst_palette[1024];
+ uint8_t f_src_palette[1024];
struct {
uint32_t v_dst_pixfmt;
@@ -29798,6 +29800,7 @@
const char wuffs_png__error__bad_chunk[] = "#png: bad chunk";
const char wuffs_png__error__bad_header[] = "#png: bad header";
const char wuffs_png__error__unsupported_png_file[] = "#png: unsupported PNG file";
+const char wuffs_png__error__internal_error_inconsistent_workbuf_length[] = "#png: internal error: inconsistent workbuf length";
const char wuffs_png__error__internal_error_zlib_decoder_did_not_exhaust_its_input[] = "#png: internal error: zlib decoder did not exhaust its input";
// ---------------- Private Consts
@@ -29806,6 +29809,12 @@
// ---------------- Private Function Prototypes
+static wuffs_base__status
+wuffs_png__decoder__filter_and_swizzle(
+ wuffs_png__decoder* self,
+ wuffs_base__pixel_buffer* a_dst,
+ wuffs_base__slice_u8 a_workbuf);
+
// ---------------- VTables
const wuffs_base__image_decoder__func_ptrs
@@ -30497,6 +30506,7 @@
uint8_t* io2_v_w WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
uint64_t v_w_mark = 0;
uint64_t v_r_mark = 0;
+ wuffs_base__status v_swizzler_status = wuffs_base__make_status(NULL);
wuffs_base__status v_zlib_status = wuffs_base__make_status(NULL);
const uint8_t* iop_a_src = NULL;
@@ -30686,6 +30696,33 @@
status = wuffs_base__make_status(wuffs_base__error__not_enough_data);
goto exit;
}
+ v_swizzler_status = wuffs_base__pixel_swizzler__prepare(&self->private_impl.f_swizzler,
+ wuffs_base__pixel_buffer__pixel_format(a_dst),
+ wuffs_base__pixel_buffer__palette_or_else(a_dst, wuffs_base__make_slice_u8(self->private_data.f_dst_palette, 1024)),
+ wuffs_base__utility__make_pixel_format(self->private_impl.f_src_pixfmt),
+ wuffs_base__make_slice_u8(self->private_data.f_src_palette, 1024),
+ a_blend);
+ if ( ! wuffs_base__status__is_ok(&v_swizzler_status)) {
+ status = v_swizzler_status;
+ if (wuffs_base__status__is_error(&status)) {
+ goto exit;
+ } else if (wuffs_base__status__is_suspension(&status)) {
+ status = wuffs_base__make_status(wuffs_base__error__cannot_return_a_suspension);
+ goto exit;
+ }
+ goto ok;
+ }
+ v_swizzler_status = wuffs_png__decoder__filter_and_swizzle(self, a_dst, a_workbuf);
+ if ( ! wuffs_base__status__is_ok(&v_swizzler_status)) {
+ status = v_swizzler_status;
+ if (wuffs_base__status__is_error(&status)) {
+ goto exit;
+ } else if (wuffs_base__status__is_suspension(&status)) {
+ status = wuffs_base__make_status(wuffs_base__error__cannot_return_a_suspension);
+ goto exit;
+ }
+ goto ok;
+ }
self->private_impl.f_call_sequence = 255;
goto ok;
@@ -30711,6 +30748,59 @@
return status;
}
+// -------- func png.decoder.filter_and_swizzle
+
+static wuffs_base__status
+wuffs_png__decoder__filter_and_swizzle(
+ wuffs_png__decoder* self,
+ wuffs_base__pixel_buffer* a_dst,
+ wuffs_base__slice_u8 a_workbuf) {
+ wuffs_base__pixel_format v_dst_pixfmt = {0};
+ uint32_t v_dst_bits_per_pixel = 0;
+ uint64_t v_dst_bytes_per_pixel = 0;
+ uint64_t v_dst_bytes_per_row = 0;
+ wuffs_base__slice_u8 v_dst_palette = {0};
+ wuffs_base__table_u8 v_tab = {0};
+ uint32_t v_y = 0;
+ wuffs_base__slice_u8 v_dst = {0};
+ uint8_t v_filter = 0;
+ wuffs_base__slice_u8 v_curr_row = {0};
+ wuffs_base__slice_u8 v_prev_row = {0};
+
+ 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 & 7) != 0) {
+ return wuffs_base__make_status(wuffs_base__error__unsupported_option);
+ }
+ v_dst_bytes_per_pixel = ((uint64_t)((v_dst_bits_per_pixel / 8)));
+ v_dst_bytes_per_row = (((uint64_t)(self->private_impl.f_width)) * v_dst_bytes_per_pixel);
+ v_dst_palette = wuffs_base__pixel_buffer__palette_or_else(a_dst, wuffs_base__make_slice_u8(self->private_data.f_dst_palette, 1024));
+ v_tab = wuffs_base__pixel_buffer__plane(a_dst, 0);
+ while (v_y < self->private_impl.f_height) {
+ v_dst = wuffs_base__table_u8__row(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);
+ }
+ if (1 > ((uint64_t)(a_workbuf.len))) {
+ return wuffs_base__make_status(wuffs_png__error__internal_error_inconsistent_workbuf_length);
+ }
+ v_filter = a_workbuf.ptr[0];
+ a_workbuf = wuffs_base__slice_u8__subslice_i(a_workbuf, 1);
+ if (self->private_impl.f_bytes_per_row > ((uint64_t)(a_workbuf.len))) {
+ return wuffs_base__make_status(wuffs_png__error__internal_error_inconsistent_workbuf_length);
+ }
+ v_curr_row = wuffs_base__slice_u8__subslice_j(a_workbuf, self->private_impl.f_bytes_per_row);
+ a_workbuf = wuffs_base__slice_u8__subslice_i(a_workbuf, self->private_impl.f_bytes_per_row);
+ if (v_filter == 0) {
+ } else if (((uint64_t)(v_prev_row.len)) == 0) {
+ }
+ wuffs_base__pixel_swizzler__swizzle_interleaved_from_slice(&self->private_impl.f_swizzler, v_dst, v_dst_palette, v_curr_row);
+ v_prev_row = v_curr_row;
+ v_y += 1;
+ }
+ return wuffs_base__make_status(NULL);
+}
+
// -------- func png.decoder.frame_dirty_rect
WUFFS_BASE__MAYBE_STATIC wuffs_base__rect_ie_u32
diff --git a/std/png/decode_png.wuffs b/std/png/decode_png.wuffs
index 89b6ab5..720a1c3 100644
--- a/std/png/decode_png.wuffs
+++ b/std/png/decode_png.wuffs
@@ -19,6 +19,7 @@
pub status "#bad header"
pub status "#unsupported PNG file"
+pri status "#internal error: inconsistent workbuf length"
pri status "#internal error: zlib decoder did not exhaust its input"
pub const DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE : base.u64 = 0
@@ -76,6 +77,9 @@
)(
crc : crc32.ieee_hasher,
zlib : zlib.decoder,
+
+ dst_palette : array[4 * 256] base.u8,
+ src_palette : array[4 * 256] base.u8,
)
pub func decoder.set_quirk_enabled!(quirk: base.u32, enabled: base.bool) {
@@ -232,10 +236,11 @@
}
pub func decoder.decode_frame?(dst: ptr base.pixel_buffer, src: base.io_reader, blend: base.pixel_blend, workbuf: slice base.u8, opts: nptr base.decode_frame_options) {
- var w : base.io_writer
- var w_mark : base.u64
- var r_mark : base.u64
- var zlib_status : base.status
+ var w : base.io_writer
+ var w_mark : base.u64
+ var r_mark : base.u64
+ var swizzler_status : base.status
+ var zlib_status : base.status
if this.call_sequence < 4 {
this.decode_frame_config?(dst: nullptr, src: args.src)
@@ -287,10 +292,85 @@
if this.workbuf_wi <> this.workbuf_length {
return base."#not enough data"
}
+ swizzler_status = this.swizzler.prepare!(
+ dst_pixfmt: args.dst.pixel_format(),
+ dst_palette: args.dst.palette_or_else(fallback: this.dst_palette[..]),
+ src_pixfmt: this.util.make_pixel_format(repr: this.src_pixfmt),
+ src_palette: this.src_palette[..],
+ blend: args.blend)
+ if not swizzler_status.is_ok() {
+ return swizzler_status
+ }
+ swizzler_status = this.filter_and_swizzle!(dst: args.dst, workbuf: args.workbuf)
+ if not swizzler_status.is_ok() {
+ return swizzler_status
+ }
this.call_sequence = 0xFF
}
+pri func decoder.filter_and_swizzle!(dst: ptr base.pixel_buffer, workbuf: slice base.u8) base.status {
+ var dst_pixfmt : base.pixel_format
+ var dst_bits_per_pixel : base.u32[..= 256]
+ var dst_bytes_per_pixel : base.u64[..= 32]
+ var dst_bytes_per_row : base.u64
+ var dst_palette : slice base.u8
+ var tab : table base.u8
+
+ var y : base.u32
+ var dst : slice base.u8
+ var filter : base.u8
+ var curr_row : slice base.u8
+ var prev_row : slice base.u8
+
+ // 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()
+ dst_bits_per_pixel = dst_pixfmt.bits_per_pixel()
+ if (dst_bits_per_pixel & 7) <> 0 {
+ return base."#unsupported option"
+ }
+ dst_bytes_per_pixel = (dst_bits_per_pixel / 8) as base.u64
+ dst_bytes_per_row = (this.width as base.u64) * dst_bytes_per_pixel
+ dst_palette = args.dst.palette_or_else(fallback: this.dst_palette[..])
+ tab = args.dst.plane(p: 0)
+
+ while y < this.height {
+ assert y < 0xFFFF_FFFF via "a < b: a < c; c <= b"(c: this.height)
+ dst = tab.row(y: y)
+ if dst_bytes_per_row < dst.length() {
+ dst = dst[.. dst_bytes_per_row]
+ }
+
+ if 1 > args.workbuf.length() {
+ return "#internal error: inconsistent workbuf length"
+ }
+ filter = args.workbuf[0]
+ args.workbuf = args.workbuf[1 ..]
+ if this.bytes_per_row > args.workbuf.length() {
+ return "#internal error: inconsistent workbuf length"
+ }
+ curr_row = args.workbuf[.. this.bytes_per_row]
+ args.workbuf = args.workbuf[this.bytes_per_row ..]
+
+ if filter == 0 {
+ // No-op.
+ } else if prev_row.length() == 0 {
+ // TODO.
+ }
+
+ this.swizzler.swizzle_interleaved_from_slice!(
+ dst: dst,
+ dst_palette: dst_palette,
+ src: curr_row)
+
+ prev_row = curr_row
+ y += 1
+ } endwhile
+
+ return ok
+}
+
pub func decoder.frame_dirty_rect() base.rect_ie_u32 {
return this.util.make_rect_ie_u32(
min_incl_x: 0,
diff --git a/test/c/std/png.c b/test/c/std/png.c
index e46beb7..9165588 100644
--- a/test/c/std/png.c
+++ b/test/c/std/png.c
@@ -83,7 +83,7 @@
WUFFS_INITIALIZE__LEAVE_INTERNAL_BUFFERS_UNINITIALIZED));
return do_test__wuffs_base__image_decoder(
wuffs_png__decoder__upcast_as__wuffs_base__image_decoder(&dec),
- "test/data/bricks-gray.png", 0, SIZE_MAX, 160, 120, 0); // TODO.
+ "test/data/bricks-gray.png", 0, SIZE_MAX, 160, 120, 0xFF060606);
}
const char* //