Browse Source

[olive.c] handle some edge cases in olivec_line

rexim 3 years ago
parent
commit
3cd4efa6bf
3 changed files with 41 additions and 3 deletions
  1. 25 3
      olive.c
  2. 16 0
      test.c
  3. BIN
      test/line_edge_cases_expected.png

+ 25 - 3
olive.c

@@ -339,8 +339,11 @@ OLIVECDEF void olivec_line(Olivec_Canvas oc, int x1, int y1, int x2, int y2, uin
     int dx = x2 - x1;
     int dx = x2 - x1;
     int dy = y2 - y1;
     int dy = y2 - y1;
 
 
+    // If both of the differences are 0 there will be a division by 0 below.
     if (dx == 0 && dy == 0) {
     if (dx == 0 && dy == 0) {
-        olivec_blend_color(&OLIVEC_PIXEL(oc, x1, y1), color);
+        if (0 <= x1 && x1 < (int) oc.width && 0 <= y1 && y1 < (int) oc.height) {
+            olivec_blend_color(&OLIVEC_PIXEL(oc, x1, y1), color);
+        }
         return;
         return;
     }
     }
 
 
@@ -349,10 +352,19 @@ OLIVECDEF void olivec_line(Olivec_Canvas oc, int x1, int y1, int x2, int y2, uin
             OLIVEC_SWAP(int, x1, x2);
             OLIVEC_SWAP(int, x1, x2);
             OLIVEC_SWAP(int, y1, y2);
             OLIVEC_SWAP(int, y1, y2);
         }
         }
+
+        // Cull out invisible line
+        if (x1 > (int) oc.width) return;
+        if (x2 < 0) return;
+
+        // Clamp the line to the boundaries
+        if (x1 < 0) x1 = 0;
+        if (x2 >= (int) oc.width) x2 = (int) oc.width - 1;
+
         for (int x = x1; x <= x2; ++x) {
         for (int x = x1; x <= x2; ++x) {
             int y = dy*(x - x1)/dx + y1;
             int y = dy*(x - x1)/dx + y1;
             // TODO: move boundary checks out side of the loops in olivec_draw_line
             // TODO: move boundary checks out side of the loops in olivec_draw_line
-            if (0 <= x && x < (int) oc.width && 0 <= y && y < (int) oc.height) {
+            if (0 <= y && y < (int) oc.height) {
                 olivec_blend_color(&OLIVEC_PIXEL(oc, x, y), color);
                 olivec_blend_color(&OLIVEC_PIXEL(oc, x, y), color);
             }
             }
         }
         }
@@ -361,9 +373,19 @@ OLIVECDEF void olivec_line(Olivec_Canvas oc, int x1, int y1, int x2, int y2, uin
             OLIVEC_SWAP(int, x1, x2);
             OLIVEC_SWAP(int, x1, x2);
             OLIVEC_SWAP(int, y1, y2);
             OLIVEC_SWAP(int, y1, y2);
         }
         }
+
+        // Cull out invisible line
+        if (y1 > (int) oc.height) return;
+        if (y2 < 0) return;
+
+        // Clamp the line to the boundaries
+        if (y1 < 0) y1 = 0;
+        if (y2 >= (int) oc.height) y2 = (int) oc.height - 1;
+
         for (int y = y1; y <= y2; ++y) {
         for (int y = y1; y <= y2; ++y) {
             int x = dx*(y - y1)/dy + x1;
             int x = dx*(y - y1)/dy + x1;
-            if (0 <= x && x < (int) oc.width && 0 <= y && y < (int) oc.height) {
+            // TODO: move boundary checks out side of the loops in olivec_draw_line
+            if (0 <= x && x < (int) oc.width) {
                 olivec_blend_color(&OLIVEC_PIXEL(oc, x, y), color);
                 olivec_blend_color(&OLIVEC_PIXEL(oc, x, y), color);
             }
             }
         }
         }

+ 16 - 0
test.c

@@ -383,6 +383,21 @@ Olivec_Canvas test_hello_world_text_rendering(void)
     return oc;
     return oc;
 }
 }
 
 
+Olivec_Canvas test_line_edge_cases(void)
+{
+    size_t width = 10;
+    size_t height = 10;
+    Olivec_Canvas oc = canvas_alloc(width, height);
+    olivec_fill(oc, BACKGROUND_COLOR);
+    // One pixel line
+    olivec_line(oc, width/2, height/2, width/2, height/2, FOREGROUND_COLOR);
+    // Out-of-bounds horizontally
+    olivec_line(oc, width + 10, height/2, width + 20, height/2, FOREGROUND_COLOR);
+    // Out-of-bounds vertically
+    olivec_line(oc, width/2, height + 10, width/2, height + 20, FOREGROUND_COLOR);
+    return oc;
+}
+
 Test_Case test_cases[] = {
 Test_Case test_cases[] = {
     DEFINE_TEST_CASE(fill_rect),
     DEFINE_TEST_CASE(fill_rect),
     DEFINE_TEST_CASE(fill_circle),
     DEFINE_TEST_CASE(fill_circle),
@@ -394,6 +409,7 @@ Test_Case test_cases[] = {
     DEFINE_TEST_CASE(lines_example),
     DEFINE_TEST_CASE(lines_example),
     DEFINE_TEST_CASE(hello_world_text_rendering),
     DEFINE_TEST_CASE(hello_world_text_rendering),
     DEFINE_TEST_CASE(lines_circle),
     DEFINE_TEST_CASE(lines_circle),
+    DEFINE_TEST_CASE(line_edge_cases),
 };
 };
 #define TEST_CASES_COUNT (sizeof(test_cases)/sizeof(test_cases[0]))
 #define TEST_CASES_COUNT (sizeof(test_cases)/sizeof(test_cases[0]))
 
 

BIN
test/line_edge_cases_expected.png