From c7e2625ed73d07ba9fd0ace798311bf0eed8111a Mon Sep 17 00:00:00 2001 From: Mitya Selivanov Date: Thu, 16 Jan 2025 08:42:53 +0100 Subject: Refactor --- graphics.c | 417 +++++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 238 insertions(+), 179 deletions(-) (limited to 'graphics.c') diff --git a/graphics.c b/graphics.c index 40052ad..1d4e724 100644 --- a/graphics.c +++ b/graphics.c @@ -26,6 +26,7 @@ typedef struct { i64 width; i64 height; + i64 line_size; vec4_f32 *pixels; } Pixel_Buffer; @@ -66,14 +67,14 @@ b8 triangle_contains (f64 x0, f64 y0, f64 x1, f64 y1, f64 x2, f64 y2, f64 px, f6 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); -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); -void fill_triangle (Brush brush, f64 x0, f64 y0, f64 x1, f64 y1, f64 x2, f64 y2); -void fill_ellipse (Brush brush, f64 x, f64 y, f64 width, f64 height); -void fill_line (Brush brush, f64 x0, f64 y0, f64 x1, f64 y1, f64 width); -void draw_text_area (Brush brush, f64 x, f64 y, f64 width, f64 height, f64 max_scale_x, f64 max_scale_y, i64 num_chars, c32 *text); -void draw_selection_cursor(Brush brush, f64 x, f64 y, f64 width, f64 height, f64 max_scale_x, f64 max_scale_y, i64 cursor, i64 selection, i64 num_chars, c32 *text); +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); +void fill_triangle (Brush brush, f64 x0, f64 y0, f64 x1, f64 y1, f64 x2, f64 y2); +void fill_ellipse (Brush brush, f64 x, f64 y, f64 width, f64 height); +void fill_line (Brush brush, f64 x0, f64 y0, f64 x1, f64 y1, f64 width); +void draw_text_area (Brush brush, f64 x, f64 y, f64 width, f64 height, f64 max_scale_x, f64 max_scale_y, i64 num_chars, c32 *text); +void draw_text_cursor(Brush brush, f64 x, f64 y, f64 width, f64 height, f64 max_scale_x, f64 max_scale_y, i64 cursor, i64 selection, i64 num_chars, c32 *text); #endif // GRAPHICS_HEADER_GUARD_ @@ -109,9 +110,11 @@ 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, }; -#define CHAR_NUM_BITS_X_ 6 -#define CHAR_NUM_BITS_Y_ 7 -#define CHAR_NUM_BITS_ (CHAR_NUM_BITS_X_ * CHAR_NUM_BITS_Y_) +enum { + CHAR_NUM_BITS_X_ = 6, + CHAR_NUM_BITS_Y_ = 7, + CHAR_NUM_BITS_ = CHAR_NUM_BITS_X_ * CHAR_NUM_BITS_Y_, +}; static i64 char_column_offset_(c32 c, i64 column_index) { if (column_index < 0 || column_index >= CHAR_NUM_BITS_X_) @@ -274,14 +277,12 @@ static void brush_defaults_(Brush *b) { 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) { - if (text == NULL) - return; - - brush_defaults_(&brush); - x_ = brush.position.x + x_ * brush.scale.x; y_ = brush.position.y + y_ * brush.scale.y; @@ -353,6 +354,169 @@ static f64 gamma_re_(f64 x) { 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 + }); + + u32 src = rgb_u32_from_f32((vec3_f32) { + .x = brush.color.x, + .y = brush.color.y, + .z = brush.color.z + }); + + vec3_f32 c = rgb_f32_from_u32(dst ^ src); + + brush.buffer.pixels[n] = (vec4_f32) { + .x = c.x, + .y = c.y, + .z = c.z, + .w = 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); +} + // ================================================================ vec3_f32 gamma_apply(vec3_f32 rgb) { @@ -557,41 +721,12 @@ void put_pixel(Brush brush, i64 x, i64 y) { if (x < 0 || x >= brush.buffer.width || y < 0 || y >= brush.buffer.height) return; - i64 n = y * brush.buffer.width + x; - - if (brush.xor_color) { - 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 - }); - - u32 src = rgb_u32_from_f32((vec3_f32) { - .x = brush.color.x, - .y = brush.color.y, - .z = brush.color.z - }); - - vec3_f32 c = rgb_f32_from_u32(dst ^ src); - - brush.buffer.pixels[n] = (vec4_f32) { - .x = c.x, - .y = c.y, - .z = c.z, - .w = 1.f, - }; - } else if (brush.alpha) { - 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, - }; - } else - brush.buffer.pixels[n] = brush.color; + if (brush.xor_color) + put_pixel_xor_(brush, x, y); + else if (brush.alpha) + put_pixel_alpha_(brush, x, y); + else + put_pixel_color_(brush, x, y); } void draw_pixels(Brush brush, f64 x, f64 y, f64 width, f64 height, Pixel_Buffer src) { @@ -622,134 +757,46 @@ void draw_pixels(Brush brush, f64 x, f64 y, f64 width, f64 height, Pixel_Buffer i64 j0 = (i64) floor(y0); i64 j1 = (i64) ceil (y1); + i64 i00 = i0; + i64 j00 = j0; + i64 iz = i1 - i0; + i64 jz = j1 - j0; + + 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) { - i64 src_j = ((j - j0) * src.height) / (j1 - j0); + i64 src_j = ((j - j00) * src.height) / jz; for (i64 i = i0; i < i1; ++i) { - i64 src_i = ((i - i0) * src.width) / (i1 - i0); - brush.color = src.pixels[src_j * src.width + src_i]; + i64 src_i = ((i - i00) * src.width) / iz; + brush.color = src.pixels[src_j * src.line_size + src_i]; put_pixel(brush, i, j); } } } void fill_rectangle(Brush brush, f64 x, f64 y, f64 width, f64 height) { - brush_defaults_(&brush); - - 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 (width < EPSILON || height < EPSILON) + return; - for (i64 j = j0; j < j1; ++j) - for (i64 i = i0; i < i1; ++i) - put_pixel(brush, i, j); - } + brush_defaults_(&brush); + fill_rectangle_(brush, x, y, width, height); } void fill_triangle(Brush brush, f64 x0, f64 y0, f64 x1, f64 y1, f64 x2, f64 y2) { - // FIXME PERF: - // Implement better algorithm. - brush_defaults_(&brush); - - 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 min_x = (i64) floor(min3_(x0, x1, x2)); - i64 min_y = (i64) floor(min3_(y0, y1, y2)); - i64 max_x = (i64) ceil (max3_(x0, x1, x2)); - i64 max_y = (i64) ceil (max3_(y0, y1, y2)); - - for (i64 j = min_y; j <= max_y; ++j) - for (i64 i = min_x; i <= max_x; ++i) - if (triangle_contains(x0, y0, x1, y1, x2, y2, (f64) i, (f64) j)) - put_pixel(brush, i, j); + fill_triangle_(brush, x0, y0, x1, y1, x2, y2); } void fill_ellipse(Brush brush, f64 x, f64 y, f64 width, f64 height) { // FIXME PERF: // Implement better algorithm. + if (width < EPSILON || height < EPSILON) + return; + brush_defaults_(&brush); f64 x0, y0, x1, y1; @@ -775,37 +822,47 @@ void fill_ellipse(Brush brush, f64 x, f64 y, f64 width, f64 height) { 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) { + for (i64 i = i0; i < i1; ++i) if (ellipse_contains(x0, y0, x1 - x0, y1 - y0, (f64) i, (f64) j)) put_pixel(brush, i, j); - } } void fill_line(Brush brush, f64 x0, f64 y0, f64 x1, f64 y1, f64 width) { + if (width < EPSILON) + return; + f64 dx = x1 - x0; f64 dy = y1 - y0; // Tangent // f64 tx = -dy; - f64 ty = dx; + f64 ty = dx; f64 tl = sqrt(tx * tx + ty * ty); - if (tl >= EPSILON) { - tx /= tl; - ty /= tl; - } - tx *= width * .5; - ty *= width * .5; - 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); + if (tl < EPSILON) + return; + + tx *= width / (tl * 2.0); + ty *= width / (tl * 2.0); + + 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); } void draw_text_area(Brush brush, f64 x, f64 y, f64 width, f64 height, f64 max_scale_x, f64 max_scale_y, i64 num_chars, c32 *text) { - if (max_scale_x < EPSILON || max_scale_y < EPSILON) + if (text == NULL || num_chars <= 0 || max_scale_x < EPSILON || max_scale_y < EPSILON) return; + brush_defaults_(&brush); + i64 num_columns = enum_text_columns_(num_chars, text); i64 num_rows = enum_text_rows_(num_chars, text); @@ -823,7 +880,7 @@ void draw_text_area(Brush brush, f64 x, f64 y, f64 width, f64 height, f64 max_sc draw_text_(brush, x, y, kx, ky, num_chars, text); } -void draw_selection_cursor(Brush brush, f64 x, f64 y, f64 width, f64 height, f64 max_scale_x, f64 max_scale_y, i64 cursor, i64 selection, i64 num_chars, c32 *text) { +void draw_text_cursor(Brush brush, f64 x, f64 y, f64 width, f64 height, f64 max_scale_x, f64 max_scale_y, i64 cursor, i64 selection, i64 num_chars, c32 *text) { if (max_scale_x < EPSILON || max_scale_y < EPSILON) return; @@ -843,6 +900,8 @@ void draw_selection_cursor(Brush brush, f64 x, f64 y, f64 width, f64 height, f64 kx = k * max_scale_x; ky = k * max_scale_y; + brush_defaults_(&brush); + if (selection != 0) { i64 selection_x, selection_y; @@ -857,7 +916,7 @@ void draw_selection_cursor(Brush brush, f64 x, f64 y, f64 width, f64 height, f64 } if (cursor_y == selection_y) - fill_rectangle( + fill_rectangle_( brush, x + kx * cursor_x, y + ky * cursor_y - ky * (CHAR_NUM_BITS_Y_ + 1), @@ -865,7 +924,7 @@ void draw_selection_cursor(Brush brush, f64 x, f64 y, f64 width, f64 height, f64 ky * (CHAR_NUM_BITS_Y_ + 1) ); else { - fill_rectangle( + fill_rectangle_( brush, x + kx * cursor_x, y + ky * cursor_y - ky * (CHAR_NUM_BITS_Y_ + 1), @@ -873,14 +932,14 @@ void draw_selection_cursor(Brush brush, f64 x, f64 y, f64 width, f64 height, f64 ky * (CHAR_NUM_BITS_Y_ + 1) ); for (i64 j = cursor_y + CHAR_NUM_BITS_Y_ + 1; j < selection_y; j += CHAR_NUM_BITS_Y_ + 1) - fill_rectangle( + fill_rectangle_( brush, x, y + ky * j - ky * (CHAR_NUM_BITS_Y_ + 1), kx * num_columns, ky * (CHAR_NUM_BITS_Y_ + 1) ); - fill_rectangle( + fill_rectangle_( brush, x, y + ky * selection_y - ky * (CHAR_NUM_BITS_Y_ + 1), @@ -889,7 +948,7 @@ void draw_selection_cursor(Brush brush, f64 x, f64 y, f64 width, f64 height, f64 ); } } else - fill_rectangle( + fill_rectangle_( brush, x + kx * cursor_x, y + ky * cursor_y - ky * CHAR_NUM_BITS_Y_, @@ -899,4 +958,4 @@ void draw_selection_cursor(Brush brush, f64 x, f64 y, f64 width, f64 height, f64 } #endif // GRAPHICS_IMPL_GUARD_ -#endif // GRAPHICS_HEADER +#endif // GRAPHICS_HEADER -- cgit v1.2.3