summaryrefslogtreecommitdiff
path: root/project-base/src/renderers
diff options
context:
space:
mode:
Diffstat (limited to 'project-base/src/renderers')
-rw-r--r--project-base/src/renderers/render_cpu.c760
-rw-r--r--project-base/src/renderers/render_gl.c959
2 files changed, 1719 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
diff --git a/project-base/src/renderers/render_gl.c b/project-base/src/renderers/render_gl.c
new file mode 100644
index 0000000..dad32a5
--- /dev/null
+++ b/project-base/src/renderers/render_gl.c
@@ -0,0 +1,959 @@
+/*
+* BSD 2-Clause “Simplified” License
+* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com
+* All rights reserved.
+*/
+
+static u8 gl_render_depth = 1;
+static vec4 current_scissor;
+static float global_rotation = 0.0f;
+
+static void gl_render_clear(platform_window *window, color tint)
+{
+ IMP_glClearColor(tint.r / 255.0f, tint.g / 255.0f, tint.b / 255.0f, tint.a / 255.0f);
+ IMP_glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+}
+
+static void gl_render_set_rotation(float32 rotation)
+{
+ global_rotation = rotation;
+}
+
+static void gl_render_reset_rotation(float32 rotation, float32 x, float32 y)
+{
+ IMP_glPopMatrix();
+}
+
+static void gl_set_gl_render_depth(s32 depth)
+{
+ gl_render_depth = depth;
+}
+
+static vec2f gl_rotateUV(vec2f uv, float rotation)
+{
+ uv = (vec2f){uv.x - 0.5f, uv.y - 0.5f};
+ float m2x2[4] = { cos(rotation), sin(rotation), -sin(rotation), cos(rotation) };
+
+ vec2f result = {0,0};
+#if 0
+ result.x = m2x2[0] * uv.x + m2x2[1] * uv.y;
+ result.y = m2x2[2] * uv.x + m2x2[3] * uv.y;
+#else
+ result.x = m2x2[0] * uv.x + m2x2[2] * uv.y;
+ result.y = m2x2[1] * uv.x + m2x2[3] * uv.y;
+#endif
+
+ result = (vec2f){result.x + 0.5f, result.y + 0.5f};
+ return result;
+}
+
+static void gl_render_rectangle(s32 x, s32 y, s32 width, s32 height, color tint)
+{
+ IMP_glBindTexture(GL_TEXTURE_2D, 0);
+ IMP_glBegin(GL_QUADS);
+ IMP_glColor4f(tint.r / 255.0f, tint.g / 255.0f, tint.b / 255.0f, tint.a / 255.0f);
+ IMP_glVertex3i(x, y, gl_render_depth);
+ IMP_glVertex3i(x, y + height, gl_render_depth);
+ IMP_glVertex3i(x + width, y + height, gl_render_depth);
+ IMP_glVertex3i(x + width, y, gl_render_depth);
+ IMP_glEnd();
+}
+
+static void gl_render_image(image *image, s32 x, s32 y, s32 width, s32 height)
+{
+ log_assert(image, "Image cannot be null");
+
+ if (image->loaded)
+ {
+ IMP_glBindTexture(GL_TEXTURE_2D, image->textureID);
+ IMP_glEnable(GL_TEXTURE_2D);
+ IMP_glBegin(GL_QUADS);
+ IMP_glColor4f(1., 1., 1., 1.);
+
+ vec2f topleft = gl_rotateUV((vec2f){0, 0}, global_rotation);
+ vec2f bottomleft = gl_rotateUV((vec2f){0, 1}, global_rotation);
+ vec2f bottomright = gl_rotateUV((vec2f){1, 1}, global_rotation);
+ vec2f topright = gl_rotateUV((vec2f){1, 0}, global_rotation);
+
+ IMP_glPushMatrix();
+ IMP_glTranslatef(x + (width / 2), y + (height / 2), 0);
+
+ IMP_glTexCoord2f(topleft.x, topleft.y);
+ IMP_glVertex3i(x, y, gl_render_depth);
+ IMP_glTexCoord2f(bottomleft.x, bottomleft.y);
+ IMP_glVertex3i(x, y + height, gl_render_depth);
+ IMP_glTexCoord2f(bottomright.x, bottomright.y);
+ IMP_glVertex3i(x + width, y + height, gl_render_depth);
+ IMP_glTexCoord2f(topright.x, topright.y);
+ IMP_glVertex3i(x + width, y, gl_render_depth);
+
+ IMP_glPopMatrix();
+
+ IMP_glEnd();
+ IMP_glBindTexture(GL_TEXTURE_2D, 0);
+ IMP_glDisable(GL_TEXTURE_2D);
+ }
+}
+
+static void gl_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)
+ {
+ IMP_glBindTexture(GL_TEXTURE_2D, image->textureID);
+ IMP_glEnable(GL_TEXTURE_2D);
+ IMP_glBegin(GL_QUADS);
+ IMP_glColor4f(tint.r / 255.0f, tint.g / 255.0f, tint.b / 255.0f, tint.a / 255.0f);
+
+ vec2f topleft = gl_rotateUV((vec2f){0, 0}, global_rotation);
+ vec2f bottomleft = gl_rotateUV((vec2f){0, 1}, global_rotation);
+ vec2f bottomright = gl_rotateUV((vec2f){1, 1}, global_rotation);
+ vec2f topright = gl_rotateUV((vec2f){1, 0}, global_rotation);
+
+ IMP_glPushMatrix();
+ IMP_glTranslatef(x + (width / 2), y + (height / 2), 0);
+
+ IMP_glTexCoord2f(topleft.x, topleft.y);
+ IMP_glVertex3i(x, y, gl_render_depth);
+ IMP_glTexCoord2f(bottomleft.x, bottomleft.y);
+ IMP_glVertex3i(x, y + height, gl_render_depth);
+ IMP_glTexCoord2f(bottomright.x, bottomright.y);
+ IMP_glVertex3i(x + width, y + height, gl_render_depth);
+ IMP_glTexCoord2f(topright.x, topright.y);
+ IMP_glVertex3i(x + width, y, gl_render_depth);
+
+ IMP_glPopMatrix();
+
+ IMP_glEnd();
+ IMP_glBindTexture(GL_TEXTURE_2D, 0);
+ IMP_glDisable(GL_TEXTURE_2D);
+ }
+}
+
+static s32 gl_render_text_ellipsed(font *font, s32 x, s32 y, s32 maxw, char *text, color tint)
+{
+ if (!font->loaded)
+ return 0;
+
+ IMP_glEnable(GL_TEXTURE_2D);
+ IMP_glColor4f(tint.r / 255.0f, tint.g / 255.0f, tint.b / 255.0f, tint.a / 255.0f);
+
+ 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);
+
+ IMP_glBindTexture(GL_TEXTURE_2D, g.textureID);
+ IMP_glBegin(GL_QUADS);
+
+ IMP_glTexCoord2i(0, 0);
+ IMP_glVertex3i(x_to_render, y_, gl_render_depth);
+ IMP_glTexCoord2i(0, 1);
+ IMP_glVertex3i(x_to_render, y_ + g.height, gl_render_depth);
+ IMP_glTexCoord2i(1, 1);
+ IMP_glVertex3i(x_to_render + g.width, y_ + g.height, gl_render_depth);
+ IMP_glTexCoord2i(1, 0);
+ IMP_glVertex3i(x_to_render + g.width, y_, gl_render_depth);
+ IMP_glBindTexture(GL_TEXTURE_2D, 0);
+ IMP_glEnd();
+
+ /* 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;
+ }
+ }
+
+ IMP_glDisable(GL_TEXTURE_2D);
+
+ return maxw;
+}
+
+static s32 gl_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;
+
+ IMP_glEnable(GL_TEXTURE_2D);
+ IMP_glColor4f(tint.r / 255.0f, tint.g / 255.0f, tint.b / 255.0f, tint.a / 255.0f);
+
+ 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);
+
+ IMP_glBindTexture(GL_TEXTURE_2D, g.textureID);
+ IMP_glBegin(GL_QUADS);
+
+ IMP_glTexCoord2i(0, 0);
+ IMP_glVertex3i(x_to_render, y_, gl_render_depth);
+ IMP_glTexCoord2i(0, 1);
+ IMP_glVertex3i(x_to_render, y_ + g.height, gl_render_depth);
+ IMP_glTexCoord2i(1, 1);
+ IMP_glVertex3i(x_to_render + g.width, y_ + g.height, gl_render_depth);
+ IMP_glTexCoord2i(1, 0);
+ IMP_glVertex3i(x_to_render + g.width, y_, gl_render_depth);
+ IMP_glBindTexture(GL_TEXTURE_2D, 0);
+ IMP_glEnd();
+
+ /* 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_;
+ }
+ }
+
+ IMP_glDisable(GL_TEXTURE_2D);
+
+ gl_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 gl_render_text_with_cursor(font *font, s32 x, s32 y, char *text, color tint, s32 cursor_pos)
+{
+ if (!font->loaded)
+ return 0;
+
+ IMP_glEnable(GL_TEXTURE_2D);
+ IMP_glColor4f(tint.r / 255.0f, tint.g / 255.0f, tint.b / 255.0f, tint.a / 255.0f);
+
+ 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);
+
+ IMP_glBindTexture(GL_TEXTURE_2D, g.textureID);
+ IMP_glBegin(GL_QUADS);
+
+ IMP_glTexCoord2i(0, 0);
+ IMP_glVertex3i(x_to_render, y_, gl_render_depth);
+ IMP_glTexCoord2i(0, 1);
+ IMP_glVertex3i(x_to_render, y_ + g.height, gl_render_depth);
+ IMP_glTexCoord2i(1, 1);
+ IMP_glVertex3i(x_to_render + g.width, y_ + g.height, gl_render_depth);
+ IMP_glTexCoord2i(1, 0);
+ IMP_glVertex3i(x_to_render + g.width, y_, gl_render_depth);
+ IMP_glBindTexture(GL_TEXTURE_2D, 0);
+ IMP_glEnd();
+
+ /* add kerning */
+ //kern = stbtt_GetCodepointKernAdvance(&font->info, ch, ch_next);
+ x_ += (g.advance);
+ index++;
+
+ if (index == cursor_pos)
+ {
+ cursor_x = x_;
+ }
+ }
+
+ IMP_glDisable(GL_TEXTURE_2D);
+
+ gl_render_rectangle(cursor_x, y - 3, 2, TEXTBOX_HEIGHT - 10, global_ui_context.style.textbox_foreground);
+
+ return x_ - x;
+}
+
+static s32 gl_render_text_rd(font *font, s32 x, s32 y, char *text, color tint, u16 target_h)
+{
+ float size_multiplier = target_h/(float)font->size;
+
+ if (!font->loaded)
+ return 0;
+
+ IMP_glEnable(GL_TEXTURE_2D);
+ IMP_glColor4f(tint.r / 255.0f, tint.g / 255.0f, tint.b / 255.0f, tint.a / 255.0f);
+
+ s32 x_ = x;
+ utf8_int32_t ch;
+ while ((text = utf8codepoint(text, &ch)) && ch)
+ {
+ if (ch == 9) ch = 32; // Turn tab into space.
+ if (ch < TEXT_CHARSET_START || ch > TEXT_CHARSET_END) ch = 0x3f; // Default character for out of range glyphs.
+ if (ch == 10) ch = 0xB6; // What the hell is this?
+ if (ch == 13) continue; // Don't draw newlines.
+
+ glyph g = font->glyphs[ch];
+
+ s32 y_ = y + (font->px_h + g.yoff)*size_multiplier;
+ s32 x_to_render = x_ + (g.lsb*size_multiplier);
+
+ IMP_glBindTexture(GL_TEXTURE_2D, g.textureID);
+ IMP_glBegin(GL_QUADS);
+
+ IMP_glTexCoord2i(0, 0);
+ IMP_glVertex3i(x_to_render, y_, gl_render_depth);
+ IMP_glTexCoord2i(0, 1);
+ IMP_glVertex3i(x_to_render, y_ + g.height*size_multiplier, gl_render_depth);
+ IMP_glTexCoord2i(1, 1);
+ IMP_glVertex3i(x_to_render + g.width*size_multiplier, y_ + g.height*size_multiplier, gl_render_depth);
+ IMP_glTexCoord2i(1, 0);
+ IMP_glVertex3i(x_to_render + g.width*size_multiplier, y_, gl_render_depth);
+ IMP_glBindTexture(GL_TEXTURE_2D, 0);
+ IMP_glEnd();
+
+ x_ += (g.advance*size_multiplier);
+ }
+
+ IMP_glDisable(GL_TEXTURE_2D);
+
+ return x_ - x;
+}
+
+static s32 gl_render_text(font *font, s32 x, s32 y, char *text, color tint)
+{
+ if (!font->loaded)
+ return 0;
+
+ IMP_glEnable(GL_TEXTURE_2D);
+ IMP_glColor4f(tint.r / 255.0f, tint.g / 255.0f, tint.b / 255.0f, tint.a / 255.0f);
+
+ s32 x_ = x;
+ utf8_int32_t ch;
+ while ((text = utf8codepoint(text, &ch)) && ch)
+ {
+ if (ch == 9) ch = 32; // Turn tab into space.
+ if (ch < TEXT_CHARSET_START || ch > TEXT_CHARSET_END) ch = 0x3f; // Default character for out of range glyphs.
+ if (ch == 10) ch = 0xB6; // What the hell is this?
+ if (ch == 13) continue; // Don't draw newlines.
+
+ glyph g = font->glyphs[ch];
+
+ s32 y_ = y + font->px_h + g.yoff;
+ s32 x_to_render = x_ + (g.lsb);
+
+ IMP_glBindTexture(GL_TEXTURE_2D, g.textureID);
+ IMP_glBegin(GL_QUADS);
+
+ IMP_glTexCoord2i(0, 0);
+ IMP_glVertex3i(x_to_render, y_, gl_render_depth);
+ IMP_glTexCoord2i(0, 1);
+ IMP_glVertex3i(x_to_render, y_ + g.height, gl_render_depth);
+ IMP_glTexCoord2i(1, 1);
+ IMP_glVertex3i(x_to_render + g.width, y_ + g.height, gl_render_depth);
+ IMP_glTexCoord2i(1, 0);
+ IMP_glVertex3i(x_to_render + g.width, y_, gl_render_depth);
+ IMP_glBindTexture(GL_TEXTURE_2D, 0);
+ IMP_glEnd();
+
+ /* add kerning */
+ //utf8_int32_t ch_next;
+ //utf8codepoint(text, &ch_next);
+ //kern = stbtt_GetCodepointKernAdvance(&font->info, ch, ch_next);
+ x_ += (g.advance);
+ }
+
+ IMP_glDisable(GL_TEXTURE_2D);
+
+ return x_ - x;
+}
+
+static s32 gl_render_text_cutoff(font *font, s32 x, s32 y, char *text, color tint, u16 cutoff_width)
+{
+ if (!font->loaded)
+ return 0;
+
+ IMP_glEnable(GL_TEXTURE_2D);
+ IMP_glColor4f(tint.r / 255.0f, tint.g / 255.0f, tint.b / 255.0f, tint.a / 255.0f);
+
+ 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);
+
+ if (x_to_render+g.advance > x + cutoff_width)
+ {
+ x_ = x;
+ y_ += font->size;
+ y__ = y_ + font->px_h + g.yoff;
+ x_to_render = x_;
+ }
+
+ IMP_glBindTexture(GL_TEXTURE_2D, g.textureID);
+ IMP_glBegin(GL_QUADS);
+
+ IMP_glTexCoord2i(0, 0);
+ IMP_glVertex3i(x_to_render, y__, gl_render_depth);
+ IMP_glTexCoord2i(0, 1);
+ IMP_glVertex3i(x_to_render, y__ + g.height, gl_render_depth);
+ IMP_glTexCoord2i(1, 1);
+ IMP_glVertex3i(x_to_render + g.width, y__ + g.height, gl_render_depth);
+ IMP_glTexCoord2i(1, 0);
+ IMP_glVertex3i(x_to_render + g.width, y__, gl_render_depth);
+ IMP_glBindTexture(GL_TEXTURE_2D, 0);
+ IMP_glEnd();
+
+ /* 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;
+ }
+ }
+
+ IMP_glDisable(GL_TEXTURE_2D);
+
+ return (y_ - y) + font->size;
+}
+
+static s32 gl_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 gl_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 gl_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 gl_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 gl_render_triangle(s32 x, s32 y, s32 w, s32 h, color tint, triangle_direction dir)
+{
+ IMP_glBegin(GL_TRIANGLES);
+ IMP_glColor4f(tint.r / 255.0f, tint.g / 255.0f, tint.b / 255.0f, tint.a / 255.0f);
+
+ if (dir == TRIANGLE_DOWN)
+ {
+ IMP_glVertex3i(x + (w / 2), y + h, gl_render_depth);
+ IMP_glVertex3i(x, y, gl_render_depth);
+ IMP_glVertex3i(x + w, y, gl_render_depth);
+ }
+ else if (dir == TRIANGLE_UP)
+ {
+ IMP_glVertex3i(x + (w / 2), y, gl_render_depth);
+ IMP_glVertex3i(x + w, y + h, gl_render_depth);
+ IMP_glVertex3i(x, y + h, gl_render_depth);
+ }
+ else if (dir == TRIANGLE_LEFT)
+ {
+ IMP_glVertex3i(x, y + (w / 2), gl_render_depth);
+ IMP_glVertex3i(x + h, y, gl_render_depth);
+ IMP_glVertex3i(x + h, y + w, gl_render_depth);
+ }
+ else if (dir == TRIANGLE_RIGHT)
+ {
+ IMP_glVertex3i(x + h, y + (w / 2), gl_render_depth);
+ IMP_glVertex3i(x, y, gl_render_depth);
+ IMP_glVertex3i(x, y + w, gl_render_depth);
+ }
+
+ IMP_glEnd();
+}
+
+static void gl_render_line(s32 x1, s32 y1, s32 x2, s32 y2, float thickness, color tint)
+{
+ IMP_glBindTexture(GL_TEXTURE_2D, 0);
+ IMP_glLineWidth(thickness);
+ IMP_glColor4f(tint.r / 255.0f, tint.g / 255.0f, tint.b / 255.0f, tint.a / 255.0f);
+ IMP_glBegin(GL_LINES);
+ IMP_glVertex3i(x1, y1, gl_render_depth);
+ IMP_glVertex3i(x2, y2, gl_render_depth);
+ IMP_glEnd();
+}
+
+static void gl_render_rectangle_outline(s32 x, s32 y, s32 width, s32 height, u16 outline_w, color tint)
+{
+ // left
+ gl_render_rectangle(x, y, outline_w, height, tint);
+ // right
+ gl_render_rectangle(x + width - outline_w, y, outline_w, height, tint);
+ // top
+ gl_render_rectangle(x + outline_w, y, width - (outline_w * 2), outline_w, tint);
+ // bottom
+ gl_render_rectangle(x + outline_w, y + height - outline_w, width - (outline_w * 2), outline_w, tint);
+}
+
+static void gl_render_set_scissor(platform_window *window, s32 x, s32 y, s32 w, s32 h)
+{
+ IMP_glEnable(GL_SCISSOR_TEST);
+ if (window) IMP_glScissor(x - 1, window->height - h - y - 1, w + 1, h + 1);
+ else IMP_glScissor(x - 1, y - 1 + h, w + 1, h + 1);
+}
+
+static vec4 gl_render_get_scissor()
+{
+ GLboolean enabled = false;
+ IMP_glGetBooleanv(GL_SCISSOR_TEST,&enabled);
+ vec4 vec;
+
+ if (enabled) {
+ IMP_glGetIntegerv(GL_SCISSOR_BOX, (GLint *)(&vec));
+ vec.x += 1;
+ vec.w -= 1;
+ vec.h -= 1;
+ vec.y += 1;
+ } else {
+ vec = (vec4){0,0,99999,99999};
+ }
+ return vec;
+}
+
+static void gl_render_reset_scissor()
+{
+ IMP_glDisable(GL_SCISSOR_TEST);
+}
+
+//
+// Arc drawing
+//
+static float gl_normalizeAngleToSmallestPositive(float angle)
+{
+ while (angle < 0.0)
+ {
+ angle += M_PI * 2;
+ }
+ while (angle >= M_PI * 2)
+ {
+ angle -= M_PI * 2;
+ }
+ return angle;
+}
+
+const int ARC_VERTEX_COUNT = 100;
+static void gl__render_arc(float angle1, float angle2, float radius, float x, float y, float useBiggerArc, color tint, float thickness)
+{
+ // Prepare angles
+ angle1 = gl_normalizeAngleToSmallestPositive(angle1);
+ angle2 = gl_normalizeAngleToSmallestPositive(angle2);
+ if (angle1 > angle2)
+ {
+ float buffer = angle1;
+ angle1 = angle2;
+ angle2 = buffer;
+ }
+ if (useBiggerArc != (angle2 - angle1 > M_PI))
+ {
+ angle1 += M_PI * 2;
+ }
+
+ s32 buffer_size = ARC_VERTEX_COUNT * 2;
+
+ // Create opengl geometry
+ GLfloat pos[200];
+ for (int i = 0; i < ARC_VERTEX_COUNT; i++)
+ {
+ pos[i * 2] = sin((float)i / (ARC_VERTEX_COUNT - 1) * (angle2 - angle1) + angle1) * radius + x;
+ pos[i * 2 + 1] = cos((float)i / (ARC_VERTEX_COUNT - 1) * (angle2 - angle1) + angle1) * radius + y;
+ }
+
+ IMP_glBindTexture(GL_TEXTURE_2D, 0);
+ IMP_glBegin(GL_LINE_STRIP);
+ IMP_glLineWidth(thickness);
+ IMP_glColor4f(tint.r / 255.0f, tint.g / 255.0f, tint.b / 255.0f, tint.a / 255.0f);
+ for (int i = 0; i < buffer_size; i += 2)
+ {
+ //printf("pos: %f %f, --- x: %f y: %f\n", x, y, pos[i], pos[i+1]);
+ IMP_glVertex3i(pos[i], pos[i + 1], gl_render_depth);
+ }
+ IMP_glEnd();
+}
+
+static void gl_render_arc(float x1, float y1, float x2, float y2, float radius, bool arcDirection, bool useBiggerArc, color tint, float thickness)
+{
+ // distance between points
+ float distance = sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
+ // halfway point
+ float xAverage = (x1 + x2) / 2.0;
+ float yAverage = (y1 + y2) / 2.0;
+ // circle center
+ float xCenter = sqrt(radius * radius - distance * distance / 4.0) * (y1 - y2) / distance;
+ float yCenter = sqrt(radius * radius - distance * distance / 4.0) * (x2 - x1) / distance;
+ xCenter = xAverage + (arcDirection ? xCenter : -xCenter);
+ yCenter = yAverage + (arcDirection ? yCenter : -yCenter);
+ // angles
+ float angle1 = atan2(x1 - xCenter, y1 - yCenter);
+ float angle2 = atan2(x2 - xCenter, y2 - yCenter);
+ // create the arc
+
+ //printf("%f %f %f, %f\n", distance, xCenter, yCenter, (radius*radius - distance*distance/4.0));
+ gl__render_arc(angle1, angle2, radius, xCenter, yCenter, useBiggerArc, tint, thickness);
+}
+
+#define ROUNDING_POINT_COUNT 16 // Larger values makes circle smoother.
+void gl_render_rounded_rect(float x, float y, float width, float height, color tint, float radius, int innerPad)
+{
+ y += height;
+ vec2f top_left[ROUNDING_POINT_COUNT];
+ vec2f bottom_left[ROUNDING_POINT_COUNT];
+ vec2f top_right[ROUNDING_POINT_COUNT];
+ vec2f bottom_right[ROUNDING_POINT_COUNT];
+
+ if( radius == 0.0 )
+ {
+ radius = min(width, height);
+ radius *= 0.10; // 10%
+ }
+
+ int i = 0;
+ float x_offset, y_offset;
+ float step = ( 2.0f * M_PI ) / (ROUNDING_POINT_COUNT * 4),
+ angle = 0.0f;
+
+ unsigned int index = 0, segment_count = ROUNDING_POINT_COUNT;
+ vec2f bottom_left_corner = { x + radius, y - height + radius };
+
+
+ while( i != segment_count )
+ {
+ x_offset = cosf( angle );
+ y_offset = sinf( angle );
+
+
+ top_left[ index ].x = bottom_left_corner.x -
+ ( x_offset * radius );
+ top_left[ index ].y = ( height - ( radius * 2.0f ) ) +
+ bottom_left_corner.y -
+ ( y_offset * radius );
+
+
+ top_right[ index ].x = ( width - ( radius * 2.0f ) ) +
+ bottom_left_corner.x +
+ ( x_offset * radius ) + (i == 0 ? -1 : 0);
+ top_right[ index ].y = ( height - ( radius * 2.0f ) ) +
+ bottom_left_corner.y -
+ ( y_offset * radius );
+
+
+ bottom_right[ index ].x = ( width - ( radius * 2.0f ) ) +
+ bottom_left_corner.x +
+ ( x_offset * radius ) + (i == 0 ? -1 : 0);
+ bottom_right[ index ].y = bottom_left_corner.y +
+ ( y_offset * radius );
+
+
+ bottom_left[ index ].x = bottom_left_corner.x -
+ ( x_offset * radius );
+ bottom_left[ index ].y = bottom_left_corner.y +
+ ( y_offset * radius );
+
+#if 1
+ top_left[ index ].x = (top_left[ index ].x)+innerPad;
+ top_left[ index ].y = (top_left[ index ].y)-innerPad;
+
+
+ top_right[ index ].x = (top_right[ index ].x)-innerPad;
+ top_right[ index ].y = (top_right[ index ].y)-innerPad;
+
+
+ bottom_right[ index ].x = (bottom_right[ index ].x)-innerPad;
+ bottom_right[ index ].y = (bottom_right[ index ].y)+innerPad;
+
+
+ bottom_left[ index ].x = (bottom_left[ index ].x)+innerPad;
+ bottom_left[ index ].y = (bottom_left[ index ].y)+innerPad;
+#endif
+
+ angle -= step;
+
+ ++index;
+
+ ++i;
+ }
+
+ IMP_glBindTexture(GL_TEXTURE_2D, 0);
+ IMP_glColor4f(tint.r / 255.0f, tint.g / 255.0f, tint.b / 255.0f, tint.a / 255.0f);
+ IMP_glBegin( GL_TRIANGLE_STRIP );
+ {
+ // Top
+ for( i = segment_count - 1 ; i >= 0 ; i--)
+ {
+ IMP_glVertex3i( top_left[ i ].x, top_left[ i ].y, gl_render_depth);
+ IMP_glVertex3i( top_right[ i ].x, top_right[ i ].y, gl_render_depth );
+ }
+
+ // In order to stop and restart the strip.
+ IMP_glVertex3i( top_right[ 0 ].x, top_right[ 0 ].y, gl_render_depth );
+ IMP_glVertex3i( top_right[ 0 ].x, top_right[ 0 ].y, gl_render_depth );
+
+ // Center
+ IMP_glVertex3i( top_right[ 0 ].x, top_right[ 0 ].y, gl_render_depth );
+ IMP_glVertex3i( top_left[ 0 ].x, top_left[ 0 ].y, gl_render_depth );
+ IMP_glVertex3i( bottom_right[ 0 ].x, bottom_right[ 0 ].y, gl_render_depth );
+ IMP_glVertex3i( bottom_left[ 0 ].x, bottom_left[ 0 ].y, gl_render_depth );
+
+ // Bottom
+ for( i = 0; i != segment_count ; i++ )
+ {
+ IMP_glVertex3i( bottom_right[ i ].x, bottom_right[ i ].y, gl_render_depth );
+ IMP_glVertex3i( bottom_left[ i ].x, bottom_left[ i ].y, gl_render_depth );
+ }
+ }
+ IMP_glEnd();
+}
+
+render_driver render_gl_driver =
+{
+ "GL",
+ gl_set_gl_render_depth,
+ gl_render_clear,
+
+ gl_render_image,
+ gl_render_image_tint,
+
+ gl_render_text,
+ gl_render_text_ellipsed,
+ gl_render_text_cutoff,
+ gl_render_text_with_cursor,
+ gl_render_text_with_selection,
+
+ gl_calculate_cursor_position,
+ gl_calculate_text_width,
+ gl_calculate_text_width_upto,
+ gl_calculate_text_width_from_upto,
+
+ gl_render_rectangle,
+ gl_render_line,
+ gl_render_rectangle_outline,
+ gl_render_triangle,
+
+ gl_render_set_scissor,
+ gl_render_get_scissor,
+ gl_render_reset_scissor,
+
+ gl_render_set_rotation,
+ gl_render_reset_rotation,
+ gl_render_arc,
+
+ gl_render_text_rd,
+ gl_render_rounded_rect,
+}; \ No newline at end of file