diff options
| author | Aldrik Ramaekers <aldrikboy@gmail.com> | 2024-11-23 22:33:43 +0100 |
|---|---|---|
| committer | Aldrik Ramaekers <aldrikboy@gmail.com> | 2024-11-23 22:33:43 +0100 |
| commit | b1e857cf1471d1871a9396696b22fa531da98249 (patch) | |
| tree | 3923008a8653057698cb339faf6dcfa92e18364b /project-base/src/renderers/render_cpu.c | |
| parent | 106bb7fcadf637cec883648916cc8d19529d6199 (diff) | |
add projbase to repo
Diffstat (limited to 'project-base/src/renderers/render_cpu.c')
| -rw-r--r-- | project-base/src/renderers/render_cpu.c | 760 |
1 files changed, 760 insertions, 0 deletions
diff --git a/project-base/src/renderers/render_cpu.c b/project-base/src/renderers/render_cpu.c new file mode 100644 index 0000000..750de97 --- /dev/null +++ b/project-base/src/renderers/render_cpu.c @@ -0,0 +1,760 @@ +/* +* BSD 2-Clause “Simplified” License +* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com +* All rights reserved. +*/ + +static u8 cpu_render_depth = 1; +static vec4 current_scissor; + +static platform_window *drawing_window = 0; + +static void _copy_image_pixel(s32 x, s32 y, image *image, render_target rec) +{ + s32 offset = (y * (drawing_window->backbuffer.width) * 5) + x * 5; + u8 *buffer_entry = drawing_window->backbuffer.buffer + offset; + + if (buffer_entry[4] > cpu_render_depth) + return; + buffer_entry[4] = cpu_render_depth; + + s32 _x = x - rec.x + rec.offset_x; + s32 _y = y - rec.y + rec.offset_y; + s32 image_offset = (_y * (image->width) * 4) + _x * 4; + u8 *color = image->data + image_offset; + + float32 alpha = color[3] / 255.0f; + float32 oneminusalpha = 1 - alpha; + + u8 b = ((color[0] * alpha) + (oneminusalpha * buffer_entry[0])); + u8 g = ((color[1] * alpha) + (oneminusalpha * buffer_entry[1])); + u8 r = ((color[2] * alpha) + (oneminusalpha * buffer_entry[2])); + u8 a = color[3]; + + s32 c = (a << 24) | (r << 16) | (g << 8) | (b << 0); + + memcpy(buffer_entry, &c, 4); +} + +static void _copy_glyph_pixel(s32 x, s32 y, glyph *gl, render_target rec, color tint) +{ + s32 offset = (y * (drawing_window->backbuffer.width) * 5) + x * 5; + u8 *buffer_entry = drawing_window->backbuffer.buffer + offset; + + if (buffer_entry[4] > cpu_render_depth) + return; + buffer_entry[4] = cpu_render_depth; + + s32 _x = x - rec.x + rec.offset_x; + s32 _y = y - rec.y + rec.offset_y; + s32 image_offset = (_y * gl->width) + _x; + u8 *color = gl->bitmap + image_offset; + + float32 alpha = color[0] / 255.0f; + float32 oneminusalpha = 1 - alpha; + + u8 r = ((tint.r * alpha) + (oneminusalpha * buffer_entry[2])); + u8 g = ((tint.g * alpha) + (oneminusalpha * buffer_entry[1])); + u8 b = ((tint.b * alpha) + (oneminusalpha * buffer_entry[0])); + u8 a = color[0]; + + s32 c = (a << 24) | (r << 16) | (g << 8) | (b << 0); + + memcpy(buffer_entry, &c, 4); +} + +static void _set_pixel(s32 x, s32 y, color tint) +{ + s32 offset = (y * (drawing_window->backbuffer.width) * 5) + x * 5; + u8 *buffer_entry = drawing_window->backbuffer.buffer + offset; + + if (buffer_entry[4] > cpu_render_depth) + return; + buffer_entry[4] = cpu_render_depth; + + float32 alpha = tint.a / 255.0f; + float32 oneminusalpha = 1 - alpha; + + u8 r = ((tint.r * alpha) + (oneminusalpha * buffer_entry[2])); + u8 g = ((tint.g * alpha) + (oneminusalpha * buffer_entry[1])); + u8 b = ((tint.b * alpha) + (oneminusalpha * buffer_entry[0])); + u8 a = tint.a; + + s32 c = (a << 24) | (r << 16) | (g << 8) | (b << 0); + + memcpy(buffer_entry, &c, 4); +} + +// returns topleft and bottomright corners. not width + height +static render_target _get_actual_rect(s32 x, s32 y, s32 width, s32 height) +{ + if (x < 0) + x = 0; + if (y < 0) + y = 0; + s32 start_x = x; + s32 start_y = y; + s32 end_x = start_x + width; + s32 end_y = start_y + height; + s32 offset_x = 0; + s32 offset_y = 0; + + if (start_x < current_scissor.x) + { + offset_x = current_scissor.x - start_x; + start_x = current_scissor.x; + } + if (start_y < current_scissor.y) + { + offset_y = current_scissor.y - start_y; + start_y = current_scissor.y; + } + + if (end_x > current_scissor.x + current_scissor.w) + end_x = current_scissor.x + current_scissor.w; + if (end_y > current_scissor.y + current_scissor.h) + end_y = current_scissor.y + current_scissor.h; + + if (end_x > drawing_window->backbuffer.width) + end_x = drawing_window->backbuffer.width; + if (end_y > drawing_window->backbuffer.height) + end_y = drawing_window->backbuffer.height; + + return (render_target){start_x, start_y, end_x, end_y, offset_x, offset_y}; +} + +static void cpu_render_reset_scissor() +{ + current_scissor = (vec4){0, 0, drawing_window->width + 1, drawing_window->height + 1}; +} + +static void cpu_render_rectangle(s32 x, s32 y, s32 width, s32 height, color tint) +{ + render_target rec = _get_actual_rect(x, y, width, height); + + for (s32 y = rec.y; y < rec.h; y++) + { + for (s32 x = rec.x; x < rec.w; x++) + { + _set_pixel(x, y, tint); + } + } +} + +static void cpu_render_clear(platform_window *window, color tint) +{ + drawing_window = window; + cpu_render_reset_scissor(); + + cpu_render_depth = 1; + + u8 pixel[5] = {0xFF, 0xFF, 0xFF, 0xFF, 0x00}; + s32 pixel_count = window->backbuffer.width * window->backbuffer.height; + for (s32 i = 0; i < pixel_count; i++) + memcpy(window->backbuffer.buffer + (i * 5), pixel, 5); +} + +static void cpu_render_set_rotation(float32 rotation) +{ + log_assert(0, "render_set_rotation not implemented on cpu"); +} + +static void cpu_render_reset_rotation(float32 rotation, float32 x, float32 y) +{ + log_assert(0, "render_reset_rotation not implemented on cpu"); +} + +static void cpu_set_cpu_render_depth(s32 depth) +{ + cpu_render_depth = depth; +} + +static void cpu_render_image(image *image, s32 x, s32 y, s32 width, s32 height) +{ + log_assert(image, "Image cannot be null"); + + if (image->loaded) + { + render_target rec = _get_actual_rect(x, y, image->width, image->height); + + for (s32 y = rec.y; y < rec.h; y++) + { + for (s32 x = rec.x; x < rec.w; x++) + { + _copy_image_pixel(x, y, image, rec); + } + } + } +} + +static void cpu_render_image_tint(image *image, s32 x, s32 y, s32 width, s32 height, color tint) +{ + log_assert(image, "Image cannot be null"); + + if (image->loaded) + { + log_assert(0, "render_image_tint not implemented for on cpu"); + } +} + +static s32 cpu_render_text_ellipsed(font *font, s32 x, s32 y, s32 maxw, char *text, color tint) +{ + if (!font->loaded) + return 0; + + char *ellipse = "..."; + bool in_ellipse = false; + + s32 len = utf8len(text); + + s32 x_ = x; + utf8_int32_t ch; + s32 count = 0; + while ((text = utf8codepoint(text, &ch)) && ch) + { + count++; + if (ch == 9) + ch = 32; + utf8_int32_t ch_next; + utf8codepoint(text, &ch_next); + if (ch < TEXT_CHARSET_START || ch > TEXT_CHARSET_END) + { + ch = 0x3f; + } + if (ch == '\n') + ch = 0xB6; + + glyph g = font->glyphs[ch]; + + s32 y_ = y + font->px_h + g.yoff; + s32 x_to_render = x_ + (g.lsb); + + render_target rec = _get_actual_rect(x_to_render, y_, g.width, g.height); + + for (s32 y = rec.y; y < rec.h; y++) + { + for (s32 x = rec.x; x < rec.w; x++) + { + _copy_glyph_pixel(x, y, &g, rec, tint); + } + } + + /* add kerning */ + //kern = stbtt_GetCodepointKernAdvance(&font->info, ch, ch_next); + x_ += (g.advance); + + if (!in_ellipse && (x_ - x) > maxw - (font->glyphs['.'].width * 3) && count < len - 3) + { + in_ellipse = true; + text = ellipse; + } + } + + return maxw; +} + +static s32 cpu_render_text_with_selection(font *font, s32 x, s32 y, char *text, color tint, s32 selection_start, s32 selection_length) +{ + if (!font->loaded) + return 0; + + s32 x_ = x; + utf8_int32_t ch; + s32 index = 0; + s32 selection_start_x = x_; + s32 selection_end_x = x_; + while ((text = utf8codepoint(text, &ch)) && ch) + { + if (ch == 9) + ch = 32; + utf8_int32_t ch_next; + utf8codepoint(text, &ch_next); + if (ch < TEXT_CHARSET_START || ch > TEXT_CHARSET_END) + { + ch = 0x3f; + } + if (ch == '\n') + ch = 0xB6; + + glyph g = font->glyphs[ch]; + + s32 y_ = y + font->px_h + g.yoff; + s32 x_to_render = x_ + (g.lsb); + + render_target rec = _get_actual_rect(x_to_render, y_, g.width, g.height); + + for (s32 y = rec.y; y < rec.h; y++) + { + for (s32 x = rec.x; x < rec.w; x++) + { + _copy_glyph_pixel(x, y, &g, rec, tint); + } + } + + /* add kerning */ + //kern = stbtt_GetCodepointKernAdvance(&font->info, ch, ch_next); + x_ += (g.advance); + + index++; + if (index == selection_start) + { + selection_start_x = x_; + } + if (index == selection_start + selection_length) + { + selection_end_x = x_; + } + } + + cpu_render_rectangle(selection_start_x, y - 3, selection_end_x - selection_start_x, TEXTBOX_HEIGHT - 10, rgba(66, 134, 244, 120)); + + return x_ - x; +} + +static s32 cpu_render_text_with_cursor(font *font, s32 x, s32 y, char *text, color tint, s32 cursor_pos) +{ + if (!font->loaded) + return 0; + + float x_ = x; + utf8_int32_t ch; + s32 index = 0; + s32 cursor_x = x_; + while ((text = utf8codepoint(text, &ch)) && ch) + { + if (ch == 9) + ch = 32; + utf8_int32_t ch_next; + utf8codepoint(text, &ch_next); + if (ch < TEXT_CHARSET_START || ch > TEXT_CHARSET_END) + { + ch = 0x3f; + } + if (ch == '\n') + ch = 0xB6; + + glyph g = font->glyphs[ch]; + + s32 y_ = y + font->px_h + g.yoff; + s32 x_to_render = x_ + (g.lsb); + + render_target rec = _get_actual_rect(x_to_render, y_, g.width, g.height); + + for (s32 y = rec.y; y < rec.h; y++) + { + for (s32 x = rec.x; x < rec.w; x++) + { + _copy_glyph_pixel(x, y, &g, rec, tint); + } + } + + /* add kerning */ + //kern = stbtt_GetCodepointKernAdvance(&font->info, ch, ch_next); + x_ += (g.advance); + index++; + + if (index == cursor_pos) + { + cursor_x = x_; + } + } + + cpu_render_rectangle(cursor_x, y - 3, 2, TEXTBOX_HEIGHT - 10, global_ui_context.style.textbox_foreground); + + return x_ - x; +} + +static s32 cpu_render_text(font *font, s32 x, s32 y, char *text, color tint) +{ + if (!font->loaded) + return 0; + + s32 x_ = x; + utf8_int32_t ch; + while ((text = utf8codepoint(text, &ch)) && ch) + { + if (ch == 9) + ch = 32; + utf8_int32_t ch_next; + utf8codepoint(text, &ch_next); + if (ch < TEXT_CHARSET_START || ch > TEXT_CHARSET_END) + { + ch = 0x3f; + } + if (ch == 10) + ch = 0xB6; + if (ch == 13) + continue; + + glyph g = font->glyphs[ch]; + + s32 y_ = y + font->px_h + g.yoff; + s32 x_to_render = x_ + (g.lsb); + + render_target rec = _get_actual_rect(x_to_render, y_, g.width, g.height); + + for (s32 y = rec.y; y < rec.h; y++) + { + for (s32 x = rec.x; x < rec.w; x++) + { + _copy_glyph_pixel(x, y, &g, rec, tint); + } + } + + /* add kerning */ + //kern = stbtt_GetCodepointKernAdvance(&font->info, ch, ch_next); + x_ += (g.advance); + } + + return x_ - x; +} + +static s32 cpu_render_text_cutoff(font *font, s32 x, s32 y, char *text, color tint, u16 cutoff_width) +{ + if (!font->loaded) + return 0; + + s32 x_ = x; + s32 y_ = y; + bool is_new_line = false; + utf8_int32_t ch; + while ((text = utf8codepoint(text, &ch)) && ch) + { + if (ch == 9) + ch = 32; + utf8_int32_t ch_next; + utf8codepoint(text, &ch_next); + if (ch < TEXT_CHARSET_START || ch > TEXT_CHARSET_END) + { + ch = 0x3f; + } + + if (ch == '\n') + { + x_ = x; + y_ += font->size; + is_new_line = true; + continue; + } + + if (is_new_line && ch == ' ') + { + is_new_line = false; + continue; + } + else if (is_new_line && ch != ' ') + { + is_new_line = false; + } + + glyph g = font->glyphs[ch]; + + s32 y__ = y_ + font->px_h + g.yoff; + s32 x_to_render = x_ + (g.lsb); + + render_target rec = _get_actual_rect(x_to_render, y__, g.width, g.height); + + for (s32 y = rec.y; y < rec.h; y++) + { + for (s32 x = rec.x; x < rec.w; x++) + { + _copy_glyph_pixel(x, y, &g, rec, tint); + } + } + + /* add kerning */ + //kern = stbtt_GetCodepointKernAdvance(&font->info, ch, ch_next); + x_ += (g.advance); + + if (x_ > x + cutoff_width) + { + x_ = x; + y_ += font->size; + is_new_line = true; + } + } + + return (y_ - y) + font->size; +} + +static s32 cpu_calculate_cursor_position(font *font, char *text, s32 click_x) +{ + if (!font->loaded) + return 0; + + s32 x = 0; + s32 index = 0; + utf8_int32_t ch; + while ((text = utf8codepoint(text, &ch)) && ch) + { + if (ch == 9) + ch = 32; + utf8_int32_t ch_next; + utf8codepoint(text, &ch_next); + if (ch < TEXT_CHARSET_START || ch > TEXT_CHARSET_END) + { + ch = 0x3f; + } + if (ch == '\n') + ch = 0xB6; + + glyph g = font->glyphs[ch]; + s32 width_next = font->glyphs[ch_next].width; + + /* add kerning */ + //kern = stbtt_GetCodepointKernAdvance(&font->info, ch, ch_next); + x += (g.advance); + if (x - (width_next / 5) > click_x) + { + return index; + } + + ++index; + } + + return index; +} + +static s32 cpu_calculate_text_width_from_upto(font *font, char *text, s32 from, s32 index) +{ + if (!font->loaded) + return 0; + + s32 x = 0; + utf8_int32_t ch; + s32 i = 0; + while ((text = utf8codepoint(text, &ch)) && ch) + { + if (index == i) + return x; + + if (ch == 9) + ch = 32; + utf8_int32_t ch_next; + utf8codepoint(text, &ch_next); + if (ch < TEXT_CHARSET_START || ch > TEXT_CHARSET_END) + { + ch = 0x3f; + } + if (ch == '\n') + ch = 0xB6; + + glyph g = font->glyphs[ch]; + + if (i >= from) + { + /* add kerning */ + //kern = stbtt_GetCodepointKernAdvance(&font->info, ch, ch_next); + x += (g.advance); + } + + i++; + } + + return x; +} + +static s32 cpu_calculate_text_width_upto(font *font, char *text, s32 index) +{ + if (!font->loaded) + return 0; + + s32 x = 0; + utf8_int32_t ch; + s32 i = 0; + while ((text = utf8codepoint(text, &ch)) && ch) + { + if (index == i) + return x; + + if (ch == 9) + ch = 32; + utf8_int32_t ch_next; + utf8codepoint(text, &ch_next); + if (ch < TEXT_CHARSET_START || ch > TEXT_CHARSET_END) + { + ch = 0x3f; + } + if (ch == '\n') + ch = 0xB6; + + glyph g = font->glyphs[ch]; + + /* add kerning */ + //kern = stbtt_GetCodepointKernAdvance(&font->info, ch, ch_next); + x += (g.advance); + + i++; + } + + return x; +} + +static s32 cpu_calculate_text_width(font *font, char *text) +{ + if (!font->loaded) + return 0; + + float x = 0; + utf8_int32_t ch; + while ((text = utf8codepoint(text, &ch)) && ch) + { + if (ch == 9) + ch = 32; + utf8_int32_t ch_next; + utf8codepoint(text, &ch_next); + if (ch < TEXT_CHARSET_START || ch > TEXT_CHARSET_END) + { + ch = 0x3f; + } + if (ch == '\n') + ch = 0xB6; + + glyph g = font->glyphs[ch]; + + /* add kerning */ + //kern = stbtt_GetCodepointKernAdvance(&font->info, ch, ch_next); + x += (g.advance); + } + + return x; +} + +static void cpu_render_triangle(s32 x, s32 y, s32 w, s32 h, color tint, triangle_direction dir) +{ + render_target rec = _get_actual_rect(x, y, w, h); + + s32 ac_w = rec.w - rec.x; + + if (dir == TRIANGLE_DOWN) + { + for (s32 y = rec.y; y < rec.h; y++) + { + s32 _y = y - rec.y; + + for (s32 x = rec.x; x < rec.w; x++) + { + s32 _x = x - rec.x; + if (_x < _y / 2 || _x >= ac_w - (_y / 2) - 1) + continue; + + _set_pixel(x, y, tint); + } + } + } + else if (dir == TRIANGLE_UP) + { + s32 ac_h = rec.h - rec.y; + + for (s32 y = rec.y; y < rec.h; y++) + { + s32 _y = (y - rec.y); + _y = ac_h - _y; + + for (s32 x = rec.x; x < rec.w; x++) + { + s32 _x = x - rec.x; + if (_x < _y / 2 || _x >= ac_w - (_y / 2)) + continue; + + _set_pixel(x, y, tint); + } + } + } + else if (dir == TRIANGLE_LEFT) + { + s32 ac_h = rec.h - rec.y; + + for (s32 y = rec.y; y < rec.h; y++) + { + s32 _y = (y - rec.y); + _y = ac_h - _y; + + for (s32 x = rec.x; x < rec.w; x++) + { + s32 _x = x - rec.x; + _x = ac_w - _x; + if (_x / 2 > _y - 1 || _x / 2 >= ac_w - (_y)) + continue; + + _set_pixel(x, y, tint); + } + } + } + else if (dir == TRIANGLE_RIGHT) + { + log_assert(0, "TRIANGLE_RIGHT not implemented"); + } +} + +static void cpu_render_line(s32 x1, s32 y1, s32 x2, s32 y2, float thickness, color tint) +{ + log_assert(0, "render_line not implemented on cpu"); +} + +static void cpu_render_rectangle_outline(s32 x, s32 y, s32 width, s32 height, u16 outline_w, color tint) +{ + // left + cpu_render_rectangle(x, y, outline_w, height, tint); + // right + cpu_render_rectangle(x + width - outline_w, y, outline_w, height, tint); + // top + cpu_render_rectangle(x + outline_w, y, width - (outline_w * 2), outline_w, tint); + // bottom + cpu_render_rectangle(x + outline_w, y + height - outline_w, width - (outline_w * 2), outline_w, tint); +} + +static void cpu_render_set_scissor(platform_window *window, s32 x, s32 y, s32 w, s32 h) +{ + current_scissor = (vec4){x, y, w + 1, h + 1}; +} + +static vec4 cpu_render_get_scissor(platform_window *window) +{ + return current_scissor; +} + +// +// Arc drawing +// +static void cpu_render_arc(float x1, float y1, float x2, float y2, float radius, bool arcDirection, bool useBiggerArc, color tint, float thickness) +{ + log_assert(0, "render_arc not implemented on cpu"); +} + +render_driver render_cpu_driver = +{ + "CPU", + cpu_set_cpu_render_depth, + cpu_render_clear, + + cpu_render_image, + cpu_render_image_tint, + + cpu_render_text, + cpu_render_text_ellipsed, + cpu_render_text_cutoff, + cpu_render_text_with_cursor, + cpu_render_text_with_selection, + + cpu_calculate_cursor_position, + cpu_calculate_text_width, + cpu_calculate_text_width_upto, + cpu_calculate_text_width_from_upto, + + cpu_render_rectangle, + cpu_render_line, + cpu_render_rectangle_outline, + cpu_render_triangle, + + cpu_render_set_scissor, + cpu_render_get_scissor, + cpu_render_reset_scissor, + + cpu_render_set_rotation, + cpu_render_reset_rotation, + cpu_render_arc, + + 0, + 0, +};
\ No newline at end of file |
