std1.0.0-beta-7 SafeC docs

Source gui/gui_draw.sc

View in the repository

// 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