Browse Source

Implement Olivec_Canvas

rexim 3 years ago
parent
commit
a800e2ac6f
7 changed files with 184 additions and 167 deletions
  1. 4 4
      build.sh
  2. 21 40
      examples/gallery.c
  3. 42 11
      examples/triangle.c
  4. 83 81
      olive.c
  5. 32 30
      test.c
  6. 2 1
      test/.gitignore
  7. BIN
      test/test_alpha_blending_expected.png

+ 4 - 4
build.sh

@@ -4,7 +4,7 @@ set -xe
 
 mkdir -p ./bin/
 
-cc -Wall -Wextra -ggdb -o ./bin/test -Ithirdparty test.c -lm
-cc -Wall -Wextra -ggdb -o ./bin/gallery -Ithirdparty -I. examples/gallery.c
-clang -Os -fno-builtin -Wall -Wextra -Wswitch-enum --target=wasm32 --no-standard-libraries -Wl,--no-entry -Wl,--export-all -Wl,--allow-undefined  -o ./bin/triangle.wasm -I. ./examples/triangle.c
-cc -Wall -Wextra -ggdb -I. -DSDL_PLATFORM -o ./bin/triangle ./examples/triangle.c -lm -lSDL2
+clang -Wall -Wextra -ggdb -o ./bin/test -Ithirdparty test.c -lm
+clang -Wall -Wextra -ggdb -o ./bin/gallery -Ithirdparty -I. examples/gallery.c
+clang -Os -fno-builtin -Wall -Wextra -Wswitch-enum --target=wasm32 --no-standard-libraries -Wl,--no-entry -Wl,--export=render -Wl,--allow-undefined  -o ./bin/triangle.wasm -I. ./examples/triangle.c
+clang -O2 -Wall -Wextra -ggdb -I. -DSDL_PLATFORM -o ./bin/triangle ./examples/triangle.c -lm -lSDL2

+ 21 - 40
examples/gallery.c

@@ -27,7 +27,9 @@ static uint32_t pixels[WIDTH*HEIGHT];
 
 bool checker_example(void)
 {
-    olivec_fill(pixels, WIDTH, HEIGHT, BACKGROUND_COLOR);
+    Olivec_Canvas oc = olivec_make_canvas(pixels, WIDTH, HEIGHT);
+
+    olivec_fill(oc, BACKGROUND_COLOR);
 
     for (int y = 0; y < ROWS; ++y) {
         for (int x = 0; x < COLS; ++x) {
@@ -35,7 +37,7 @@ bool checker_example(void)
             if ((x + y)%2 == 0) {
                 color = 0xFF2020FF;
             }
-            olivec_fill_rect(pixels, WIDTH, HEIGHT, x*CELL_WIDTH, y*CELL_HEIGHT, CELL_WIDTH, CELL_HEIGHT, color);
+            olivec_rect(oc, x*CELL_WIDTH, y*CELL_HEIGHT, CELL_WIDTH, CELL_HEIGHT, color);
         }
     }
 
@@ -55,7 +57,8 @@ float lerpf(float a, float b, float t)
 
 bool circle_example(void)
 {
-    olivec_fill(pixels, WIDTH, HEIGHT, BACKGROUND_COLOR);
+    Olivec_Canvas oc = olivec_make_canvas(pixels, WIDTH, HEIGHT);
+    olivec_fill(oc, BACKGROUND_COLOR);
 
     for (int y = 0; y < ROWS; ++y) {
         for (int x = 0; x < COLS; ++x) {
@@ -66,10 +69,10 @@ bool circle_example(void)
             size_t radius = CELL_WIDTH;
             if (CELL_HEIGHT < radius) radius = CELL_HEIGHT;
 
-            olivec_fill_circle(pixels, WIDTH, HEIGHT,
-                               x*CELL_WIDTH + CELL_WIDTH/2, y*CELL_HEIGHT + CELL_HEIGHT/2,
-                               (size_t) lerpf(radius/8, radius/2, t),
-                               FOREGROUND_COLOR);
+            olivec_circle(oc,
+                          x*CELL_WIDTH + CELL_WIDTH/2, y*CELL_HEIGHT + CELL_HEIGHT/2,
+                          (size_t) lerpf(radius/8, radius/2, t),
+                          FOREGROUND_COLOR);
         }
     }
 
@@ -84,39 +87,17 @@ bool circle_example(void)
 
 bool lines_example(void)
 {
-    olivec_fill(pixels, WIDTH, HEIGHT, BACKGROUND_COLOR);
-
-    olivec_draw_line(pixels, WIDTH, HEIGHT,
-                     0, 0, WIDTH, HEIGHT,
-                     FOREGROUND_COLOR);
-
-    olivec_draw_line(pixels, WIDTH, HEIGHT,
-                     WIDTH, 0, 0, HEIGHT,
-                     FOREGROUND_COLOR);
-
-    olivec_draw_line(pixels, WIDTH, HEIGHT,
-                     0, 0, WIDTH/4, HEIGHT,
-                     0xFF20FF20);
-
-    olivec_draw_line(pixels, WIDTH, HEIGHT,
-                     WIDTH/4, 0, 0, HEIGHT,
-                     0xFF20FF20);
-
-    olivec_draw_line(pixels, WIDTH, HEIGHT,
-                     WIDTH, 0, WIDTH/4*3, HEIGHT,
-                     0xFF20FF20);
-
-    olivec_draw_line(pixels, WIDTH, HEIGHT,
-                     WIDTH/4*3, 0, WIDTH, HEIGHT,
-                     0xFF20FF20);
-
-    olivec_draw_line(pixels, WIDTH, HEIGHT,
-                     0, HEIGHT/2, WIDTH, HEIGHT/2,
-                     0xFFFF3030);
-
-    olivec_draw_line(pixels, WIDTH, HEIGHT,
-                     WIDTH/2, 0, WIDTH/2, HEIGHT,
-                     0xFFFF3030);
+    Olivec_Canvas oc = olivec_make_canvas(pixels, WIDTH, HEIGHT);
+
+    olivec_fill(oc, BACKGROUND_COLOR);
+    olivec_line(oc, 0, 0, WIDTH, HEIGHT, FOREGROUND_COLOR);
+    olivec_line(oc, WIDTH, 0, 0, HEIGHT, FOREGROUND_COLOR);
+    olivec_line(oc, 0, 0, WIDTH/4, HEIGHT, 0xFF20FF20);
+    olivec_line(oc, WIDTH/4, 0, 0, HEIGHT, 0xFF20FF20);
+    olivec_line(oc, WIDTH, 0, WIDTH/4*3, HEIGHT, 0xFF20FF20);
+    olivec_line(oc, WIDTH/4*3, 0, WIDTH, HEIGHT, 0xFF20FF20);
+    olivec_line(oc, 0, HEIGHT/2, WIDTH, HEIGHT/2, 0xFFFF3030);
+    olivec_line(oc, WIDTH/2, 0, WIDTH/2, HEIGHT, 0xFFFF3030);
 
     const char *file_path = IMGS_DIR_PATH"/lines.png";
     printf("Generated %s\n", file_path);

+ 42 - 11
examples/triangle.c

@@ -5,9 +5,16 @@
 
 #define WIDTH 800
 #define HEIGHT 600
+#define BACKGROUND_COLOR 0xFF181818
+#define CIRCLE_RADIUS 100
+#define CIRCLE_COLOR 0x99AA2020
 
 static uint32_t pixels[WIDTH*HEIGHT];
-static float angle = 0;
+static float triangle_angle = 0;
+static float circle_x = WIDTH/2;
+static float circle_y = HEIGHT/2;
+static float circle_dx = 100;
+static float circle_dy = 100;
 
 float sqrtf(float x);
 float atan2f(float y, float x);
@@ -16,30 +23,54 @@ float cosf(float x);
 
 #define PI 3.14159265359
 
-void rotate_point(int *x, int *y)
+static inline void rotate_point(float *x, float *y)
 {
-    int dx = *x - WIDTH/2;
-    int dy = *y - HEIGHT/2;
+    float dx = *x - WIDTH/2;
+    float dy = *y - HEIGHT/2;
     float mag = sqrtf(dx*dx + dy*dy);
-    float dir = atan2f(dy, dx) + angle;
+    float dir = atan2f(dy, dx) + triangle_angle;
     *x = cosf(dir)*mag + WIDTH/2;
     *y = sinf(dir)*mag + HEIGHT/2;
 }
 
 uint32_t *render(float dt)
 {
-    angle += 0.5f*PI*dt;
+    Olivec_Canvas oc = olivec_make_canvas(pixels, WIDTH, HEIGHT);
 
-    olivec_fill(pixels, WIDTH, HEIGHT, 0xFF181818);
+    olivec_fill(oc, BACKGROUND_COLOR);
+
+    // Triangle
     {
-        int x1 = WIDTH/2, y1 = HEIGHT/8;
-        int x2 = WIDTH/8, y2 = HEIGHT/2;
-        int x3 = WIDTH*7/8, y3 = HEIGHT*7/8;
+        triangle_angle += 0.5f*PI*dt;
+
+        float x1 = WIDTH/2, y1 = HEIGHT/8;
+        float x2 = WIDTH/8, y2 = HEIGHT/2;
+        float x3 = WIDTH*7/8, y3 = HEIGHT*7/8;
         rotate_point(&x1, &y1);
         rotate_point(&x2, &y2);
         rotate_point(&x3, &y3);
-        olivec_fill_triangle(pixels, WIDTH, HEIGHT, x1, y1, x2, y2, x3, y3, 0xFF2020AA);
+        olivec_triangle(oc, x1, y1, x2, y2, x3, y3, 0xFF2020AA);
+    }
+
+    // Circle
+    {
+        float x = circle_x + circle_dx*dt;
+        if (x - CIRCLE_RADIUS < 0 || x + CIRCLE_RADIUS >= WIDTH) {
+            circle_dx *= -1;
+        } else {
+            circle_x = x;
+        }
+
+        float y = circle_y + circle_dy*dt;
+        if (y - CIRCLE_RADIUS < 0 || y + CIRCLE_RADIUS >= HEIGHT) {
+            circle_dy *= -1;
+        } else {
+            circle_y = y;
+        }
+
+        olivec_circle(oc, circle_x, circle_y, CIRCLE_RADIUS, CIRCLE_COLOR);
     }
+
     return pixels;
 }
 

+ 83 - 81
olive.c

@@ -5,31 +5,49 @@
 #include <stdint.h>
 #include <stdbool.h>
 
+#ifndef OLIVECDEF
+#define OLIVECDEF static inline
+#endif
+
 #define OLIVEC_SWAP(T, a, b) do { T t = a; a = b; b = t; } while (0)
 #define OLIVEC_SIGN(T, x) ((T)((x) > 0) - (T)((x) < 0))
 #define OLIVEC_ABS(T, x) (OLIVEC_SIGN(T, x)*(x))
 
-void olivec_fill(uint32_t *pixels, size_t width, size_t height, uint32_t color)
+typedef struct {
+    uint32_t *pixels;
+    size_t width;
+    size_t height;
+    size_t stride;
+} Olivec_Canvas;
+
+#define OLIVEC_CANVAS_NULL ((Olivec_Canvas) {0})
+#define OLIVEC_PIXEL(oc, x, y) (oc).pixels[(y)*(oc).stride + (x)]
+
+OLIVECDEF Olivec_Canvas olivec_make_canvas(uint32_t *pixels, size_t width, size_t height)
 {
-    for (size_t i = 0; i < width*height; ++i) {
-        pixels[i] = color;
-    }
+    Olivec_Canvas oc = {
+        .pixels = pixels,
+        .width  = width,
+        .height = height,
+        .stride = width,
+    };
+    return oc;
 }
 
 // The point of this function is to produce two ranges x1..x2 and y1..y2 that are guaranteed to be safe to iterate over the canvas of size pixels_width by pixels_height without any boundary checks.
-// 
+//
 // if (olivec_normalize_rect(x, y, w, h, WIDTH, HEIGHT, &x1, &y1, &x2, &y2)) {
 //     for (int x = x1; x <= x2; ++x) {
-//         for (itn y = y1; y <= y2; ++y) {
-//             pixels[y*pixels_width + x] = 0x69696969;
+//         for (int y = y1; y <= y2; ++y) {
+//             OLIVEC_PIXEL(oc, x, y) = 0x69696969;
 //         }
 //     }
 // } else {
 //     // Rectangle is invisible cause it's completely out-of-bounds
 // }
-bool olivec_normalize_rect(int x, int y, int w, int h,
-                           size_t pixels_width, size_t pixels_height,
-                           int *x1, int *x2, int *y1, int *y2)
+OLIVECDEF bool olivec_normalize_rect(int x, int y, int w, int h,
+                                     size_t pixels_width, size_t pixels_height,
+                                     int *x1, int *x2, int *y1, int *y2)
 {
     *x1 = x;
     *y1 = y;
@@ -55,88 +73,75 @@ bool olivec_normalize_rect(int x, int y, int w, int h,
     return true;
 }
 
-typedef enum {
-    COMP_RED = 0,
-    COMP_GREEN,
-    COMP_BLUE,
-    COMP_ALPHA,
-    COUNT_COMPS,
-} Comp_Index;
-
-void unpack_rgba32(uint32_t c, uint8_t comp[COUNT_COMPS])
-{
-    for (size_t i = 0; i < COUNT_COMPS; ++i) {
-        comp[i] = c&0xFF;
-        c >>= 8;
-    }
-}
-
-uint32_t pack_rgba32(uint8_t comp[COUNT_COMPS])
+OLIVECDEF Olivec_Canvas olivec_subcanvas(Olivec_Canvas oc, int x, int y, int w, int h)
 {
-    uint32_t result = 0;
-    for (size_t i = 0; i < COUNT_COMPS; ++i) {
-        result |= comp[i]<<(8*i);
-    }
-    return result;
+    int x1, x2, y1, y2;
+    if (!olivec_normalize_rect(x, y, w, h, oc.width, oc.height, &x1, &x2, &y1, &y2)) return OLIVEC_CANVAS_NULL;
+    oc.pixels = &OLIVEC_PIXEL(oc, x1, y1);
+    oc.width = x2 - x1 + 1;
+    oc.height = y2 - y1 + 1;
+    return oc;
 }
 
-uint8_t olivec_mix_comps(uint16_t c1, uint16_t c2, uint16_t a)
-{
-    return c1 + (c2 - c1)*a/255;
-}
 
-uint32_t olivec_mix_colors(uint32_t c1, uint32_t c2)
+OLIVECDEF void olivec_blend_color(uint32_t *c1, uint32_t c2)
 {
-    uint8_t comp1[COUNT_COMPS];
-    unpack_rgba32(c1, comp1);
+    uint32_t r1 = ((*c1)>>(0*8))&0xFF;
+    uint32_t g1 = ((*c1)>>(1*8))&0xFF;
+    uint32_t b1 = ((*c1)>>(2*8))&0xFF;
+    uint32_t a1 = ((*c1)>>(3*8))&0xFF;
 
-    uint8_t comp2[COUNT_COMPS];
-    unpack_rgba32(c2, comp2);
+    uint32_t r2 = (c2>>(0*8))&0xFF;
+    uint32_t g2 = (c2>>(1*8))&0xFF;
+    uint32_t b2 = (c2>>(2*8))&0xFF;
+    uint32_t a2 = (c2>>(3*8))&0xFF;
 
-    for (size_t i = 0; i < COMP_ALPHA; ++i) {
-        comp1[i] = olivec_mix_comps(comp1[i], comp2[i], comp2[COMP_ALPHA]);
-    }
+    r1 = (r1*(255 - a2) + r2*a2)/255; if (r1 > 255) r1 = 255;
+    g1 = (g1*(255 - a2) + g2*a2)/255; if (g1 > 255) g1 = 255;
+    b1 = (b1*(255 - a2) + b2*a2)/255; if (b1 > 255) b1 = 255;
 
-    return pack_rgba32(comp1);
+    *c1 = (r1<<(0*8)) | (g1<<(1*8)) | (b1<<(2*8)) | (a1<<(3*8));
 }
 
-void olivec_fill_rect(uint32_t *pixels, size_t pixels_width, size_t pixels_height,
-                      int x, int y, int w, int h,
-                      uint32_t color)
+OLIVECDEF void olivec_fill(Olivec_Canvas oc, uint32_t color)
 {
-    int x1, y1, x2, y2;
-    if (!olivec_normalize_rect(x, y, w, h, pixels_width, pixels_height, &x1, &x2, &y1, &y2)) return;
+    for (size_t y = 0; y < oc.height; ++y) {
+        for (size_t x = 0; x < oc.width; ++x) {
+            OLIVEC_PIXEL(oc, x, y) = color;
+        }
+    }
+}
 
-    for (int y = y1; y <= y2; ++y) {
-        for (int x = x1; x <= x2; ++x) {
-            pixels[y*pixels_width + x] = olivec_mix_colors(pixels[y*pixels_width + x], color);
+OLIVECDEF void olivec_rect(Olivec_Canvas oc, int x, int y, int w, int h, uint32_t color)
+{
+    int x1, x2, y1, y2;
+    if (!olivec_normalize_rect(x, y, w, h, oc.width, oc.height, &x1, &x2, &y1, &y2)) return;
+    for (int x = x1; x <= x2; ++x) {
+        for (int y = y1; y <= y2; ++y) {
+            olivec_blend_color(&OLIVEC_PIXEL(oc, x, y), color);
         }
     }
 }
 
-void olivec_fill_circle(uint32_t *pixels, size_t pixels_width, size_t pixels_height,
-                        int cx, int cy, int r,
-                        uint32_t color)
+OLIVECDEF void olivec_circle(Olivec_Canvas oc, int cx, int cy, int r, uint32_t color)
 {
     int x1, y1, x2, y2;
     int r1 = r + OLIVEC_SIGN(int, r);
-    if (!olivec_normalize_rect(cx - r1, cy - r1, 2*r1, 2*r1, pixels_width, pixels_height, &x1, &x2, &y1, &y2)) return;
+    if (!olivec_normalize_rect(cx - r1, cy - r1, 2*r1, 2*r1, oc.width, oc.height, &x1, &x2, &y1, &y2)) return;
 
     for (int y = y1; y <= y2; ++y) {
         for (int x = x1; x <= x2; ++x) {
             int dx = x - cx;
             int dy = y - cy;
             if (dx*dx + dy*dy <= r*r) {
-                pixels[y*pixels_width + x] = olivec_mix_colors(pixels[y*pixels_width + x], color);
+                olivec_blend_color(&OLIVEC_PIXEL(oc, x, y), color);
             }
         }
     }
 }
 
 // TODO: lines with different thicness
-void olivec_draw_line(uint32_t *pixels, size_t pixels_width, size_t pixels_height,
-                      int x1, int y1, int x2, int y2,
-                      uint32_t color)
+OLIVECDEF void olivec_line(Olivec_Canvas oc, int x1, int y1, int x2, int y2, uint32_t color)
 {
     // TODO: fix the olivec_draw_line stairs
     int dx = x2 - x1;
@@ -148,35 +153,31 @@ void olivec_draw_line(uint32_t *pixels, size_t pixels_width, size_t pixels_heigh
         if (x1 > x2) OLIVEC_SWAP(int, x1, x2);
         for (int x = x1; x <= x2; ++x) {
             // TODO: move boundary checks out side of the loops in olivec_draw_line
-            if (0 <= x && x < (int) pixels_width) {
+            if (0 <= x && x < (int) oc.width) {
                 int sy1 = dy*x/dx + c;
                 int sy2 = dy*(x + 1)/dx + c;
                 if (sy1 > sy2) OLIVEC_SWAP(int, sy1, sy2);
                 for (int y = sy1; y <= sy2; ++y) {
-                    if (0 <= y && y < (int) pixels_height) {
-                        pixels[y*pixels_width + x] = olivec_mix_colors(pixels[y*pixels_width + x], color);
+                    if (0 <= y && y < (int) oc.height) {
+                        olivec_blend_color(&OLIVEC_PIXEL(oc, x, y), color);
                     }
                 }
             }
         }
     } else {
         int x = x1;
-        if (0 <= x && x < (int) pixels_width) {
+        if (0 <= x && x < (int) oc.width) {
             if (y1 > y2) OLIVEC_SWAP(int, y1, y2);
             for (int y = y1; y <= y2; ++y) {
-                if (0 <= y && y < (int) pixels_height) {
-                    pixels[y*pixels_width + x] = olivec_mix_colors(pixels[y*pixels_width + x], color);
+                if (0 <= y && y < (int) oc.height) {
+                    olivec_blend_color(&OLIVEC_PIXEL(oc, x, y), color);
                 }
             }
         }
     }
 }
 
-void olivec_fill_triangle(uint32_t *pixels, size_t pixels_width, size_t pixels_height,
-                          int x1, int y1,
-                          int x2, int y2,
-                          int x3, int y3,
-                          uint32_t color)
+OLIVECDEF void olivec_triangle(Olivec_Canvas oc, int x1, int y1, int x2, int y2, int x3, int y3, uint32_t color)
 {
     if (y1 > y2) {
         OLIVEC_SWAP(int, x1, x2);
@@ -200,13 +201,13 @@ void olivec_fill_triangle(uint32_t *pixels, size_t pixels_width, size_t pixels_h
 
     for (int y = y1; y <= y2; ++y) {
         // TODO: move boundary checks outside of loops in olivec_fill_triangle
-        if (0 <= y && (size_t) y < pixels_height) {
+        if (0 <= y && (size_t) y < oc.height) {
             int s1 = dy12 != 0 ? (y - y1)*dx12/dy12 + x1 : x1;
             int s2 = dy13 != 0 ? (y - y1)*dx13/dy13 + x1 : x1;
             if (s1 > s2) OLIVEC_SWAP(int, s1, s2);
             for (int x = s1; x <= s2; ++x) {
-                if (0 <= x && (size_t) x < pixels_width) {
-                    pixels[y*pixels_width + x] = olivec_mix_colors(pixels[y*pixels_width + x], color);
+                if (0 <= x && (size_t) x < oc.width) {
+                    olivec_blend_color(&OLIVEC_PIXEL(oc, x, y), color);
                 }
             }
         }
@@ -218,23 +219,24 @@ void olivec_fill_triangle(uint32_t *pixels, size_t pixels_width, size_t pixels_h
     int dy31 = y1 - y3;
 
     for (int y = y2; y <= y3; ++y) {
-        if (0 <= y && (size_t) y < pixels_height) {
+        if (0 <= y && (size_t) y < oc.height) {
             int s1 = dy32 != 0 ? (y - y3)*dx32/dy32 + x3 : x3;
             int s2 = dy31 != 0 ? (y - y3)*dx31/dy31 + x3 : x3;
             if (s1 > s2) OLIVEC_SWAP(int, s1, s2);
             for (int x = s1; x <= s2; ++x) {
-                if (0 <= x && (size_t) x < pixels_width) {
-                    pixels[y*pixels_width + x] = olivec_mix_colors(pixels[y*pixels_width + x], color);
+                if (0 <= x && (size_t) x < oc.width) {
+                    olivec_blend_color(&OLIVEC_PIXEL(oc, x, y), color);
                 }
             }
         }
     }
 }
 
-// TODO: Olivec_Canvas
 // TODO: supersampling
-// TODO: olivec_draw_circle
-// TODO: olivec_(draw|fill)_ellipse
+// TODO: 3D triangles
 // TODO: Benchmarking
+// TODO: SIMD implementations
+// TODO: olivec_ring
+// TODO: olivec_ellipse
 
 #endif // OLIVE_C_

+ 32 - 30
test.c

@@ -160,65 +160,67 @@ typedef struct {
 
 void test_fill_rect(void)
 {
-    olivec_fill(actual_pixels, WIDTH, HEIGHT, BACKGROUND_COLOR);
-    olivec_fill_rect(actual_pixels, WIDTH, HEIGHT, WIDTH/2 - WIDTH/8, HEIGHT/2 - HEIGHT/8, WIDTH/4, HEIGHT/4, RED_COLOR);
-    olivec_fill_rect(actual_pixels, WIDTH, HEIGHT, WIDTH - 1, HEIGHT - 1, -WIDTH/2, -HEIGHT/2, GREEN_COLOR);
-    olivec_fill_rect(actual_pixels, WIDTH, HEIGHT, -WIDTH/4, -HEIGHT/4, WIDTH/2, HEIGHT/2, BLUE_COLOR);
+    Olivec_Canvas oc = olivec_make_canvas(actual_pixels, WIDTH, HEIGHT);
+    olivec_fill(oc, BACKGROUND_COLOR);
+    olivec_rect(oc, WIDTH/2 - WIDTH/8, HEIGHT/2 - HEIGHT/8, WIDTH/4, HEIGHT/4, RED_COLOR);
+    olivec_rect(oc, WIDTH - 1, HEIGHT - 1, -WIDTH/2, -HEIGHT/2, GREEN_COLOR);
+    olivec_rect(oc, -WIDTH/4, -HEIGHT/4, WIDTH/2, HEIGHT/2, BLUE_COLOR);
 }
 
 void test_fill_circle(void)
 {
-    olivec_fill(actual_pixels, WIDTH, HEIGHT, BACKGROUND_COLOR);
-    olivec_fill_circle(actual_pixels, WIDTH, HEIGHT, 0, 0, WIDTH/2, RED_COLOR);
-    olivec_fill_circle(actual_pixels, WIDTH, HEIGHT, WIDTH/2, HEIGHT/2, WIDTH/4, BLUE_COLOR);
-    olivec_fill_circle(actual_pixels, WIDTH, HEIGHT, WIDTH*3/4, HEIGHT*3/4, -WIDTH/4, GREEN_COLOR);
+    Olivec_Canvas oc = olivec_make_canvas(actual_pixels, WIDTH, HEIGHT);
+    olivec_fill(oc, BACKGROUND_COLOR);
+    olivec_circle(oc, 0, 0, WIDTH/2, RED_COLOR);
+    olivec_circle(oc, WIDTH/2, HEIGHT/2, WIDTH/4, BLUE_COLOR);
+    olivec_circle(oc, WIDTH*3/4, HEIGHT*3/4, -WIDTH/4, GREEN_COLOR);
 }
 
 void test_draw_line(void)
 {
-    olivec_fill(actual_pixels, WIDTH, HEIGHT, BACKGROUND_COLOR);
-    olivec_draw_line(actual_pixels, WIDTH, HEIGHT, 0, 0, WIDTH, HEIGHT, RED_COLOR);
-    olivec_draw_line(actual_pixels, WIDTH, HEIGHT, WIDTH, 0, 0, HEIGHT, BLUE_COLOR);
-    olivec_draw_line(actual_pixels, WIDTH, HEIGHT, WIDTH/2, 0, WIDTH/2, HEIGHT, GREEN_COLOR);
+    Olivec_Canvas oc = olivec_make_canvas(actual_pixels, WIDTH, HEIGHT);
+    olivec_fill(oc, BACKGROUND_COLOR);
+    olivec_line(oc, 0, 0, WIDTH, HEIGHT, RED_COLOR);
+    olivec_line(oc, WIDTH, 0, 0, HEIGHT, BLUE_COLOR);
+    olivec_line(oc, WIDTH/2, 0, WIDTH/2, HEIGHT, GREEN_COLOR);
 }
 
 void test_fill_triangle(void)
 {
-    olivec_fill(actual_pixels, WIDTH, HEIGHT, BACKGROUND_COLOR);
+    Olivec_Canvas oc = olivec_make_canvas(actual_pixels, WIDTH, HEIGHT);
+
+    olivec_fill(oc, BACKGROUND_COLOR);
 
     {
-        int x1 = WIDTH/2, y1 = HEIGHT/8;
-        int x2 = WIDTH/8, y2 = HEIGHT/2;
+        int x1 = WIDTH/2,   y1 = HEIGHT/8;
+        int x2 = WIDTH/8,   y2 = HEIGHT/2;
         int x3 = WIDTH*7/8, y3 = HEIGHT*7/8;
-        olivec_fill_triangle(actual_pixels, WIDTH, HEIGHT, x1, y1, x2, y2, x3, y3, RED_COLOR);
+        olivec_triangle(oc, x1, y1, x2, y2, x3, y3, RED_COLOR);
     }
 
     {
-        int x1 = WIDTH/2, y1 = HEIGHT*2/8;
+        int x1 = WIDTH/2,   y1 = HEIGHT*2/8;
         int x2 = WIDTH*2/8, y2 = HEIGHT/2;
         int x3 = WIDTH*6/8, y3 = HEIGHT/2;
-        olivec_fill_triangle(actual_pixels, WIDTH, HEIGHT, x1, y1, x2, y2, x3, y3, GREEN_COLOR);
+        olivec_triangle(oc, x1, y1, x2, y2, x3, y3, GREEN_COLOR);
     }
 
     {
-        int x1 = WIDTH/8, y1 = HEIGHT/8;
-        int x2 = WIDTH/8, y2 = HEIGHT*3/8;
+        int x1 = WIDTH/8,   y1 = HEIGHT/8;
+        int x2 = WIDTH/8,   y2 = HEIGHT*3/8;
         int x3 = WIDTH*3/8, y3 = HEIGHT*3/8;
-        olivec_fill_triangle(actual_pixels, WIDTH, HEIGHT, x1, y1, x2, y2, x3, y3, BLUE_COLOR);
+        olivec_triangle(oc, x1, y1, x2, y2, x3, y3, BLUE_COLOR);
     }
 }
 
 void test_alpha_blending(void)
 {
-    olivec_fill(actual_pixels, WIDTH, HEIGHT, BACKGROUND_COLOR);
-    olivec_fill_rect(actual_pixels, WIDTH, HEIGHT, 0, 0, WIDTH*3/4, HEIGHT*3/4, RED_COLOR);
-    olivec_fill_rect(actual_pixels, WIDTH, HEIGHT, WIDTH-1, HEIGHT-1, -WIDTH*3/4, -HEIGHT*3/4, 0x5520AA20);
-    olivec_fill_circle(actual_pixels, WIDTH, HEIGHT, WIDTH/2, HEIGHT/2, WIDTH/4, 0xBBAA2020);
-    olivec_fill_triangle(actual_pixels, WIDTH, HEIGHT,
-                         0,     HEIGHT,
-                         WIDTH, HEIGHT,
-                         WIDTH/2, 0,
-                         0xBB20AAAA);
+    Olivec_Canvas oc = olivec_make_canvas(actual_pixels, WIDTH, HEIGHT);
+    olivec_fill(oc, BACKGROUND_COLOR);
+    olivec_rect(oc, 0, 0, WIDTH*3/4, HEIGHT*3/4, RED_COLOR);
+    olivec_rect(oc, WIDTH-1, HEIGHT-1, -WIDTH*3/4, -HEIGHT*3/4, 0x5520AA20);
+    olivec_circle(oc, WIDTH/2, HEIGHT/2, WIDTH/4, 0xBBAA2020);
+    olivec_triangle(oc, 0, HEIGHT, WIDTH, HEIGHT, WIDTH/2, 0, 0xBB20AAAA);
 }
 
 Test_Case test_cases[] = {

+ 2 - 1
test/.gitignore

@@ -1 +1,2 @@
-*_failure.png
+*_actual.png
+*_diff.png

BIN
test/test_alpha_blending_expected.png