Browse Source

Fix cut out of bounds bugs in olivec_copy()

rexim 3 years ago
parent
commit
cf8132a3c4
5 changed files with 49 additions and 15 deletions
  1. 29 10
      olive.c
  2. 20 5
      test.c
  3. BIN
      test/copy_flip_cut_expected.png
  4. BIN
      test/copy_out_of_bounds_cut_expected.png
  5. BIN
      wasm/squish.wasm

+ 29 - 10
olive.c

@@ -702,16 +702,35 @@ OLIVECDEF void olivec_text(Olivec_Canvas oc, const char *text, int tx, int ty, O
 // TODO: bilinear interpolation for olivec_copy
 OLIVECDEF void olivec_copy(Olivec_Canvas src, Olivec_Canvas dst, int x, int y, int w, int h)
 {
-    int x1, x2, y1, y2;
-    if (olivec_normalize_rect(x, y, w, h, dst.width, dst.height, &x1, &x2, &y1, &y2)) {
-        int xa = x1; if (w < 0) xa = x2;
-        int ya = y1; if (h < 0) ya = y2;
-        for (int y = y1; y <= y2; ++y) {
-            for (int x = x1; x <= x2; ++x) {
-                size_t nx = (x - xa)*((int) src.width)/w;
-                size_t ny = (y - ya)*((int) src.height)/h;
-                olivec_blend_color(&OLIVEC_PIXEL(dst, x, y), OLIVEC_PIXEL(src, nx, ny));
-            }
+    int ox1 = x;
+    int oy1 = y;
+
+    // Convert the rectangle to 2-points representation
+    int ox2 = ox1 + OLIVEC_SIGN(int, w)*(OLIVEC_ABS(int, w) - 1);
+    if (ox1 > ox2) OLIVEC_SWAP(int, ox1, ox2);
+    int oy2 = oy1 + OLIVEC_SIGN(int, h)*(OLIVEC_ABS(int, h) - 1);
+    if (oy1 > oy2) OLIVEC_SWAP(int, oy1, oy2);
+
+    // Cull out invisible rectangle
+    if (ox1 >= (int) dst.width) return;
+    if (ox2 < 0) return;
+    if (oy1 >= (int) dst.height) return;
+    if (oy2 < 0) return;
+
+    // Clamp the rectangle to the boundaries
+    int x1 = ox1, x2 = ox2, y1 = oy1, y2 = oy2;
+    if (x1 < 0) x1 = 0;
+    if (x2 >= (int) dst.width) x2 = (int) dst.width - 1;
+    if (y1 < 0) y1 = 0;
+    if (y2 >= (int) dst.height) y2 = (int) dst.height - 1;
+
+    int xa = ox1; if (w < 0) xa = ox2;
+    int ya = oy1; if (h < 0) ya = oy2;
+    for (int y = y1; y <= y2; ++y) {
+        for (int x = x1; x <= x2; ++x) {
+            size_t nx = (x - xa)*((int) src.width)/w;
+            size_t ny = (y - ya)*((int) src.height)/h;
+            olivec_blend_color(&OLIVEC_PIXEL(dst, x, y), OLIVEC_PIXEL(src, nx, ny));
         }
     }
 }

+ 20 - 5
test.c

@@ -476,12 +476,12 @@ Olivec_Canvas test_copy_out_of_bounds_cut(void)
     size_t width = 128;
     size_t height = 128;
     Olivec_Canvas dst = canvas_alloc(width, height);
+    Olivec_Canvas src = olivec_canvas(png, png_width, png_height, png_width);
     olivec_fill(dst, RED_COLOR);
-    olivec_copy(
-        olivec_canvas(png, png_width, png_height, png_width),
-        dst,
-        width/2, height/2, width, height);
-
+    olivec_copy(src, dst, -width/2, -height/2, width, height);
+    olivec_copy(src, dst, width/2, -height/2, width, height);
+    olivec_copy(src, dst, -width/2, height/2, width, height);
+    olivec_copy(src, dst, width/2, height/2, width, height);
     return dst;
 }
 
@@ -499,6 +499,20 @@ Olivec_Canvas test_copy_flip(void)
     return dst;
 }
 
+Olivec_Canvas test_copy_flip_cut(void)
+{
+    size_t width = 128;
+    size_t height = 128;
+    Olivec_Canvas dst = canvas_alloc(width, height);
+    Olivec_Canvas src = olivec_canvas(png, png_width, png_height, png_width);
+    olivec_fill(dst, RED_COLOR);
+    olivec_copy(src, dst, -width/2, -height/2, width, height);
+    olivec_copy(src, dst, width - 1 + width/2, -height/2, -width, height);
+    olivec_copy(src, dst, -width/2, height - 1 + height/2, width, -height);
+    olivec_copy(src, dst, width - 1 + width/2, height - 1 + height/2, -width, -height);
+    return dst;
+}
+
 Test_Case test_cases[] = {
     DEFINE_TEST_CASE(fill_rect),
     DEFINE_TEST_CASE(fill_circle),
@@ -515,6 +529,7 @@ Test_Case test_cases[] = {
     DEFINE_TEST_CASE(blending_of_copy),
     DEFINE_TEST_CASE(copy_out_of_bounds_cut),
     DEFINE_TEST_CASE(copy_flip),
+    DEFINE_TEST_CASE(copy_flip_cut),
 };
 #define TEST_CASES_COUNT (sizeof(test_cases)/sizeof(test_cases[0]))
 

BIN
test/copy_flip_cut_expected.png


BIN
test/copy_out_of_bounds_cut_expected.png


BIN
wasm/squish.wasm