// SafeC Standard Library — GUI: software rasterizer implementation (see
// gui_draw.h). The 8x8 bitmap font below was generated from a real system
// monospace font (rendered at high resolution, downsampled, thresholded)
// rather than hand-transcribed, covering printable ASCII 0x20-0x7E.
#pragma once
#include <std/gui/gui.h>
#include <std/gui/gui_draw.h>
namespace std {
#define GUI_FONT_FIRST 32
#define GUI_FONT_LAST 126
#define GUI_FONT_COUNT 95
static const unsigned char gFont8x8[GUI_FONT_COUNT * 8] = {
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, // 32 ' '
0x00U, 0x00U, 0x10U, 0x10U, 0x00U, 0x10U, 0x00U, 0x00U, // 33 '!'
0x00U, 0x00U, 0x18U, 0x10U, 0x00U, 0x00U, 0x00U, 0x00U, // 34 '"'
0x00U, 0x00U, 0x1CU, 0x1CU, 0x38U, 0x38U, 0x00U, 0x00U, // 35 '#'
0x00U, 0x18U, 0x3CU, 0x18U, 0x1CU, 0x3CU, 0x18U, 0x00U, // 36 '$'
0x00U, 0x20U, 0x34U, 0x38U, 0x1CU, 0x2CU, 0x04U, 0x00U, // 37 '%'
0x00U, 0x10U, 0x38U, 0x10U, 0x3CU, 0x2CU, 0x10U, 0x00U, // 38 '&'
0x00U, 0x00U, 0x18U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, // 39 '''
0x00U, 0x08U, 0x10U, 0x10U, 0x10U, 0x10U, 0x08U, 0x00U, // 40 '('
0x00U, 0x10U, 0x18U, 0x08U, 0x08U, 0x18U, 0x10U, 0x00U, // 41 ')'
0x00U, 0x00U, 0x00U, 0x18U, 0x18U, 0x00U, 0x00U, 0x00U, // 42 '*'
0x00U, 0x00U, 0x00U, 0x18U, 0x18U, 0x00U, 0x00U, 0x00U, // 43 '+'
0x00U, 0x00U, 0x00U, 0x10U, 0x10U, 0x00U, 0x00U, 0x00U, // 44 ','
0x00U, 0x00U, 0x00U, 0x18U, 0x00U, 0x00U, 0x00U, 0x00U, // 45 '-'
0x00U, 0x00U, 0x00U, 0x18U, 0x18U, 0x00U, 0x00U, 0x00U, // 46 '.'
0x00U, 0x00U, 0x08U, 0x08U, 0x10U, 0x10U, 0x00U, 0x00U, // 47 '/'
0x00U, 0x18U, 0x2CU, 0x2CU, 0x34U, 0x3CU, 0x18U, 0x00U, // 48 '0'
0x00U, 0x00U, 0x38U, 0x08U, 0x08U, 0x18U, 0x18U, 0x00U, // 49 '1'
0x00U, 0x18U, 0x2CU, 0x08U, 0x08U, 0x10U, 0x18U, 0x00U, // 50 '2'
0x00U, 0x18U, 0x2CU, 0x08U, 0x0CU, 0x24U, 0x18U, 0x00U, // 51 '3'
0x00U, 0x00U, 0x10U, 0x10U, 0x3CU, 0x1CU, 0x00U, 0x00U, // 52 '4'
0x00U, 0x18U, 0x20U, 0x38U, 0x04U, 0x3CU, 0x18U, 0x00U, // 53 '5'
0x00U, 0x00U, 0x10U, 0x38U, 0x24U, 0x3CU, 0x18U, 0x00U, // 54 '6'
0x00U, 0x18U, 0x0CU, 0x08U, 0x18U, 0x10U, 0x00U, 0x00U, // 55 '7'
0x00U, 0x18U, 0x24U, 0x18U, 0x3CU, 0x24U, 0x18U, 0x00U, // 56 '8'
0x00U, 0x18U, 0x2CU, 0x24U, 0x18U, 0x18U, 0x00U, 0x00U, // 57 '9'
0x00U, 0x00U, 0x18U, 0x00U, 0x00U, 0x18U, 0x00U, 0x00U, // 58 ':'
0x00U, 0x00U, 0x18U, 0x00U, 0x10U, 0x10U, 0x00U, 0x00U, // 59 ';'
0x00U, 0x00U, 0x08U, 0x10U, 0x18U, 0x08U, 0x00U, 0x00U, // 60 '<'
0x00U, 0x00U, 0x18U, 0x18U, 0x18U, 0x00U, 0x00U, 0x00U, // 61 '='
0x00U, 0x00U, 0x10U, 0x08U, 0x18U, 0x10U, 0x00U, 0x00U, // 62 '>'
0x00U, 0x18U, 0x0CU, 0x08U, 0x00U, 0x10U, 0x00U, 0x00U, // 63 '?'
0x00U, 0x18U, 0x24U, 0x3CU, 0x3CU, 0x20U, 0x18U, 0x00U, // 64 '@'
0x00U, 0x00U, 0x18U, 0x18U, 0x38U, 0x24U, 0x00U, 0x00U, // 65 'A'
0x00U, 0x18U, 0x2CU, 0x38U, 0x2CU, 0x24U, 0x18U, 0x00U, // 66 'B'
0x00U, 0x18U, 0x34U, 0x20U, 0x20U, 0x34U, 0x18U, 0x00U, // 67 'C'
0x00U, 0x18U, 0x2CU, 0x24U, 0x24U, 0x2CU, 0x18U, 0x00U, // 68 'D'
0x00U, 0x18U, 0x30U, 0x30U, 0x30U, 0x30U, 0x18U, 0x00U, // 69 'E'
0x00U, 0x18U, 0x30U, 0x30U, 0x38U, 0x20U, 0x00U, 0x00U, // 70 'F'
0x00U, 0x18U, 0x34U, 0x20U, 0x2CU, 0x34U, 0x18U, 0x00U, // 71 'G'
0x00U, 0x00U, 0x24U, 0x3CU, 0x2CU, 0x24U, 0x00U, 0x00U, // 72 'H'
0x00U, 0x18U, 0x18U, 0x10U, 0x10U, 0x18U, 0x18U, 0x00U, // 73 'I'
0x00U, 0x18U, 0x0CU, 0x0CU, 0x0CU, 0x38U, 0x10U, 0x00U, // 74 'J'
0x00U, 0x00U, 0x28U, 0x30U, 0x38U, 0x2CU, 0x00U, 0x00U, // 75 'K'
0x00U, 0x00U, 0x30U, 0x30U, 0x20U, 0x30U, 0x18U, 0x00U, // 76 'L'
0x00U, 0x00U, 0x2CU, 0x3CU, 0x3CU, 0x24U, 0x00U, 0x00U, // 77 'M'
0x00U, 0x00U, 0x34U, 0x34U, 0x2CU, 0x2CU, 0x00U, 0x00U, // 78 'N'
0x00U, 0x18U, 0x24U, 0x24U, 0x24U, 0x24U, 0x18U, 0x00U, // 79 'O'
0x00U, 0x18U, 0x34U, 0x24U, 0x38U, 0x20U, 0x00U, 0x00U, // 80 'P'
0x00U, 0x18U, 0x24U, 0x24U, 0x24U, 0x3CU, 0x0CU, 0x00U, // 81 'Q'
0x00U, 0x18U, 0x2CU, 0x24U, 0x38U, 0x2CU, 0x00U, 0x00U, // 82 'R'
0x00U, 0x18U, 0x24U, 0x30U, 0x0CU, 0x24U, 0x18U, 0x00U, // 83 'S'
0x00U, 0x18U, 0x18U, 0x10U, 0x18U, 0x18U, 0x00U, 0x00U, // 84 'T'
0x00U, 0x00U, 0x24U, 0x24U, 0x24U, 0x3CU, 0x18U, 0x00U, // 85 'U'
0x00U, 0x00U, 0x24U, 0x28U, 0x18U, 0x18U, 0x00U, 0x00U, // 86 'V'
0x00U, 0x00U, 0x24U, 0x3CU, 0x3CU, 0x3CU, 0x00U, 0x00U, // 87 'W'
0x00U, 0x00U, 0x3CU, 0x18U, 0x18U, 0x2CU, 0x00U, 0x00U, // 88 'X'
0x00U, 0x00U, 0x24U, 0x18U, 0x18U, 0x18U, 0x00U, 0x00U, // 89 'Y'
0x00U, 0x1CU, 0x0CU, 0x08U, 0x10U, 0x30U, 0x38U, 0x00U, // 90 'Z'
0x00U, 0x18U, 0x10U, 0x10U, 0x10U, 0x10U, 0x18U, 0x00U, // 91 '['
0x00U, 0x00U, 0x10U, 0x10U, 0x08U, 0x08U, 0x00U, 0x00U, // 92 '\'
0x00U, 0x18U, 0x08U, 0x08U, 0x08U, 0x08U, 0x18U, 0x00U, // 93 ']'
0x00U, 0x00U, 0x18U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, // 94 '^'
0x00U, 0x00U, 0x00U, 0x00U, 0x38U, 0x00U, 0x00U, 0x00U, // 95 '_'
0x00U, 0x10U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, // 96 '`'
0x00U, 0x00U, 0x18U, 0x1CU, 0x3CU, 0x18U, 0x00U, 0x00U, // 97 'a'
0x00U, 0x00U, 0x30U, 0x38U, 0x24U, 0x3CU, 0x18U, 0x00U, // 98 'b'
0x00U, 0x00U, 0x18U, 0x20U, 0x20U, 0x18U, 0x00U, 0x00U, // 99 'c'
0x00U, 0x00U, 0x0CU, 0x3CU, 0x24U, 0x2CU, 0x18U, 0x00U, // 100 'd'
0x00U, 0x00U, 0x18U, 0x3CU, 0x30U, 0x18U, 0x00U, 0x00U, // 101 'e'
0x00U, 0x08U, 0x18U, 0x18U, 0x10U, 0x10U, 0x00U, 0x00U, // 102 'f'
0x00U, 0x18U, 0x2CU, 0x24U, 0x3CU, 0x1CU, 0x10U, 0x00U, // 103 'g'
0x00U, 0x00U, 0x20U, 0x38U, 0x24U, 0x24U, 0x00U, 0x00U, // 104 'h'
0x00U, 0x08U, 0x00U, 0x18U, 0x08U, 0x18U, 0x18U, 0x00U, // 105 'i'
0x00U, 0x08U, 0x18U, 0x08U, 0x08U, 0x08U, 0x18U, 0x00U, // 106 'j'
0x00U, 0x00U, 0x10U, 0x18U, 0x18U, 0x18U, 0x00U, 0x00U, // 107 'k'
0x00U, 0x10U, 0x18U, 0x08U, 0x08U, 0x18U, 0x18U, 0x00U, // 108 'l'
0x00U, 0x00U, 0x3CU, 0x3CU, 0x3CU, 0x24U, 0x00U, 0x00U, // 109 'm'
0x00U, 0x00U, 0x38U, 0x24U, 0x24U, 0x20U, 0x00U, 0x00U, // 110 'n'
0x00U, 0x00U, 0x18U, 0x24U, 0x24U, 0x18U, 0x00U, 0x00U, // 111 'o'
0x00U, 0x18U, 0x3CU, 0x24U, 0x38U, 0x30U, 0x00U, 0x00U, // 112 'p'
0x00U, 0x18U, 0x2CU, 0x24U, 0x3CU, 0x0CU, 0x00U, 0x00U, // 113 'q'
0x00U, 0x00U, 0x1CU, 0x10U, 0x10U, 0x18U, 0x00U, 0x00U, // 114 'r'
0x00U, 0x00U, 0x18U, 0x10U, 0x0CU, 0x18U, 0x00U, 0x00U, // 115 's'
0x00U, 0x00U, 0x18U, 0x10U, 0x10U, 0x18U, 0x08U, 0x00U, // 116 't'
0x00U, 0x00U, 0x24U, 0x24U, 0x2CU, 0x18U, 0x00U, 0x00U, // 117 'u'
0x00U, 0x00U, 0x24U, 0x18U, 0x18U, 0x18U, 0x00U, 0x00U, // 118 'v'
0x00U, 0x00U, 0x3CU, 0x3CU, 0x3CU, 0x18U, 0x00U, 0x00U, // 119 'w'
0x00U, 0x00U, 0x28U, 0x18U, 0x18U, 0x00U, 0x00U, 0x00U, // 120 'x'
0x00U, 0x00U, 0x2CU, 0x18U, 0x18U, 0x10U, 0x00U, 0x00U, // 121 'y'
0x00U, 0x00U, 0x1CU, 0x08U, 0x10U, 0x18U, 0x00U, 0x00U, // 122 'z'
0x00U, 0x08U, 0x08U, 0x10U, 0x10U, 0x08U, 0x08U, 0x00U, // 123 '{'
0x00U, 0x18U, 0x18U, 0x18U, 0x18U, 0x18U, 0x18U, 0x00U, // 124 '|'
0x00U, 0x10U, 0x10U, 0x08U, 0x08U, 0x10U, 0x10U, 0x00U, // 125 '}'
0x00U, 0x00U, 0x10U, 0x3CU, 0x00U, 0x00U, 0x00U, 0x00U, // 126 '~'
};
struct GuiColor GuiColor::rgba(unsigned char r, unsigned char g, unsigned char b, unsigned char a) {
struct GuiColor c;
c.r = r; c.g = g; c.b = b; c.a = a;
return c;
}
struct GuiColor GuiColor::rgb(unsigned char r, unsigned char g, unsigned char b) {
return GuiColor::rgba(r, g, b, 255U);
}
struct GuiColor GuiColor::hex(unsigned int rrggbb) {
return GuiColor::rgba((unsigned char)((rrggbb >> 16) & 0xFFU), (unsigned char)((rrggbb >> 8) & 0xFFU),
(unsigned char)(rrggbb & 0xFFU), (unsigned char)255);
}
struct GuiColor GuiColor::hexa(unsigned int v) {
return GuiColor::rgba((unsigned char)((v >> 24) & 0xFFU), (unsigned char)((v >> 16) & 0xFFU),
(unsigned char)((v >> 8) & 0xFFU), (unsigned char)(v & 0xFFU));
}
static unsigned char __gui_unit_byte(float f) {
if (!(f > 0.0f)) { return (unsigned char)0; }
if (f >= 1.0f) { return (unsigned char)255; }
return (unsigned char)(int)(f * 255.0f + 0.5f);
}
struct GuiColor GuiColor::rgbaf(float r, float g, float b, float a) {
return GuiColor::rgba(__gui_unit_byte(r), __gui_unit_byte(g), __gui_unit_byte(b), __gui_unit_byte(a));
}
struct GuiColor GuiColor::hsv(float h, float s, float v, float a) {
// h in degrees, wrapped into [0, 360).
float hh = h;
while (hh < 0.0f) { hh = hh + 360.0f; }
while (hh >= 360.0f) { hh = hh - 360.0f; }
if (s < 0.0f) { s = 0.0f; }
if (s > 1.0f) { s = 1.0f; }
if (v < 0.0f) { v = 0.0f; }
if (v > 1.0f) { v = 1.0f; }
float c = v * s;
float hp = hh / 60.0f;
int sector = (int)hp;
float frac = hp - (float)(sector / 2 * 2);
float x = c * (1.0f - (frac - 1.0f < 0.0f ? 1.0f - frac : frac - 1.0f));
float r = 0.0f; float g = 0.0f; float b = 0.0f;
if (sector == 0) { r = c; g = x; }
else if (sector == 1) { r = x; g = c; }
else if (sector == 2) { g = c; b = x; }
else if (sector == 3) { g = x; b = c; }
else if (sector == 4) { r = x; b = c; }
else { r = c; b = x; }
float m = v - c;
return GuiColor::rgbaf(r + m, g + m, b + m, a);
}
void GuiColor::to_hsv(&float h, &float s, &float v) const {
float r = (float)self.r / 255.0f;
float g = (float)self.g / 255.0f;
float b = (float)self.b / 255.0f;
float mx = r > g ? (r > b ? r : b) : (g > b ? g : b);
float mn = r < g ? (r < b ? r : b) : (g < b ? g : b);
float d = mx - mn;
float hh = 0.0f;
if (d > 0.0f) {
if (mx == r) { hh = 60.0f * ((g - b) / d); }
else if (mx == g) { hh = 60.0f * ((b - r) / d + 2.0f); }
else { hh = 60.0f * ((r - g) / d + 4.0f); }
if (hh < 0.0f) { hh = hh + 360.0f; }
}
*h = hh;
*s = mx > 0.0f ? d / mx : 0.0f;
*v = mx;
}
struct GuiColor GuiColor::with_alpha(float f) const {
if (f < 0.0f) { f = 0.0f; }
if (f > 1.0f) { f = 1.0f; }
return GuiColor::rgba(self.r, self.g, self.b, (unsigned char)(int)((float)self.a * f + 0.5f));
}
struct GuiColor GuiColor::mix(struct GuiColor o, float t) const {
if (t < 0.0f) { t = 0.0f; }
if (t > 1.0f) { t = 1.0f; }
float u = 1.0f - t;
return GuiColor::rgba((unsigned char)(int)((float)self.r * u + (float)o.r * t + 0.5f),
(unsigned char)(int)((float)self.g * u + (float)o.g * t + 0.5f),
(unsigned char)(int)((float)self.b * u + (float)o.b * t + 0.5f),
(unsigned char)(int)((float)self.a * u + (float)o.a * t + 0.5f));
}
struct GuiColor GuiColor::shade(float amount) const {
struct GuiColor target = amount >= 0.0f ? GuiColor::rgba((unsigned char)255, (unsigned char)255, (unsigned char)255, self.a)
: GuiColor::rgba((unsigned char)0, (unsigned char)0, (unsigned char)0, self.a);
return self.mix(target, amount >= 0.0f ? amount : -amount);
}
unsigned int GuiColor::packed() const {
return ((unsigned int)self.r << 24) | ((unsigned int)self.g << 16) | ((unsigned int)self.b << 8) | (unsigned int)self.a;
}
static unsigned int __gui_pack(struct GuiColor c) {
unsigned int r = (unsigned int)c.r;
unsigned int g = (unsigned int)c.g;
unsigned int b = (unsigned int)c.b;
unsigned int a = (unsigned int)c.a;
return (r << 24) | (g << 16) | (b << 8) | a;
}
void GuiWindow::fill_rect(int x, int y, int w, int h, struct GuiColor color) {
unsigned int rgbaPacked = __gui_pack(color);
int yy = y;
while (yy < y + h) {
int xx = x;
while (xx < x + w) {
self.set_pixel(xx, yy, rgbaPacked);
xx = xx + 1;
}
yy = yy + 1;
}
}
void GuiWindow::draw_rect_border(int x, int y, int w, int h,
struct GuiColor color, int thickness) {
self.fill_rect(x, y, w, thickness, color);
self.fill_rect(x, y + h - thickness, w, thickness, color);
self.fill_rect(x, y, thickness, h, color);
self.fill_rect(x + w - thickness, y, thickness, h, color);
}
void GuiWindow::draw_line(int x0, int y0, int x1, int y1, struct GuiColor color) {
unsigned int rgbaPacked = __gui_pack(color);
int dx = x1 - x0; if (dx < 0) { dx = -dx; }
int dy = y1 - y0; if (dy < 0) { dy = -dy; }
int sx = (x0 < x1) ? 1 : -1;
int sy = (y0 < y1) ? 1 : -1;
int err = dx - dy;
int cx = x0; int cy = y0;
int guard = 0;
while (1) {
self.set_pixel(cx, cy, rgbaPacked);
if (cx == x1 && cy == y1) { break; }
int e2 = 2 * err;
if (e2 > -dy) { err = err - dy; cx = cx + sx; }
if (e2 < dx) { err = err + dx; cy = cy + sy; }
guard = guard + 1;
if (guard > 100000) { break; }
}
}
static void __gui_draw_glyph(&GuiWindow win, int x, int y, char ch,
struct GuiColor color, int scale) {
int code = (int)(unsigned char)ch;
if (code < GUI_FONT_FIRST || code > GUI_FONT_LAST) { return; }
int idx = code - GUI_FONT_FIRST;
unsigned int rgbaPacked = __gui_pack(color);
int row = 0;
while (row < 8) {
unsigned char bits;
unsafe { bits = gFont8x8[idx * 8 + row]; }
int col = 0;
while (col < 8) {
int bit = (int)((bits >> (7 - col)) & 1U);
if (bit) {
win.fill_rect(x + col * scale, y + row * scale, scale, scale, color);
}
col = col + 1;
}
row = row + 1;
}
(void)rgbaPacked;
}
int GuiWindow::draw_text(int x, int y, const &char text,
struct GuiColor color, int scale) {
int cx = x;
unsigned long i = 0UL;
unsafe {
while (text[i] != '\0') {
__gui_draw_glyph(self, cx, y, text[i], color, scale);
cx = cx + 8 * scale;
i = i + 1UL;
}
}
return cx - x;
}
int gui_text_width(const &char text, int scale) {
unsigned long len = 0UL;
unsafe {
while (text[len] != '\0') { len = len + 1UL; }
}
return (int)len * 8 * scale;
}
int gui_text_height(int scale) {
return 8 * scale;
}
} // namespace std