summaryrefslogtreecommitdiff
path: root/graphics.c
diff options
context:
space:
mode:
Diffstat (limited to 'graphics.c')
-rw-r--r--graphics.c572
1 files changed, 352 insertions, 220 deletions
diff --git a/graphics.c b/graphics.c
index 1d4e724..54519b5 100644
--- a/graphics.c
+++ b/graphics.c
@@ -18,6 +18,17 @@
#include "reduced_system_layer.c"
// ================================================================
+//
+// Vector math
+//
+// ================================================================
+
+vec4_f32 vec4_from_vec3_f32(vec3_f32 v, f32 w);
+vec3_f32 vec3_from_vec4_f32(vec4_f32 v);
+vec3_f32 vec3_f32_lerp (vec3_f32 a, vec3_f32 b, f32 t);
+vec4_f32 vec4_f32_lerp (vec4_f32 a, vec4_f32 b, f32 t);
+
+// ================================================================
#ifndef EPSILON
#define EPSILON (1e-9)
@@ -40,8 +51,6 @@ typedef struct {
vec4_f32 color;
} Brush;
-vec3_f32 gamma_apply (vec3_f32 rgb);
-vec3_f32 gamma_revert(vec3_f32 rgb);
vec3_f32 lab_from_rgb(vec3_f32 rgb);
vec3_f32 rgb_from_lab(vec3_f32 lab);
vec3_f32 lab_from_lch(vec3_f32 lch);
@@ -49,8 +58,17 @@ vec3_f32 lch_from_lab(vec3_f32 lab);
vec3_f32 rgb_from_lch(vec3_f32 lch);
vec3_f32 lch_from_rgb(vec3_f32 rgb);
-vec4_f32 with_alpha(vec3_f32 color, f32 alpha);
-vec3_f32 without_alpha(vec4_f32 color);
+vec3_f32 rgb_gamma_add (vec3_f32 rgb);
+vec3_f32 rgb_gamma_remove (vec3_f32 rgb);
+vec4_f32 rgba_gamma_add (vec4_f32 rgb);
+vec4_f32 rgba_gamma_remove(vec4_f32 rgb);
+
+vec3_f32 rgb_gamma_lerp (vec3_f32 a, vec3_f32 b, f32 t);
+vec4_f32 rgba_gamma_lerp(vec4_f32 a, vec4_f32 b, f32 t);
+vec3_f32 rgb_mix (vec3_f32 a, vec3_f32 b, f32 t);
+vec4_f32 rgba_mix (vec4_f32 a, vec4_f32 b, f32 t);
+vec3_f32 lch_mix (vec3_f32 a, vec3_f32 b, f32 t);
+vec3_f32 lch_lerp (vec3_f32 a, vec3_f32 b, f32 t);
u32 rgb_u32_from_f32(vec3_f32 color);
vec3_f32 rgb_f32_from_u32(u32 color);
@@ -59,14 +77,17 @@ vec4_f32 rgba_f32_from_u32(u32 color);
#define RGB(...) ((Brush) { .color = { __VA_ARGS__, 1.f } })
#define RGBA(...) ((Brush) { .color = { __VA_ARGS__ } })
-#define LCH(...) ((Brush) { .color = with_alpha(gamma_apply(rgb_from_lch((vec3_f32) { __VA_ARGS__ })), 1.f) } })
-#define LCHA(...) ((Brush) { .color = with_alpha(gamma_apply(rgb_from_lch(without_alpha((vec4_f32) { __VA_ARGS__ }))), ((vec4_f32) { __VA_ARGS__ }).w) } })
+#define LCH(...) ((Brush) { .color = vec4_from_vec3_f32(rgb_from_lch((vec3_f32) { __VA_ARGS__ }), 1.f) } })
+#define LCHA(...) ((Brush) { .color = vec4_from_vec3_f32(rgb_from_lch(vec3_from_vec4_f32((vec4_f32) { __VA_ARGS__ })), ((vec4_f32) { __VA_ARGS__ }).w) } })
b8 rectangle_contains(f64 x0, f64 y0, f64 width, f64 height, f64 px, f64 py);
b8 triangle_contains (f64 x0, f64 y0, f64 x1, f64 y1, f64 x2, f64 y2, f64 px, f64 py);
b8 ellipse_contains (f64 x0, f64 y0, f64 width, f64 height, f64 px, f64 py);
b8 line_contains (f64 x0, f64 y0, f64 x1, f64 y1, f64 width, f64 px, f64 py);
+Pixel_Buffer window_pixels(void);
+Pixel_Buffer subimage(Pixel_Buffer image, i64 x, i64 y, i64 width, i64 height);
+
void put_pixel (Brush brush, i64 x, i64 y);
void draw_pixels (Brush brush, f64 x, f64 y, f64 width, f64 height, Pixel_Buffer src);
void fill_rectangle (Brush brush, f64 x, f64 y, f64 width, f64 height);
@@ -84,6 +105,16 @@ void draw_text_cursor(Brush brush, f64 x, f64 y, f64 width, f64 height, f64 max_
#ifndef GRAPHICS_IMPL_GUARD_
#define GRAPHICS_IMPL_GUARD_
+static u64 _bitfont[] = {
+ 0xbc0000000000, 0xc00300000, 0x5fd5040093f24fc9, 0xa00a2c2a1a280105, 0xc000415e6f, 0x400000020be0000, 0x1c38a8400000007d, 0x40002043e1020215, 0x408102000000010, 0x9800000000020002, 0xf913e00000033, 0x53200000207c8800, 0x3654880000099, 0x54b800000f840e00, 0xe953c000001a, 0x953e000000674080, 0x1e54b800000f, 0x490000000000240, 0x88a08000000, 0x20a220050a142850, 0x6520800000, 0x912f801eab260be, 0x800034952bf0001f, 0xc850bf0000921427, 0xf00010a54afc0003, 0xd29427800002142b, 0x840007e1023f0000, 0x7d09100000217e, 0x3f000188a08fc000, 0xc30c0cfc00000810, 0x27803f101013f00f, 0xc244bf0000f214, 0x4bf0002f21427800, 0xc254a480006c24, 0x407c00102fc08100, 0xf208080f0000fa0, 0x531007d81c607c0, 0xc208288c031141, 0x83fc00046954b10, 0x180e03000000, 0x41040000000ff04, 0x8102040810000404, 0x2a54600000000101, 0x309123e0000e, 0xc912180000a22447, 0x8000062a54700007, 0xe52a4300000029f0, 0xa0000602043e0001, 0x1d48000002074, 0x1f000003610f8000, 0x13e04f800000010, 0x470000780813e00f, 0x184893e0000e224, 0x23e0001f12243000, 0x82a54100000008, 0x40780000009f0200, 0xe208080e0001f20, 0xa22007981860780, 0x82082888022282, 0x16c200004ca95320, 0x7f000004, 0x408200000086d04, 0x8204,
+};
+
+enum {
+ CHAR_NUM_BITS_X_ = 6,
+ CHAR_NUM_BITS_Y_ = 7,
+ CHAR_NUM_BITS_ = CHAR_NUM_BITS_X_ * CHAR_NUM_BITS_Y_,
+};
+
static f64 min3_(f64 a, f64 b, f64 c) {
if (a < b && a < c)
return a;
@@ -106,15 +137,166 @@ static b8 same_sign_(f64 a, f64 b) {
return 1;
}
-static u64 _bitfont[] = {
- 0xbc0000000000, 0xc00300000, 0x5fd5040093f24fc9, 0xa00a2c2a1a280105, 0xc000415e6f, 0x400000020be0000, 0x1c38a8400000007d, 0x40002043e1020215, 0x408102000000010, 0x9800000000020002, 0xf913e00000033, 0x53200000207c8800, 0x3654880000099, 0x54b800000f840e00, 0xe953c000001a, 0x953e000000674080, 0x1e54b800000f, 0x490000000000240, 0x88a08000000, 0x20a220050a142850, 0x6520800000, 0x912f801eab260be, 0x800034952bf0001f, 0xc850bf0000921427, 0xf00010a54afc0003, 0xd29427800002142b, 0x840007e1023f0000, 0x7d09100000217e, 0x3f000188a08fc000, 0xc30c0cfc00000810, 0x27803f101013f00f, 0xc244bf0000f214, 0x4bf0002f21427800, 0xc254a480006c24, 0x407c00102fc08100, 0xf208080f0000fa0, 0x531007d81c607c0, 0xc208288c031141, 0x83fc00046954b10, 0x180e03000000, 0x41040000000ff04, 0x8102040810000404, 0x2a54600000000101, 0x309123e0000e, 0xc912180000a22447, 0x8000062a54700007, 0xe52a4300000029f0, 0xa0000602043e0001, 0x1d48000002074, 0x1f000003610f8000, 0x13e04f800000010, 0x470000780813e00f, 0x184893e0000e224, 0x23e0001f12243000, 0x82a54100000008, 0x40780000009f0200, 0xe208080e0001f20, 0xa22007981860780, 0x82082888022282, 0x16c200004ca95320, 0x7f000004, 0x408200000086d04, 0x8204,
-};
+static f64 gamma_(f64 x) {
+ if (x >= 0.0031308)
+ return 1.055 * pow(x, 1.0 / 2.4) - 0.055;
+ return 12.92 * x;
+}
-enum {
- CHAR_NUM_BITS_X_ = 6,
- CHAR_NUM_BITS_Y_ = 7,
- CHAR_NUM_BITS_ = CHAR_NUM_BITS_X_ * CHAR_NUM_BITS_Y_,
-};
+static f64 gamma_re_(f64 x) {
+ if (x >= 0.04045)
+ return pow((x + 0.055) / 1.055, 2.4);
+ return x / 12.92;
+}
+
+static void put_pixel_xor_(Brush brush, i64 x, i64 y) {
+ i64 n = y * brush.buffer.line_size + x;
+
+ vec3_f32 c = rgb_f32_from_u32(
+ rgb_u32_from_f32(vec3_from_vec4_f32(brush.buffer.pixels[n]))
+ ^ rgb_u32_from_f32(vec3_from_vec4_f32(brush.color))
+ );
+
+ brush.buffer.pixels[n] = vec4_from_vec3_f32(c, 1.f);;
+}
+
+static void put_pixel_alpha_(Brush brush, i64 x, i64 y) {
+ i64 n = y * brush.buffer.line_size + x;
+
+ vec4_f32 dst = brush.buffer.pixels[n];
+ f64 a = brush.color.w;
+
+ brush.buffer.pixels[n] = (vec4_f32) {
+ .x = (f32) (dst.x * (1. - a) + brush.color.x * a),
+ .y = (f32) (dst.y * (1. - a) + brush.color.y * a),
+ .z = (f32) (dst.z * (1. - a) + brush.color.z * a),
+ .w = 1.f,
+ };
+}
+
+static void put_pixel_color_(Brush brush, i64 x, i64 y) {
+ brush.buffer.pixels[y * brush.buffer.line_size + x] = brush.color;
+}
+
+void fill_rectangle_(Brush brush, f64 x, f64 y, f64 width, f64 height) {
+ f64 x0, y0, x1, y1;
+
+ if (brush.scale.x < 0) {
+ x1 = brush.position.x + x * brush.scale.x;
+ x0 = x1 + width * brush.scale.x;
+ } else {
+ x0 = brush.position.x + x * brush.scale.x;
+ x1 = x0 + width * brush.scale.x;
+ }
+
+ if (brush.scale.y < 0) {
+ y1 = brush.position.y + y * brush.scale.y;
+ y0 = y1 + height * brush.scale.y;
+ } else {
+ y0 = brush.position.y + y * brush.scale.y;
+ y1 = y0 + height * brush.scale.y;
+ }
+
+ if (!brush.quick) {
+ i64 i0 = (i64) ceil (x0);
+ i64 i1 = (i64) floor(x1);
+ i64 j0 = (i64) ceil (y0);
+ i64 j1 = (i64) floor(y1);
+
+ f64 kx0 = i0 - x0;
+ f64 kx1 = x1 - i1;
+ f64 ky0 = j0 - y0;
+ f64 ky1 = y1 - j1;
+
+ for (i64 j = j0; j < j1; ++j)
+ for (i64 i = i0; i < i1; ++i)
+ put_pixel(brush, i, j);
+
+ if (i0 > i1) {
+ kx0 *= kx1;
+ kx1 = 0.;
+ }
+
+ if (j0 > j1) {
+ ky0 *= ky1;
+ ky1 = 0.;
+ }
+
+ f64 alpha = brush.alpha ? brush.color.w : 1.;
+ brush.alpha = 1;
+
+ brush.color.w = alpha * gamma_(kx0);
+ for (i64 j = j0; j < j1; ++j)
+ put_pixel(brush, i0 - 1, j);
+
+ brush.color.w = alpha * gamma_(kx1);
+ for (i64 j = j0; j < j1; ++j)
+ put_pixel(brush, i1, j);
+
+ brush.color.w = alpha * gamma_(ky0);
+ for (i64 i = i0; i < i1; ++i)
+ put_pixel(brush, i, j0 - 1);
+
+ brush.color.w = alpha * gamma_(ky1);
+ for (i64 i = i0; i < i1; ++i)
+ put_pixel(brush, i, j1);
+
+ brush.color.w = alpha * gamma_(kx0 * ky0);
+ put_pixel(brush, i0 - 1, j0 - 1);
+
+ brush.color.w = alpha * gamma_(kx1 * ky0);
+ put_pixel(brush, i1, j0 - 1);
+
+ brush.color.w = alpha * gamma_(kx0 * ky1);
+ put_pixel(brush, i0 - 1, j1);
+
+ brush.color.w = alpha * gamma_(kx1 * ky1);
+ put_pixel(brush, i1, j1);
+ } else {
+ i64 i0 = (i64) floor(x0);
+ i64 i1 = (i64) ceil (x1);
+ i64 j0 = (i64) floor(y0);
+ i64 j1 = (i64) ceil (y1);
+
+ if (i0 < 0) i0 = 0;
+ if (i1 > brush.buffer.width) i1 = brush.buffer.width;
+ if (j0 < 0) j0 = 0;
+ if (j1 > brush.buffer.height) j1 = brush.buffer.height;
+
+ for (i64 j = j0; j < j1; ++j)
+ for (i64 i = i0; i < i1; ++i)
+ put_pixel(brush, i, j);
+ }
+}
+
+void fill_triangle_(Brush brush, f64 x0, f64 y0, f64 x1, f64 y1, f64 x2, f64 y2) {
+ // FIXME PERF:
+ // Implement better algorithm.
+
+ x0 = brush.position.x + x0 * brush.scale.x;
+ y0 = brush.position.y + y0 * brush.scale.y;
+
+ x1 = brush.position.x + x1 * brush.scale.x;
+ y1 = brush.position.y + y1 * brush.scale.y;
+
+ x2 = brush.position.x + x2 * brush.scale.x;
+ y2 = brush.position.y + y2 * brush.scale.y;
+
+ i64 i0 = (i64) floor(min3_(x0, x1, x2));
+ i64 j0 = (i64) floor(min3_(y0, y1, y2));
+ i64 i1 = (i64) ceil (max3_(x0, x1, x2)) + 1;
+ i64 j1 = (i64) ceil (max3_(y0, y1, y2)) + 1;
+
+ if (i0 < 0) i0 = 0;
+ if (i1 > brush.buffer.width) i1 = brush.buffer.width;
+ if (j0 < 0) j0 = 0;
+ if (j1 > brush.buffer.height) j1 = brush.buffer.height;
+
+ for (i64 j = j0; j < j1; ++j)
+ for (i64 i = i0; i < i1; ++i)
+ if (triangle_contains(x0, y0, x1, y1, x2, y2, (f64) i, (f64) j))
+ put_pixel(brush, i, j);
+}
static i64 char_column_offset_(c32 c, i64 column_index) {
if (column_index < 0 || column_index >= CHAR_NUM_BITS_X_)
@@ -266,22 +448,6 @@ static i64 enum_text_rows_(i64 num_chars, c32 *text) {
return rows;
}
-static void brush_defaults_(Brush *b) {
- if (g_platform.frame_width > 0) {
- if (b->scale.x == 0.) b->scale.x = ((f64) g_platform.frame_width) / g_platform.real_width;
- if (b->scale.y == 0.) b->scale.y = ((f64) g_platform.frame_height) / g_platform.real_height;
- }
-
- if (b->buffer.pixels == NULL) {
- b->buffer.width = g_platform.frame_width;
- b->buffer.height = g_platform.frame_height;
- b->buffer.pixels = g_platform.pixels;
- }
-
- if (b->buffer.line_size == 0)
- b->buffer.line_size = b->buffer.width;
-}
-
static void draw_text_(Brush brush, f64 x_, f64 y_, f64 scale_x, f64 scale_y, i64 num_chars, c32 *text) {
x_ = brush.position.x + x_ * brush.scale.x;
y_ = brush.position.y + y_ * brush.scale.y;
@@ -320,18 +486,22 @@ static void draw_text_(Brush brush, f64 x_, f64 y_, f64 scale_x, f64 scale_y, i6
i64 j0 = (i64) floor(y);
i64 j1 = (i64) ceil (y + h);
- for (i64 i = i0; i < i1; ++i) {
- if (i < 0) continue;
- if (i >= g_platform.frame_width) break;
+ i64 i00 = i0;
+ i64 j00 = j0;
+ i64 iz = i1 - i0;
+ i64 jz = j1 - j0;
- i64 column = ((i - i0) * num_cols) / (i1 - i0);
+ if (i0 < 0) i0 = 0;
+ if (i1 > brush.buffer.width) i1 = brush.buffer.width;
+ if (j0 < 0) j0 = 0;
+ if (j1 > brush.buffer.height) j1 = brush.buffer.height;
+
+ for (i64 i = i0; i < i1; ++i) {
+ i64 column = ((i - i00) * num_cols) / iz;
i64 offset = char_column_offset_(text[n], column);
for (i64 j = j0; j < j1; ++j) {
- if (j < 0) continue;
- if (j >= g_platform.frame_height) break;
-
- i64 row = ((j - j0) * CHAR_NUM_BITS_Y_) / (j1 - j0);
+ i64 row = ((j - j00) * CHAR_NUM_BITS_Y_) / jz;
if (char_bit_(offset, row))
put_pixel(brush, i, j);
@@ -342,184 +512,60 @@ static void draw_text_(Brush brush, f64 x_, f64 y_, f64 scale_x, f64 scale_y, i6
}
}
-static f64 gamma_(f64 x) {
- if (x >= 0.0031308)
- return 1.055 * pow(x, 1.0 / 2.4) - 0.055;
- return 12.92 * x;
-}
-
-static f64 gamma_re_(f64 x) {
- if (x >= 0.04045)
- return pow((x + 0.055) / 1.055, 2.4);
- return x / 12.92;
-}
-
-static void put_pixel_xor_(Brush brush, i64 x, i64 y) {
- i64 n = y * brush.buffer.line_size + x;
-
- u32 dst = rgb_u32_from_f32((vec3_f32) {
- .x = brush.buffer.pixels[n].x,
- .y = brush.buffer.pixels[n].y,
- .z = brush.buffer.pixels[n].z
- });
+static Brush brush_defaults_(Brush b) {
+ if (g_platform.frame_width > 0) {
+ if (b.scale.x == 0.) b.scale.x = ((f64) g_platform.frame_width) / g_platform.real_width;
+ if (b.scale.y == 0.) b.scale.y = ((f64) g_platform.frame_height) / g_platform.real_height;
+ }
- u32 src = rgb_u32_from_f32((vec3_f32) {
- .x = brush.color.x,
- .y = brush.color.y,
- .z = brush.color.z
- });
+ if (b.buffer.pixels == NULL)
+ b.buffer = window_pixels();
- vec3_f32 c = rgb_f32_from_u32(dst ^ src);
+ if (b.buffer.line_size == 0)
+ b.buffer.line_size = b.buffer.width;
- brush.buffer.pixels[n] = (vec4_f32) {
- .x = c.x,
- .y = c.y,
- .z = c.z,
- .w = 1.f,
- };
+ return b;
}
-static void put_pixel_alpha_(Brush brush, i64 x, i64 y) {
- i64 n = y * brush.buffer.line_size + x;
-
- vec4_f32 dst = brush.buffer.pixels[n];
- f64 a = brush.color.w;
+// ================================================================
- brush.buffer.pixels[n] = (vec4_f32) {
- .x = (f32) (dst.x * (1. - a) + brush.color.x * a),
- .y = (f32) (dst.y * (1. - a) + brush.color.y * a),
- .z = (f32) (dst.z * (1. - a) + brush.color.z * a),
- .w = 1.f,
+vec4_f32 vec4_from_vec3_f32(vec3_f32 v, f32 w) {
+ return (vec4_f32) {
+ .x = v.x,
+ .y = v.y,
+ .z = v.z,
+ .w = w,
};
}
-static void put_pixel_color_(Brush brush, i64 x, i64 y) {
- brush.buffer.pixels[y * brush.buffer.line_size + x] = brush.color;
+vec3_f32 vec3_from_vec4_f32(vec4_f32 v) {
+ return (vec3_f32) {
+ .x = v.x,
+ .y = v.y,
+ .z = v.z,
+ };
}
-void fill_rectangle_(Brush brush, f64 x, f64 y, f64 width, f64 height) {
- f64 x0, y0, x1, y1;
-
- if (brush.scale.x < 0) {
- x1 = brush.position.x + x * brush.scale.x;
- x0 = x1 + width * brush.scale.x;
- } else {
- x0 = brush.position.x + x * brush.scale.x;
- x1 = x0 + width * brush.scale.x;
- }
-
- if (brush.scale.y < 0) {
- y1 = brush.position.y + y * brush.scale.y;
- y0 = y1 + height * brush.scale.y;
- } else {
- y0 = brush.position.y + y * brush.scale.y;
- y1 = y0 + height * brush.scale.y;
- }
-
- if (!brush.quick) {
- i64 i0 = (i64) ceil (x0);
- i64 i1 = (i64) floor(x1);
- i64 j0 = (i64) ceil (y0);
- i64 j1 = (i64) floor(y1);
-
- f64 kx0 = i0 - x0;
- f64 kx1 = x1 - i1;
- f64 ky0 = j0 - y0;
- f64 ky1 = y1 - j1;
-
- for (i64 j = j0; j < j1; ++j)
- for (i64 i = i0; i < i1; ++i)
- put_pixel(brush, i, j);
-
- if (i0 > i1) {
- kx0 *= kx1;
- kx1 = 0.;
- }
-
- if (j0 > j1) {
- ky0 *= ky1;
- ky1 = 0.;
- }
-
- f64 alpha = brush.alpha ? brush.color.w : 1.;
- brush.alpha = 1;
-
- brush.color.w = alpha * gamma_(kx0);
- for (i64 j = j0; j < j1; ++j)
- put_pixel(brush, i0 - 1, j);
-
- brush.color.w = alpha * gamma_(kx1);
- for (i64 j = j0; j < j1; ++j)
- put_pixel(brush, i1, j);
-
- brush.color.w = alpha * gamma_(ky0);
- for (i64 i = i0; i < i1; ++i)
- put_pixel(brush, i, j0 - 1);
-
- brush.color.w = alpha * gamma_(ky1);
- for (i64 i = i0; i < i1; ++i)
- put_pixel(brush, i, j1);
-
- brush.color.w = alpha * gamma_(kx0 * ky0);
- put_pixel(brush, i0 - 1, j0 - 1);
-
- brush.color.w = alpha * gamma_(kx1 * ky0);
- put_pixel(brush, i1, j0 - 1);
-
- brush.color.w = alpha * gamma_(kx0 * ky1);
- put_pixel(brush, i0 - 1, j1);
-
- brush.color.w = alpha * gamma_(kx1 * ky1);
- put_pixel(brush, i1, j1);
- } else {
- i64 i0 = (i64) floor(x0);
- i64 i1 = (i64) ceil (x1);
- i64 j0 = (i64) floor(y0);
- i64 j1 = (i64) ceil (y1);
-
- if (i0 < 0) i0 = 0;
- if (i1 > brush.buffer.width) i1 = brush.buffer.width;
- if (j0 < 0) j0 = 0;
- if (j1 > brush.buffer.height) j1 = brush.buffer.height;
-
- for (i64 j = j0; j < j1; ++j)
- for (i64 i = i0; i < i1; ++i)
- put_pixel(brush, i, j);
- }
+vec3_f32 vec3_f32_lerp(vec3_f32 a, vec3_f32 b, f32 t) {
+ return (vec3_f32) {
+ .x = a.x + (b.x - a.x) * t,
+ .y = a.y + (b.y - a.y) * t,
+ .z = a.z + (b.z - a.z) * t,
+ };
}
-void fill_triangle_(Brush brush, f64 x0, f64 y0, f64 x1, f64 y1, f64 x2, f64 y2) {
- // FIXME PERF:
- // Implement better algorithm.
-
- x0 = brush.position.x + x0 * brush.scale.x;
- y0 = brush.position.y + y0 * brush.scale.y;
-
- x1 = brush.position.x + x1 * brush.scale.x;
- y1 = brush.position.y + y1 * brush.scale.y;
-
- x2 = brush.position.x + x2 * brush.scale.x;
- y2 = brush.position.y + y2 * brush.scale.y;
-
- i64 i0 = (i64) floor(min3_(x0, x1, x2));
- i64 j0 = (i64) floor(min3_(y0, y1, y2));
- i64 i1 = (i64) ceil (max3_(x0, x1, x2)) + 1;
- i64 j1 = (i64) ceil (max3_(y0, y1, y2)) + 1;
-
- if (i0 < 0) i0 = 0;
- if (i1 > brush.buffer.width) i1 = brush.buffer.width;
- if (j0 < 0) j0 = 0;
- if (j1 > brush.buffer.height) j1 = brush.buffer.height;
-
- for (i64 j = j0; j < j1; ++j)
- for (i64 i = i0; i < i1; ++i)
- if (triangle_contains(x0, y0, x1, y1, x2, y2, (f64) i, (f64) j))
- put_pixel(brush, i, j);
+vec4_f32 vec4_f32_lerp(vec4_f32 a, vec4_f32 b, f32 t) {
+ return (vec4_f32) {
+ .x = a.x + (b.x - a.x) * t,
+ .y = a.y + (b.y - a.y) * t,
+ .z = a.z + (b.z - a.z) * t,
+ .w = a.w + (b.w - a.w) * t,
+ };
}
// ================================================================
-vec3_f32 gamma_apply(vec3_f32 rgb) {
+vec3_f32 rgb_gamma_add(vec3_f32 rgb) {
return (vec3_f32) {
.x = (f32) gamma_(rgb.x),
.y = (f32) gamma_(rgb.y),
@@ -527,7 +573,7 @@ vec3_f32 gamma_apply(vec3_f32 rgb) {
};
}
-vec3_f32 gamma_revert(vec3_f32 rgb) {
+vec3_f32 rgb_gamma_remove(vec3_f32 rgb) {
return (vec3_f32) {
.x = (f32) gamma_re_(rgb.x),
.y = (f32) gamma_re_(rgb.y),
@@ -535,6 +581,24 @@ vec3_f32 gamma_revert(vec3_f32 rgb) {
};
}
+vec4_f32 rgba_gamma_add(vec4_f32 rgba) {
+ return (vec4_f32) {
+ .x = (f32) gamma_(rgba.x),
+ .y = (f32) gamma_(rgba.y),
+ .z = (f32) gamma_(rgba.z),
+ .w = (f32) gamma_(rgba.w),
+ };
+}
+
+vec4_f32 rgba_gamma_remove(vec4_f32 rgba) {
+ return (vec4_f32) {
+ .x = (f32) gamma_re_(rgba.x),
+ .y = (f32) gamma_re_(rgba.y),
+ .z = (f32) gamma_re_(rgba.z),
+ .w = (f32) gamma_re_(rgba.w),
+ };
+}
+
vec3_f32 lab_from_rgb(vec3_f32 rgb) {
f64 l = 0.4122214708 * rgb.x + 0.5363325363 * rgb.y + 0.0514459929 * rgb.z;
f64 m = 0.2119034982 * rgb.x + 0.6806995451 * rgb.y + 0.1073969566 * rgb.z;
@@ -594,20 +658,62 @@ vec3_f32 lch_from_rgb(vec3_f32 rgb) {
return lch_from_lab(lab_from_rgb(rgb));
}
-vec4_f32 with_alpha(vec3_f32 color, f32 alpha) {
- return (vec4_f32) {
- .x = color.x,
- .y = color.y,
- .z = color.z,
- .w = alpha,
+vec3_f32 rgb_gamma_lerp (vec3_f32 a, vec3_f32 b, f32 t) {
+ return rgb_gamma_add(vec3_f32_lerp(
+ rgb_gamma_remove(a),
+ rgb_gamma_remove(b),
+ t
+ ));
+}
+
+vec4_f32 rgba_gamma_lerp(vec4_f32 a, vec4_f32 b, f32 t) {
+ return rgba_gamma_add(vec4_f32_lerp(
+ rgba_gamma_remove(a),
+ rgba_gamma_remove(b),
+ t
+ ));
+}
+
+vec3_f32 rgb_mix(vec3_f32 a, vec3_f32 b, f32 t) {
+ vec3_f32 a_lch = lch_from_rgb(a);
+ vec3_f32 b_lch = lch_from_rgb(b);
+
+ return rgb_from_lch((vec3_f32) {
+ a_lch.x + (b_lch.x - a_lch.x) * t,
+ a_lch.y + (b_lch.y - a_lch.y) * t,
+ lch_from_rgb(rgb_gamma_lerp(a, b, t)).z,
+ });
+}
+
+vec4_f32 rgba_mix(vec4_f32 a, vec4_f32 b, f32 t) {
+ vec3_f32 a_lch = lch_from_rgb(vec3_from_vec4_f32(a));
+ vec3_f32 b_lch = lch_from_rgb(vec3_from_vec4_f32(b));
+ vec4_f32 ab = rgba_gamma_lerp(a, b, t);
+
+ return vec4_from_vec3_f32(rgb_from_lch((vec3_f32) {
+ a_lch.x + (b_lch.x - a_lch.x) * t,
+ a_lch.y + (b_lch.y - a_lch.y) * t,
+ lch_from_rgb(vec3_from_vec4_f32(ab)).z,
+ }), ab.w);
+}
+
+vec3_f32 lch_mix(vec3_f32 a, vec3_f32 b, f32 t) {
+ return (vec3_f32) {
+ a.x + (b.x - a.x) * t,
+ a.y + (b.y - a.y) * t,
+ lch_from_rgb(rgb_gamma_lerp(rgb_from_lch(a), rgb_from_lch(b), t)).z,
};
}
-vec3_f32 without_alpha(vec4_f32 color) {
+vec3_f32 lch_lerp(vec3_f32 a, vec3_f32 b, f32 t) {
+ f32 delta_hue = b.z - a.z;
+ if (delta_hue > M_PI) delta_hue -= M_PI * 2;
+ if (delta_hue < -M_PI) delta_hue += M_PI * 2;
+
return (vec3_f32) {
- .x = color.x,
- .y = color.y,
- .z = color.z,
+ .x = a.x + (b.x - a.x) * t,
+ .y = a.y + (b.y - a.y) * t,
+ .z = a.z + delta_hue * t,
};
}
@@ -715,8 +821,36 @@ b8 line_contains(f64 x0, f64 y0, f64 x1, f64 y1, f64 width, f64 px, f64 py) {
|| triangle_contains(x0 - tx, y0 - ty, x1 + tx, y1 + ty, x1 - tx, y1 - ty, px, py);
}
+Pixel_Buffer window_pixels(void) {
+ return (Pixel_Buffer) {
+ .width = g_platform.frame_width,
+ .height = g_platform.frame_height,
+ .line_size = g_platform.frame_width,
+ .pixels = g_platform.pixels,
+ };
+}
+
+Pixel_Buffer subimage(Pixel_Buffer image, i64 x, i64 y, i64 width, i64 height) {
+ if (x < 0 || y < 0 || width < 0 || height < 0 || x + width > image.width || y + height > image.height || image.pixels == NULL)
+ return (Pixel_Buffer) {
+ .width = 0,
+ .height = 0,
+ .line_size = 0,
+
+ // Set pixels pointer to prevent default initialization.
+ .pixels = g_platform.pixels,
+ };
+
+ return (Pixel_Buffer) {
+ .width = width,
+ .height = height,
+ .line_size = image.line_size,
+ .pixels = image.pixels + (y * image.line_size + x),
+ };
+}
+
void put_pixel(Brush brush, i64 x, i64 y) {
- brush_defaults_(&brush);
+ brush = brush_defaults_(brush);
if (x < 0 || x >= brush.buffer.width || y < 0 || y >= brush.buffer.height)
return;
@@ -732,7 +866,7 @@ void put_pixel(Brush brush, i64 x, i64 y) {
void draw_pixels(Brush brush, f64 x, f64 y, f64 width, f64 height, Pixel_Buffer src) {
// FIXME PERF
- brush_defaults_(&brush);
+ brush = brush_defaults_(brush);
f64 x0, y0, x1, y1;
@@ -781,13 +915,11 @@ void fill_rectangle(Brush brush, f64 x, f64 y, f64 width, f64 height) {
if (width < EPSILON || height < EPSILON)
return;
- brush_defaults_(&brush);
- fill_rectangle_(brush, x, y, width, height);
+ fill_rectangle_(brush_defaults_(brush), x, y, width, height);
}
void fill_triangle(Brush brush, f64 x0, f64 y0, f64 x1, f64 y1, f64 x2, f64 y2) {
- brush_defaults_(&brush);
- fill_triangle_(brush, x0, y0, x1, y1, x2, y2);
+ fill_triangle_(brush_defaults_(brush), x0, y0, x1, y1, x2, y2);
}
void fill_ellipse(Brush brush, f64 x, f64 y, f64 width, f64 height) {
@@ -797,7 +929,7 @@ void fill_ellipse(Brush brush, f64 x, f64 y, f64 width, f64 height) {
if (width < EPSILON || height < EPSILON)
return;
- brush_defaults_(&brush);
+ brush = brush_defaults_(brush);
f64 x0, y0, x1, y1;
@@ -852,7 +984,7 @@ void fill_line(Brush brush, f64 x0, f64 y0, f64 x1, f64 y1, f64 width) {
tx *= width / (tl * 2.0);
ty *= width / (tl * 2.0);
- brush_defaults_(&brush);
+ brush = brush_defaults_(brush);
fill_triangle_(brush, x0 - tx, y0 - ty, x0 + tx, y0 + ty, x1 + tx, y1 + ty);
fill_triangle_(brush, x0 - tx, y0 - ty, x1 + tx, y1 + ty, x1 - tx, y1 - ty);
}
@@ -861,7 +993,7 @@ void draw_text_area(Brush brush, f64 x, f64 y, f64 width, f64 height, f64 max_sc
if (text == NULL || num_chars <= 0 || max_scale_x < EPSILON || max_scale_y < EPSILON)
return;
- brush_defaults_(&brush);
+ brush = brush_defaults_(brush);
i64 num_columns = enum_text_columns_(num_chars, text);
i64 num_rows = enum_text_rows_(num_chars, text);
@@ -900,7 +1032,7 @@ void draw_text_cursor(Brush brush, f64 x, f64 y, f64 width, f64 height, f64 max_
kx = k * max_scale_x;
ky = k * max_scale_y;
- brush_defaults_(&brush);
+ brush = brush_defaults_(brush);
if (selection != 0) {
i64 selection_x, selection_y;