From c67b45d485bee3656287d6a77532d7c53ac17325 Mon Sep 17 00:00:00 2001 From: Mitya Selivanov Date: Thu, 16 Jan 2025 16:55:42 +0100 Subject: Graphics colors; Refactor --- graphics.c | 572 ++++++++++++++++++++++++++++++------------------- reduced_system_layer.c | 1 + 2 files changed, 353 insertions(+), 220 deletions(-) diff --git a/graphics.c b/graphics.c index 1d4e724..54519b5 100644 --- a/graphics.c +++ b/graphics.c @@ -17,6 +17,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 @@ -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; diff --git a/reduced_system_layer.c b/reduced_system_layer.c index 634b13e..8b35ea3 100644 --- a/reduced_system_layer.c +++ b/reduced_system_layer.c @@ -41,6 +41,7 @@ // - UI // - Icons // - Vector math +// - CMYK color // - Test suite // - System // - Window -- cgit v1.2.3