|
@@ -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_
|