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
 }