Have std/bmp skip_frame handle RLE compression
diff --git a/release/c/wuffs-unsupported-snapshot.c b/release/c/wuffs-unsupported-snapshot.c
index 502d2c4..fdf5ddf 100644
--- a/release/c/wuffs-unsupported-snapshot.c
+++ b/release/c/wuffs-unsupported-snapshot.c
@@ -5635,6 +5635,7 @@
uint64_t scratch;
} s_decode_frame[1];
struct {
+ uint32_t v_rle_state;
uint64_t scratch;
} s_skip_frame[1];
struct {
@@ -18225,6 +18226,9 @@
wuffs_base__io_buffer* a_src) {
wuffs_base__status status = wuffs_base__make_status(NULL);
+ uint8_t v_code = 0;
+ uint32_t v_rle_state = 0;
+
const uint8_t* iop_a_src = NULL;
const uint8_t* io0_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
const uint8_t* io1_a_src WUFFS_BASE__POTENTIALLY_UNUSED = NULL;
@@ -18237,6 +18241,9 @@
}
uint32_t coro_susp_point = self->private_impl.p_skip_frame[0];
+ if (coro_susp_point) {
+ v_rle_state = self->private_data.s_skip_frame[0].v_rle_state;
+ }
switch (coro_susp_point) {
WUFFS_BASE__COROUTINE_SUSPENSION_POINT_0;
@@ -18249,15 +18256,167 @@
goto suspend;
}
iop_a_src += self->private_data.s_skip_frame[0].scratch;
- self->private_data.s_skip_frame[0].scratch = (self->private_impl.f_bytes_per_row * ((uint64_t)(self->private_impl.f_height)));
- WUFFS_BASE__COROUTINE_SUSPENSION_POINT(2);
- if (self->private_data.s_skip_frame[0].scratch > ((uint64_t)(io2_a_src - iop_a_src))) {
- self->private_data.s_skip_frame[0].scratch -= ((uint64_t)(io2_a_src - iop_a_src));
- iop_a_src = io2_a_src;
- status = wuffs_base__make_status(wuffs_base__suspension__short_read);
- goto suspend;
+ if ((self->private_impl.f_compression != 1) && (self->private_impl.f_compression != 2)) {
+ self->private_data.s_skip_frame[0].scratch = (self->private_impl.f_bytes_per_row * ((uint64_t)(self->private_impl.f_height)));
+ WUFFS_BASE__COROUTINE_SUSPENSION_POINT(2);
+ if (self->private_data.s_skip_frame[0].scratch > ((uint64_t)(io2_a_src - iop_a_src))) {
+ self->private_data.s_skip_frame[0].scratch -= ((uint64_t)(io2_a_src - iop_a_src));
+ iop_a_src = io2_a_src;
+ status = wuffs_base__make_status(wuffs_base__suspension__short_read);
+ goto suspend;
+ }
+ iop_a_src += self->private_data.s_skip_frame[0].scratch;
+ } else {
+ label__loop__continue:;
+ while (true) {
+ if (v_rle_state == 0) {
+ {
+ 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_0 = *iop_a_src++;
+ v_code = t_0;
+ }
+ if (v_code == 0) {
+ v_rle_state = 2;
+ goto label__loop__continue;
+ }
+ self->private_impl.f_rle_length = ((uint32_t)(v_code));
+ v_rle_state = 1;
+ goto label__loop__continue;
+ } else if (v_rle_state == 1) {
+ {
+ 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_1 = *iop_a_src++;
+ v_code = t_1;
+ }
+ wuffs_base__u32__sat_add_indirect(&self->private_impl.f_dst_x, self->private_impl.f_rle_length);
+ v_rle_state = 0;
+ goto label__loop__continue;
+ } else if (v_rle_state == 2) {
+ {
+ WUFFS_BASE__COROUTINE_SUSPENSION_POINT(5);
+ 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_code = t_2;
+ }
+ if (v_code < 2) {
+ if ((self->private_impl.f_dst_y >= self->private_impl.f_height) && (v_code == 0)) {
+ status = wuffs_base__make_status(wuffs_bmp__error__bad_rle_compression);
+ goto exit;
+ }
+ self->private_impl.f_dst_x = 0;
+ self->private_impl.f_dst_y += self->private_impl.f_dst_y_inc;
+ if (v_code > 0) {
+ goto label__loop__break;
+ }
+ v_rle_state = 0;
+ goto label__loop__continue;
+ } else if (v_code == 2) {
+ v_rle_state = 4;
+ goto label__loop__continue;
+ }
+ self->private_impl.f_rle_length = ((uint32_t)(v_code));
+ v_rle_state = 3;
+ goto label__loop__continue;
+ } else if (v_rle_state == 3) {
+ if (self->private_impl.f_bits_per_pixel == 8) {
+ self->private_data.s_skip_frame[0].scratch = (((self->private_impl.f_rle_length + 1) / 2) * 2);
+ WUFFS_BASE__COROUTINE_SUSPENSION_POINT(6);
+ if (self->private_data.s_skip_frame[0].scratch > ((uint64_t)(io2_a_src - iop_a_src))) {
+ self->private_data.s_skip_frame[0].scratch -= ((uint64_t)(io2_a_src - iop_a_src));
+ iop_a_src = io2_a_src;
+ status = wuffs_base__make_status(wuffs_base__suspension__short_read);
+ goto suspend;
+ }
+ iop_a_src += self->private_data.s_skip_frame[0].scratch;
+ } else {
+ self->private_data.s_skip_frame[0].scratch = (((self->private_impl.f_rle_length + 3) / 4) * 2);
+ WUFFS_BASE__COROUTINE_SUSPENSION_POINT(7);
+ if (self->private_data.s_skip_frame[0].scratch > ((uint64_t)(io2_a_src - iop_a_src))) {
+ self->private_data.s_skip_frame[0].scratch -= ((uint64_t)(io2_a_src - iop_a_src));
+ iop_a_src = io2_a_src;
+ status = wuffs_base__make_status(wuffs_base__suspension__short_read);
+ goto suspend;
+ }
+ iop_a_src += self->private_data.s_skip_frame[0].scratch;
+ }
+ wuffs_base__u32__sat_add_indirect(&self->private_impl.f_dst_x, self->private_impl.f_rle_length);
+ self->private_impl.f_rle_length = 0;
+ v_rle_state = 0;
+ goto label__loop__continue;
+ } else if (v_rle_state == 4) {
+ {
+ WUFFS_BASE__COROUTINE_SUSPENSION_POINT(8);
+ 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++;
+ self->private_impl.f_rle_delta_x = t_3;
+ }
+ v_rle_state = 5;
+ goto label__loop__continue;
+ }
+ {
+ WUFFS_BASE__COROUTINE_SUSPENSION_POINT(9);
+ 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_4 = *iop_a_src++;
+ v_code = t_4;
+ }
+ if (self->private_impl.f_rle_delta_x > 0) {
+ wuffs_base__u32__sat_add_indirect(&self->private_impl.f_dst_x, ((uint32_t)(self->private_impl.f_rle_delta_x)));
+ self->private_impl.f_rle_delta_x = 0;
+ if (self->private_impl.f_dst_x > self->private_impl.f_width) {
+ status = wuffs_base__make_status(wuffs_bmp__error__bad_rle_compression);
+ goto exit;
+ }
+ }
+ if (v_code > 0) {
+#if defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wconversion"
+#endif
+ v_code -= 1;
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
+ while (true) {
+ self->private_impl.f_dst_y += self->private_impl.f_dst_y_inc;
+ if (self->private_impl.f_dst_y >= self->private_impl.f_height) {
+ status = wuffs_base__make_status(wuffs_bmp__error__bad_rle_compression);
+ goto exit;
+ }
+ if (v_code <= 0) {
+ goto label__0__break;
+ }
+#if defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wconversion"
+#endif
+ v_code -= 1;
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
+ }
+ label__0__break:;
+ }
+ v_rle_state = 0;
+ }
+ label__loop__break:;
}
- iop_a_src += self->private_data.s_skip_frame[0].scratch;
self->private_impl.f_call_sequence = 3;
goto ok;
@@ -18269,6 +18428,7 @@
goto suspend;
suspend:
self->private_impl.p_skip_frame[0] = wuffs_base__status__is_suspension(&status) ? coro_susp_point : 0;
+ self->private_data.s_skip_frame[0].v_rle_state = v_rle_state;
goto exit;
exit:
diff --git a/std/bmp/decode_bmp.wuffs b/std/bmp/decode_bmp.wuffs
index b32c53b..f0a8467 100644
--- a/std/bmp/decode_bmp.wuffs
+++ b/std/bmp/decode_bmp.wuffs
@@ -1087,8 +1087,97 @@
}
pri func decoder.skip_frame?(src: base.io_reader) {
+ var code : base.u8
+ var rle_state : base.u32
+
args.src.skip_u32?(n: this.padding)
- args.src.skip?(n: this.bytes_per_row * (this.height as base.u64))
+
+ if (this.compression <> COMPRESSION_RLE8) and
+ (this.compression <> COMPRESSION_RLE4) {
+ args.src.skip?(n: this.bytes_per_row * (this.height as base.u64))
+
+ } else {
+ while.loop true {
+ if rle_state == RLE_STATE_NEUTRAL {
+ code = args.src.read_u8?()
+ if code == 0 {
+ rle_state = RLE_STATE_ESCAPE
+ continue.loop
+ }
+ this.rle_length = code as base.u32
+ rle_state = RLE_STATE_RUN
+ continue.loop
+
+ } else if rle_state == RLE_STATE_RUN {
+ code = args.src.read_u8?()
+ this.dst_x ~sat+= this.rle_length
+ rle_state = RLE_STATE_NEUTRAL
+ continue.loop
+
+ } else if rle_state == RLE_STATE_ESCAPE {
+ code = args.src.read_u8?()
+ if code < 2 { // 0=EOL, 1=EOF.
+ if (this.dst_y >= this.height) and (code == 0) {
+ return "#bad RLE compression"
+ }
+ this.dst_x = 0
+ this.dst_y ~mod+= this.dst_y_inc
+ if code > 0 {
+ break.loop
+ }
+ rle_state = RLE_STATE_NEUTRAL
+ continue.loop
+ } else if code == 2 { // 2=DELTA.
+ rle_state = RLE_STATE_DELTA_X
+ continue.loop
+ }
+ this.rle_length = code as base.u32
+ rle_state = RLE_STATE_LITERAL
+ continue.loop
+
+ } else if rle_state == RLE_STATE_LITERAL {
+ if this.bits_per_pixel == 8 {
+ args.src.skip_u32?(n: ((this.rle_length + 1) / 2) * 2)
+ } else {
+ args.src.skip_u32?(n: ((this.rle_length + 3) / 4) * 2)
+ }
+ this.dst_x ~sat+= this.rle_length
+ this.rle_length = 0
+ rle_state = RLE_STATE_NEUTRAL
+ continue.loop
+
+ } else if rle_state == RLE_STATE_DELTA_X {
+ this.rle_delta_x = args.src.read_u8?()
+ rle_state = RLE_STATE_DELTA_Y
+ continue.loop
+
+ } // else (rle_state == RLE_STATE_DELTA_Y).
+ code = args.src.read_u8?()
+ if this.rle_delta_x > 0 {
+ this.dst_x ~sat+= this.rle_delta_x as base.u32
+ this.rle_delta_x = 0
+ if this.dst_x > this.width {
+ return "#bad RLE compression"
+ }
+ }
+
+ if code > 0 {
+ code -= 1
+ while true {
+ this.dst_y ~mod+= this.dst_y_inc
+ if this.dst_y >= this.height {
+ return "#bad RLE compression"
+ }
+ if code <= 0 {
+ break
+ }
+ code -= 1
+ } endwhile
+ }
+
+ rle_state = RLE_STATE_NEUTRAL
+ } endwhile.loop
+ }
this.call_sequence = 3
}