diff options
-rw-r--r-- | Dockerfile | 2 | ||||
-rwxr-xr-x | build_all.sh | 19 | ||||
-rw-r--r-- | examples/pixels.c | 44 | ||||
-rw-r--r-- | graphics.c | 518 | ||||
-rw-r--r-- | index.htm | 1 | ||||
-rwxr-xr-x | reduced_system_layer.c | 186 |
6 files changed, 607 insertions, 163 deletions
@@ -5,7 +5,7 @@ RUN apk add clang lld COPY examples /usr/examples COPY reduced_system_layer.c /usr/reduced_system_layer.c COPY graphics.c /usr/graphics.c -RUN clang --target=wasm32 -nostdlib -fno-builtin -mbulk-memory -Wl,--no-entry,--allow-undefined -o /usr/index.wasm /usr/examples/ui.c +RUN clang --target=wasm32 -O3 -nostdlib -fno-builtin -mbulk-memory -Wl,--no-entry,--allow-undefined -o /usr/index.wasm /usr/examples/ui.c FROM nginx:alpine EXPOSE 80 diff --git a/build_all.sh b/build_all.sh index 619cbcb..8fae799 100755 --- a/build_all.sh +++ b/build_all.sh @@ -14,12 +14,13 @@ if [ ! -d ./bin ]; then mkdir ./bin fi -gcc $FLAGS -o ./bin/graph ./examples/graph.c -gcc $FLAGS -o ./bin/particles ./examples/particles.c -gcc $FLAGS -o ./bin/julia_set ./examples/julia_set.c -gcc $FLAGS -o ./bin/game_of_life ./examples/game_of_life.c -gcc $FLAGS -o ./bin/labyrinth ./examples/labyrinth.c -gcc $FLAGS -o ./bin/sinewave ./examples/sinewave.c -gcc $FLAGS -o ./bin/ui ./examples/ui.c -gcc $FLAGS -o ./bin/echo ./examples/echo.c -gcc $FLAGS -o ./bin/proto ./examples/proto.c +# gcc $FLAGS -o ./bin/graph ./examples/graph.c +# gcc $FLAGS -o ./bin/particles ./examples/particles.c +# gcc $FLAGS -o ./bin/julia_set ./examples/julia_set.c +# gcc $FLAGS -o ./bin/game_of_life ./examples/game_of_life.c +# gcc $FLAGS -o ./bin/labyrinth ./examples/labyrinth.c +# gcc $FLAGS -o ./bin/sinewave ./examples/sinewave.c +gcc $FLAGS -o ./bin/pixels ./examples/pixels.c +# gcc $FLAGS -o ./bin/ui ./examples/ui.c +# gcc $FLAGS -o ./bin/echo ./examples/echo.c +# gcc $FLAGS -o ./bin/proto ./examples/proto.c diff --git a/examples/pixels.c b/examples/pixels.c new file mode 100644 index 0000000..c47ac35 --- /dev/null +++ b/examples/pixels.c @@ -0,0 +1,44 @@ +#define MIN_PIXEL_SIZE 16 +#include "../graphics.c" + +i64 t = 0; +f64 x = 100.; +f64 y = 100.; + +void update_and_render_frame(void) { + p_handle_events(); + + i64 time_elapsed = p_time() - t; + t += time_elapsed; + + if (g_platform.key_down[KEY_LEFT]) x -= .01 * time_elapsed; + if (g_platform.key_down[KEY_RIGHT]) x += .01 * time_elapsed; + if (g_platform.key_down[KEY_UP]) y -= .01 * time_elapsed; + if (g_platform.key_down[KEY_DOWN]) y += .01 * time_elapsed; + + Brush white = RGB(1.f, 1.f, 1.f); + Brush black = RGB(0.f, 0.f, 0.f); + + black.antialiasing = 1; + + fill_rectangle(white, 0., 0., g_platform.real_width, g_platform.real_height); + fill_rectangle(black, x, y, 40., 40.); + + p_render_frame(); + p_sleep_for(0); +} + +i32 main(i32 argc, c8 **argv) { + (void) argc; + (void) argv; + + g_platform = (Platform) { + .title = "Pixels", + }; + + t = p_time(); + + p_event_loop(); + + return 0; +} @@ -17,24 +17,58 @@ #include "reduced_system_layer.c" -#define EPSILON 1e-9 +// ================================================================ -enum { - OP_SET, - OP_XOR, -}; +#ifndef EPSILON +#define EPSILON (1e-9) +#endif + +typedef struct { + i64 width; + i64 height; + vec4_f32 *pixels; +} Pixel_Buffer; + +typedef struct { + Pixel_Buffer buffer; + vec2 position; + vec2 scale; + b8 antialiasing : 1; + b8 alpha : 1; + b8 xor_color : 1; + vec4_f32 color; +} Brush; + +vec3_f32 gamma_apply (vec3_f32 rgb); +vec3_f32 gamma_revert(vec3_f32 rgb); +vec3_f32 lab_from_rgb(vec3_f32 rgb); +vec3_f32 rgb_from_lab(vec3_f32 lab); +vec3_f32 lab_from_lch(vec3_f32 lch); +vec3_f32 lch_from_lab(vec3_f32 lab); +vec3_f32 rgb_from_lch(vec3_f32 lch); +vec3_f32 lch_from_rgb(vec3_f32 rgb); + +vec4_f32 with_alpha(vec3_f32 color, f32 alpha); +vec3_f32 without_alpha(vec4_f32 color); + +#define RGB(...) ((Brush) { .color = { __VA_ARGS__, 1.f } }) +#define RGBA(...) ((Brush) { .color = { __VA_ARGS__ } }) +#define LCH(...) ((Brush) { .color = with_alpha(gamma_apply(rgb_from_lch((vec3_f32) { __VA_ARGS__ })), 1.f) } }) +#define LCHA(...) ((Brush) { .color = with_alpha(gamma_apply(rgb_from_lch(without_alpha((vec4_f32) { __VA_ARGS__ }))), ((vec4_f32) { __VA_ARGS__ }).w) } }) b8 rectangle_contains(f64 x0, f64 y0, f64 width, f64 height, f64 px, f64 py); b8 triangle_contains (f64 x0, f64 y0, f64 x1, f64 y1, f64 x2, f64 y2, f64 px, f64 py); 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 fill_rectangle (u32 op, vec3_f32 color, f64 x0, f64 y0, f64 width, f64 height); -void fill_triangle (u32 op, vec3_f32 color, f64 x0, f64 y0, f64 x1, f64 y1, f64 x2, f64 y2); -void fill_ellipse (u32 op, vec3_f32 color, f64 x0, f64 y0, f64 width, f64 height); -void fill_line (u32 op, vec3_f32 color, f64 x0, f64 y0, f64 x1, f64 y1, f64 width); -void draw_text_area (vec3_f32 color, f64 x0, f64 y0, f64 width, f64 height, f64 max_scale_x, f64 max_scale_y, i64 num_chars, c32 *text); -void draw_selection_cursor(vec3_f32 color, f64 x0, f64 y0, 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_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); #endif // GRAPHICS_HEADER_GUARD_ @@ -44,7 +78,7 @@ void draw_selection_cursor(vec3_f32 color, f64 x0, f64 y0, f64 width, f64 height #ifndef GRAPHICS_IMPL_GUARD_ #define GRAPHICS_IMPL_GUARD_ -f64 min3(f64 a, f64 b, f64 c) { +static f64 min3(f64 a, f64 b, f64 c) { if (a < b && a < c) return a; if (b < c) @@ -52,7 +86,7 @@ f64 min3(f64 a, f64 b, f64 c) { return c; } -f64 max3(f64 a, f64 b, f64 c) { +static f64 max3(f64 a, f64 b, f64 c) { if (a > b && a > c) return a; if (b > c) @@ -60,7 +94,7 @@ f64 max3(f64 a, f64 b, f64 c) { return c; } -b8 same_sign(f64 a, f64 b) { +static b8 same_sign(f64 a, f64 b) { if (a >= EPSILON && b <= -EPSILON) return 0; if (a <= -EPSILON && b >= EPSILON) return 0; return 1; @@ -74,13 +108,13 @@ static u64 _bitfont[] = { #define CHAR_NUM_BITS_Y 7 #define CHAR_NUM_BITS (CHAR_NUM_BITS_X * CHAR_NUM_BITS_Y) -i64 char_column_offset(c32 c, i64 column_index) { +static i64 char_column_offset(c32 c, i64 column_index) { if (column_index < 0 || column_index >= CHAR_NUM_BITS_X) return -1; return (c - 32) * CHAR_NUM_BITS + column_index * CHAR_NUM_BITS_Y; } -b8 char_bit(i64 column_offset, i64 row_index) { +static b8 char_bit(i64 column_offset, i64 row_index) { if (column_offset < 0 || row_index < 0 || row_index >= CHAR_NUM_BITS_Y) return 0; @@ -92,7 +126,7 @@ b8 char_bit(i64 column_offset, i64 row_index) { return !!(_bitfont[qword_index] & mask); } -u64 char_column_convolved(c32 c, i64 column_index) { +static u64 char_column_convolved(c32 c, i64 column_index) { if (column_index < 0 || column_index >= CHAR_NUM_BITS_X) return 0; @@ -106,7 +140,7 @@ u64 char_column_convolved(c32 c, i64 column_index) { return column; } -b8 char_column_empty(c32 c, i64 column_index) { +static b8 char_column_empty(c32 c, i64 column_index) { if (column_index < 0 || column_index >= CHAR_NUM_BITS_X) return 1; @@ -119,7 +153,7 @@ b8 char_column_empty(c32 c, i64 column_index) { return 1; } -i64 char_width(c32 c) { +static i64 char_width(c32 c) { if (c < 32) return 0; if (c == ' ' || c > 127) @@ -134,7 +168,7 @@ i64 char_width(c32 c) { return width; } -i64 char_spacing(i64 num_chars, c32 *text, i64 index) { +static i64 char_spacing(i64 num_chars, c32 *text, i64 index) { if (text == NULL) return 0; @@ -150,7 +184,7 @@ i64 char_spacing(i64 num_chars, c32 *text, i64 index) { return 0; } -i64 text_cursor(i64 num_chars, c32 *text) { +static i64 text_cursor(i64 num_chars, c32 *text) { if (text == NULL) return 0; @@ -174,7 +208,7 @@ i64 text_cursor(i64 num_chars, c32 *text) { return cursor; } -i64 enum_text_columns(i64 num_chars, c32 *text) { +static i64 enum_text_columns(i64 num_chars, c32 *text) { if (text == NULL) return 0; @@ -208,7 +242,7 @@ i64 enum_text_columns(i64 num_chars, c32 *text) { return cols; } -i64 enum_text_rows(i64 num_chars, c32 *text) { +static i64 enum_text_rows(i64 num_chars, c32 *text) { if (text == NULL) return 0; @@ -224,12 +258,35 @@ i64 enum_text_rows(i64 num_chars, c32 *text) { return rows; } -void draw_text(vec3_f32 color, f64 x0, f64 y0, f64 scale_x, f64 scale_y, i64 num_chars, c32 *text) { +static void brush_defaults(Brush *b) { + if (g_platform.frame_width > 0) { + if (b->scale.x == 0.) b->scale.x = ((f64) g_platform.frame_width) / g_platform.real_width; + if (b->scale.y == 0.) b->scale.y = ((f64) g_platform.frame_height) / g_platform.real_height; + } + + if (b->buffer.pixels == NULL) { + b->buffer.width = g_platform.frame_width; + b->buffer.height = g_platform.frame_height; + b->buffer.pixels = g_platform.pixels; + } +} + +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; - f64 x = x0; - f64 y = y0; + brush_defaults(&brush); + + x_ = brush.position.x + x_ * brush.scale.x; + y_ = brush.position.y + y_ * brush.scale.y; + + if (brush.scale.x < 0) scale_x *= -brush.scale.x; + else scale_x *= brush.scale.x; + if (brush.scale.y < 0) scale_y *= -brush.scale.y; + else scale_y *= brush.scale.y; + + f64 x = x_; + f64 y = y_; f64 kx = scale_x; f64 h = scale_y * CHAR_NUM_BITS_Y; @@ -237,13 +294,13 @@ void draw_text(vec3_f32 color, f64 x0, f64 y0, f64 scale_x, f64 scale_y, i64 num for (i64 n = 0; n < num_chars; ++n) { if (text[n] <= ' ') { if (text[n] == '\n') { - x = x0; + x = x_; y += scale_y * (CHAR_NUM_BITS_Y + 1); } else if (text[n] == '\b' && n > 0) x -= kx * (char_width(text[n - 1]) + char_spacing(num_chars, text, n - 1)); else if (text[n] == '\r') - x = x0; + x = x_; else x += kx * (char_width(' ') + char_spacing(num_chars, text, n)); continue; @@ -252,10 +309,10 @@ void draw_text(vec3_f32 color, f64 x0, f64 y0, f64 scale_x, f64 scale_y, i64 num i64 num_cols = char_width(text[n]); f64 w = num_cols * kx; - i64 i0 = (i64) floor(x + .5); - i64 i1 = (i64) floor(x + w + .5); - i64 j0 = (i64) floor(y + .5); - i64 j1 = (i64) floor(y + h + .5); + i64 i0 = (i64) floor(x); + i64 i1 = (i64) ceil (x + w); + i64 j0 = (i64) floor(y); + i64 j1 = (i64) ceil (y + h); for (i64 i = i0; i < i1; ++i) { if (i < 0) continue; @@ -271,7 +328,7 @@ void draw_text(vec3_f32 color, f64 x0, f64 y0, f64 scale_x, f64 scale_y, i64 num i64 row = ((j - j0) * CHAR_NUM_BITS_Y) / (j1 - j0); if (char_bit(offset, row)) - g_platform.pixels[j * g_platform.frame_width + i] = color; + put_pixel(brush, i, j); } } @@ -279,13 +336,110 @@ void draw_text(vec3_f32 color, f64 x0, f64 y0, f64 scale_x, f64 scale_y, i64 num } } -void put_pixel(i64 i, i64 j, u32 op, vec3_f32 color) { - if (i < 0 || i >= g_platform.frame_width || j < 0 || j >= g_platform.frame_height) - return; - if (op == OP_XOR) - g_platform.pixels[j * g_platform.frame_width + i] = rgb_f32_from_u32(rgb_u32_from_f32(color) ^ rgb_u32_from_f32(g_platform.pixels[j * g_platform.frame_width + i])); - else - g_platform.pixels[j * g_platform.frame_width + i] = color; +static f64 gamma_(f64 x) { + if (x >= 0.0031308) + return 1.055 * pow(x, 1.0 / 2.4) - 0.055; + return 12.92 * x; +} + +static f64 gamma_re_(f64 x) { + if (x >= 0.04045) + return pow((x + 0.055) / 1.055, 2.4); + return x / 12.92; +} + +// ================================================================ + +vec3_f32 gamma_apply (vec3_f32 rgb) { + return (vec3_f32) { + .x = (f32) gamma_(rgb.x), + .y = (f32) gamma_(rgb.y), + .z = (f32) gamma_(rgb.z), + }; +} + +vec3_f32 gamma_revert(vec3_f32 rgb) { + return (vec3_f32) { + .x = (f32) gamma_re_(rgb.x), + .y = (f32) gamma_re_(rgb.y), + .z = (f32) gamma_re_(rgb.z), + }; +} + +vec3_f32 lab_from_rgb(vec3_f32 rgb) { + f64 l = 0.4122214708 * rgb.x + 0.5363325363 * rgb.y + 0.0514459929 * rgb.z; + f64 m = 0.2119034982 * rgb.x + 0.6806995451 * rgb.y + 0.1073969566 * rgb.z; + f64 s = 0.0883024619 * rgb.x + 0.2817188376 * rgb.y + 0.6299787005 * rgb.z; + + f64 l_ = cbrt(l); + f64 m_ = cbrt(m); + f64 s_ = cbrt(s); + + return (vec3_f32) { + .x = (f32) (0.2104542553f * l_ + 0.7936177850f * m_ - 0.0040720468f * s_), + .y = (f32) (1.9779984951f * l_ - 2.4285922050f * m_ + 0.4505937099f * s_), + .z = (f32) (0.0259040371f * l_ + 0.7827717662f * m_ - 0.8086757660f * s_), + }; +} + +vec3_f32 rgb_from_lab(vec3_f32 lab) { + f64 l_ = lab.x + 0.3963377774 * lab.y + 0.2158037573 * lab.z; + f64 m_ = lab.x - 0.1055613458 * lab.y - 0.0638541728 * lab.z; + f64 s_ = lab.x - 0.0894841775 * lab.y - 1.2914855480 * lab.z; + + f64 l = l_ * l_ * l_; + f64 m = m_ * m_ * m_; + f64 s = s_ * s_ * s_; + + return (vec3_f32) { + .x = (f32) (+4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s), + .y = (f32) (-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s), + .z = (f32) (-0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s), + }; +} + +vec3_f32 lch_from_lab(vec3_f32 lab) { + f64 a = lab.y; + f64 b = lab.z; + + return (vec3_f32) { + .x = lab.x, + .y = (f32) sqrt (a * a + b * b), + .z = (f32) atan2(b, a), + }; +} + +vec3_f32 lab_from_lch(vec3_f32 lch) { + return (vec3_f32) { + .x = lch.x, + .y = (f32) (lch.y * cos(lch.z)), + .z = (f32) (lch.y * sin(lch.z)), + }; +} + +vec3_f32 rgb_from_lch(vec3_f32 lch) { + return rgb_from_lab(lab_from_lch(lch)); +} + +vec3_f32 lch_from_rgb(vec3_f32 rgb) { + return lch_from_lab(lab_from_rgb(rgb)); +} + +vec4_f32 with_alpha(vec3_f32 color, f32 alpha) { + return (vec4_f32) { + .x = color.x, + .y = color.y, + .z = color.z, + .w = alpha, + }; +} + +vec3_f32 without_alpha(vec4_f32 color) { + return (vec3_f32) { + .x = color.x, + .y = color.y, + .z = color.z, + }; } b8 rectangle_contains(f64 x0, f64 y0, f64 width, f64 height, f64 px, f64 py) { @@ -349,26 +503,190 @@ b8 line_contains(f64 x0, f64 y0, f64 x1, f64 y1, f64 width, f64 px, f64 py) { || triangle_contains(x0 - tx, y0 - ty, x1 + tx, y1 + ty, x1 - tx, y1 - ty, px, py); } -void fill_rectangle(u32 op, vec3_f32 color, f64 x0, f64 y0, f64 width, f64 height) { - i64 i0 = (i64) floor(x0 + .5); - i64 j0 = (i64) floor(y0 + .5); - i64 i1 = (i64) floor(x0 + width + .5); - i64 j1 = (i64) floor(y0 + height + .5); +void put_pixel(Brush brush, i64 x, i64 y) { + brush_defaults(&brush); - if (i0 < 0) i0 = 0; - if (j0 < 0) j0 = 0; - if (i1 >= g_platform.frame_width) i1 = g_platform.frame_width - 1; - if (j1 >= g_platform.frame_height) j1 = g_platform.frame_height - 1; + if (x < 0 || x >= brush.buffer.width || y < 0 || y >= brush.buffer.height) + return; - for (i64 j = j0; j < j1; ++j) + 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; +} + +void draw_pixels(Brush brush, f64 x, f64 y, f64 width, f64 height, Pixel_Buffer src) { + // FIXME PERF + + 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; + } + + i64 i0 = (i64) floor(x0); + i64 i1 = (i64) ceil (x1); + i64 j0 = (i64) floor(y0); + i64 j1 = (i64) ceil (y1); + + for (i64 j = j0; j < j1; ++j) { + i64 src_j = ((j - j0) * src.height) / (j1 - j0); + 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]; + 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.antialiasing) { + 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(i, j, op, color); + 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); + + for (i64 j = j0; j < j1; ++j) + for (i64 i = i0; i < i1; ++i) + put_pixel(brush, i, j); + } } -void fill_triangle(u32 op, vec3_f32 color, f64 x0, f64 y0, f64 x1, f64 y1, f64 x2, f64 y2) { +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)); @@ -377,31 +695,46 @@ void fill_triangle(u32 op, vec3_f32 color, f64 x0, f64 y0, f64 x1, f64 y1, f64 x 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(i, j, op, color); + put_pixel(brush, i, j); } -void fill_ellipse(u32 op, vec3_f32 color, f64 x0, f64 y0, f64 width, f64 height) { +void fill_ellipse(Brush brush, f64 x, f64 y, f64 width, f64 height) { // FIXME PERF: // Implement better algorithm. - i64 i0 = (i64) floor(x0 + .5); - i64 j0 = (i64) floor(y0 + .5); - i64 i1 = (i64) floor(x0 + width + .5); - i64 j1 = (i64) floor(y0 + height + .5); + brush_defaults(&brush); - for (i64 j = j0; j < j1; ++j) { - if (j < 0 || j >= g_platform.frame_height) - continue; + 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; + } + + i64 i0 = (i64) floor(x0); + i64 i1 = (i64) ceil (x1); + i64 j0 = (i64) floor(y0); + i64 j1 = (i64) ceil (y1); + + for (i64 j = j0; j < j1; ++j) for (i64 i = i0; i < i1; ++i) { - if (i < 0 || i >= g_platform.frame_width) - continue; - if (ellipse_contains(x0, y0, width, height, (f64) i, (f64) j)) - put_pixel(i, j, op, color); + if (ellipse_contains(x0, y0, x1 - x0, y1 - y0, (f64) i, (f64) j)) + put_pixel(brush, i, j); } - } } -void fill_line(u32 op, vec3_f32 color, f64 x0, f64 y0, f64 x1, f64 y1, f64 width) { +void fill_line(Brush brush, f64 x0, f64 y0, f64 x1, f64 y1, f64 width) { f64 dx = x1 - x0; f64 dy = y1 - y0; @@ -417,12 +750,12 @@ void fill_line(u32 op, vec3_f32 color, f64 x0, f64 y0, f64 x1, f64 y1, f64 width tx *= width * .5; ty *= width * .5; - fill_triangle(op, color, x0 - tx, y0 - ty, x0 + tx, y0 + ty, x1 + tx, y1 + ty); - fill_triangle(op, color, x0 - tx, y0 - ty, x1 + tx, y1 + ty, x1 - tx, y1 - ty); + 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(vec3_f32 color, f64 x0, f64 y0, f64 width, f64 height, f64 max_scale_x, f64 max_scale_y, i64 num_chars, c32 *text) { - if (max_scale_x < 1e-6 || max_scale_y < 1e-6) +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) return; i64 num_columns = enum_text_columns(num_chars, text); @@ -439,17 +772,18 @@ void draw_text_area(vec3_f32 color, f64 x0, f64 y0, f64 width, f64 height, f64 m kx = k * max_scale_x; ky = k * max_scale_y; - draw_text(color, x0, y0, kx, ky, num_chars, text); + draw_text(brush, x, y, kx, ky, num_chars, text); } -void draw_selection_cursor(vec3_f32 color, f64 x0, f64 y0, 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 < 1e-6 || max_scale_y < 1e-6) +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) { + if (max_scale_x < EPSILON || max_scale_y < EPSILON) return; i64 num_columns = enum_text_columns(num_chars, text); i64 num_rows = enum_text_rows(num_chars, text); i64 cursor_x = text_cursor(cursor, text); i64 cursor_y = enum_text_rows(cursor, text); + f64 scale_x = width / num_columns; f64 scale_y = height / num_rows; @@ -475,35 +809,45 @@ void draw_selection_cursor(vec3_f32 color, f64 x0, f64 y0, f64 width, f64 height } if (cursor_y == selection_y) - fill_rectangle(OP_XOR, color, - x0 + kx * cursor_x, - y0 + ky * cursor_y - ky * (CHAR_NUM_BITS_Y + 1), + fill_rectangle( + brush, + x + kx * cursor_x, + y + ky * cursor_y - ky * (CHAR_NUM_BITS_Y + 1), kx * (selection_x - cursor_x), ky * (CHAR_NUM_BITS_Y + 1) ); else { - fill_rectangle(OP_XOR, color, - x0 + kx * cursor_x, - y0 + ky * cursor_y - ky * (CHAR_NUM_BITS_Y + 1), + fill_rectangle( + brush, + x + kx * cursor_x, + y + ky * cursor_y - ky * (CHAR_NUM_BITS_Y + 1), kx * (num_columns - cursor_x), 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(OP_XOR, color, - x0, - y0 + ky * j - ky * (CHAR_NUM_BITS_Y + 1), + fill_rectangle( + brush, + x, + y + ky * j - ky * (CHAR_NUM_BITS_Y + 1), kx * num_columns, ky * (CHAR_NUM_BITS_Y + 1) ); - fill_rectangle(OP_XOR, color, - x0, - y0 + ky * selection_y - ky * (CHAR_NUM_BITS_Y + 1), + fill_rectangle( + brush, + x, + y + ky * selection_y - ky * (CHAR_NUM_BITS_Y + 1), kx * selection_x, ky * (CHAR_NUM_BITS_Y + 1) ); } } else - fill_rectangle(OP_XOR, color, x0 + kx * cursor_x, y0 + ky * cursor_y - ky * CHAR_NUM_BITS_Y, kx * .5, ky * (CHAR_NUM_BITS_Y - 1)); + fill_rectangle( + brush, + x + kx * cursor_x, + y + ky * cursor_y - ky * CHAR_NUM_BITS_Y, + kx * .5, + ky * (CHAR_NUM_BITS_Y - 1) + ); } #endif // GRAPHICS_IMPL_GUARD_ @@ -47,6 +47,7 @@ floor : Math.floor, ceil : Math.ceil, sqrt : Math.sqrt, + cbrt : Math.cbrt, pow : Math.pow, log : Math.log, log2 : Math.log2, diff --git a/reduced_system_layer.c b/reduced_system_layer.c index 9eabca9..3d020bd 100755 --- a/reduced_system_layer.c +++ b/reduced_system_layer.c @@ -75,6 +75,7 @@ #/ - Graphics #/ - Font #/ - Adaptive resolution +#/ - Oklab color #/ #/ ---------------------------------------------------------------- #/ @@ -124,7 +125,7 @@ typedef unsigned u32; typedef unsigned long long u64; typedef char c8; typedef int c32; -typedef signed char b8; +typedef unsigned char b8; typedef float f32; typedef double f64; @@ -170,23 +171,59 @@ typedef struct { f64 v[16]; } mat4; extern "C" { #endif -enum { - MAX_NUM_PIXELS = 10 * 1024 * 1024, - MAX_INPUT_SIZE = 256, - MAX_CLIPBOARD_SIZE = 10 * 1024 * 1024, - MAX_NUM_SOCKETS = 64, - MAX_NUM_KEYS = 512, - MAX_PIXEL_SIZE = 16, +#ifndef MAX_NUM_PIXELS +#define MAX_NUM_PIXELS (10 * 1024 * 1024) +#endif - MIN_FRAME_DURATION = 9, - MAX_FRAME_DURATION = 38, +#ifndef MAX_INPUT_SIZE +#define MAX_INPUT_SIZE 256 +#endif - AUDIO_NUM_CHANNELS = 2, - AUDIO_SAMPLE_RATE = 44100, - AUDIO_AVAIL_MIN = 512, +#ifndef MAX_CLIPBOARD_SIZE +#define MAX_CLIPBOARD_SIZE (10 * 1024 * 1024) +#endif - MAX_NUM_AUDIO_FRAMES = 10 * AUDIO_SAMPLE_RATE * AUDIO_NUM_CHANNELS, // 10 seconds +#ifndef MAX_NUM_SOCKETS +#define MAX_NUM_SOCKETS 64 +#endif + +#ifndef MAX_NUM_KEYS +#define MAX_NUM_KEYS 512 +#endif + +#ifndef MIN_PIXEL_SIZE +#define MIN_PIXEL_SIZE 1 +#endif + +#ifndef MAX_PIXEL_SIZE +#define MAX_PIXEL_SIZE 16 +#endif + +#ifndef MIN_FRAME_DURATION +#define MIN_FRAME_DURATION 9 +#endif + +#ifndef MAX_FRAME_DURATION +#define MAX_FRAME_DURATION 38 +#endif + +#ifndef NUM_SOUND_CHANNELS +#define NUM_SOUND_CHANNELS 2 +#endif +#ifndef SOUND_SAMPLE_RATE +#define SOUND_SAMPLE_RATE 44100 +#endif + +#ifndef SOUND_AVAIL_MIN +#define SOUND_AVAIL_MIN 512 +#endif + +#ifndef MAX_NUM_SOUND_FRAMES +#define MAX_NUM_SOUND_FRAMES (10 * SOUND_SAMPLE_RATE * NUM_SOUND_CHANNELS) +#endif + +enum { IPv4_UDP = 1, IPv6_UDP = 2, @@ -329,15 +366,17 @@ typedef struct { typedef struct { c8 * title; + i32 real_width; + i32 real_height; i32 frame_width; i32 frame_height; - vec3_f32 * pixels; + vec4_f32 * pixels; i64 input_size; Input_Key *input; i64 clipboard_size; c8 * clipboard; b8 done; - b8 exact_resolution; // TODO + b8 exact_resolution; b8 graceful_exit; b8 has_focus; b8 has_cursor; @@ -408,6 +447,7 @@ i32 main(i32 argc, c8 **argv); f64 floor(f64 x); f64 ceil(f64 x); f64 sqrt(f64 x); +f64 cbrt(f64 x); f64 pow(f64 x, f64 y); f64 log(f64 x); f64 log2(f64 x); @@ -457,9 +497,9 @@ void update_and_render_frame(void) { for (i64 j = 0; j < g_platform.frame_height; ++j) for (i64 i = 0; i < g_platform.frame_width; ++i) if (i < x || i >= x + w || j < y || j >= y + h) - g_platform.pixels[j * g_platform.frame_width + i] = (vec3_f32) { .8f, .8f, .8f }; + g_platform.pixels[j * g_platform.frame_width + i] = (vec4_f32) { .8f, .8f, .8f, 1.f }; else - g_platform.pixels[j * g_platform.frame_width + i] = (vec3_f32) { .27f, .21f, .24f }; + g_platform.pixels[j * g_platform.frame_width + i] = (vec4_f32) { .27f, .21f, .24f, 1.f }; p_render_frame(); p_sleep_for(0); @@ -888,7 +928,7 @@ i64 p_send(u16 slot, IP_Address address, i64 size, u8 *data, u16 *local_port) { static b8 _sound_ready = 0; static i64 _sound_position = 0; -static f32 _sound_ring[MAX_NUM_AUDIO_FRAMES] = {0}; +static f32 _sound_ring[MAX_NUM_SOUND_FRAMES] = {0}; static snd_pcm_t *_sound_out = NULL; static void sound_init(void) { @@ -922,11 +962,11 @@ static void sound_init(void) { if (s < 0) fprintf(stderr, "%s:%d, %s: snd_pcm_hw_params_set_format failed: %s\n", __FILE__, __LINE__, __func__, snd_strerror(s)); - s = snd_pcm_hw_params_set_rate(_sound_out, hw_params, AUDIO_SAMPLE_RATE, 0); + s = snd_pcm_hw_params_set_rate(_sound_out, hw_params, SOUND_SAMPLE_RATE, 0); if (s < 0) fprintf(stderr, "%s:%d, %s: snd_pcm_hw_params_set_rate failed: %s\n", __FILE__, __LINE__, __func__, snd_strerror(s)); - s = snd_pcm_hw_params_set_channels(_sound_out, hw_params, AUDIO_NUM_CHANNELS); + s = snd_pcm_hw_params_set_channels(_sound_out, hw_params, NUM_SOUND_CHANNELS); if (s < 0) fprintf(stderr, "%s:%d, %s: snd_pcm_hw_params_set_channels failed: %s\n", __FILE__, __LINE__, __func__, snd_strerror(s)); @@ -940,7 +980,7 @@ static void sound_init(void) { if (s < 0) fprintf(stderr, "%s:%d, %s: snd_pcm_sw_params_current failed: %s\n", __FILE__, __LINE__, __func__, snd_strerror(s)); - s = snd_pcm_sw_params_set_avail_min(_sound_out, sw_params, AUDIO_AVAIL_MIN); + s = snd_pcm_sw_params_set_avail_min(_sound_out, sw_params, SOUND_AVAIL_MIN); if (s < 0) fprintf(stderr, "%s:%d, %s: snd_pcm_sw_params_set_avail_min failed: %s\n", __FILE__, __LINE__, __func__, snd_strerror(s)); @@ -979,30 +1019,30 @@ static void sound_cleanup(void) { void p_handle_audio(i64 samples_elapsed) { sound_init(); - i64 num_frames = samples_elapsed * AUDIO_NUM_CHANNELS; + i64 num_frames = samples_elapsed * NUM_SOUND_CHANNELS; - if (num_frames > MAX_NUM_AUDIO_FRAMES) { + if (num_frames > MAX_NUM_SOUND_FRAMES) { fprintf(stderr, "%s:%d, %s: Sound buffer overflow.\n", __FILE__, __LINE__, __func__); - num_frames = MAX_NUM_AUDIO_FRAMES; + num_frames = MAX_NUM_SOUND_FRAMES; } i32 s; - if (num_frames <= MAX_NUM_AUDIO_FRAMES - _sound_position) { - s = snd_pcm_writei(_sound_out, _sound_ring + _sound_position, num_frames / AUDIO_NUM_CHANNELS); + if (num_frames <= MAX_NUM_SOUND_FRAMES - _sound_position) { + s = snd_pcm_writei(_sound_out, _sound_ring + _sound_position, num_frames / NUM_SOUND_CHANNELS); if (s < 0) fprintf(stderr, "%s:%d, %s: snd_pcm_writei failed: %s\n", __FILE__, __LINE__, __func__, snd_strerror(s)); memset(_sound_ring + _sound_position, 0, num_frames * sizeof *_sound_ring); } else { - i64 part_one = MAX_NUM_AUDIO_FRAMES - _sound_position; + i64 part_one = MAX_NUM_SOUND_FRAMES - _sound_position; i64 part_two = num_frames - part_one; - s = snd_pcm_writei(_sound_out, _sound_ring + _sound_position, part_one / AUDIO_NUM_CHANNELS); + s = snd_pcm_writei(_sound_out, _sound_ring + _sound_position, part_one / NUM_SOUND_CHANNELS); if (s < 0) fprintf(stderr, "%s:%d, %s: snd_pcm_writei failed: %s\n", __FILE__, __LINE__, __func__, snd_strerror(s)); - s = snd_pcm_writei(_sound_out, _sound_ring, part_two / AUDIO_NUM_CHANNELS); + s = snd_pcm_writei(_sound_out, _sound_ring, part_two / NUM_SOUND_CHANNELS); if (s < 0) fprintf(stderr, "%s:%d, %s: snd_pcm_writei failed: %s\n", __FILE__, __LINE__, __func__, snd_strerror(s)); @@ -1010,7 +1050,7 @@ void p_handle_audio(i64 samples_elapsed) { memset(_sound_ring, 0, part_two * sizeof *_sound_ring); } - _sound_position = (_sound_position + num_frames) % MAX_NUM_AUDIO_FRAMES; + _sound_position = (_sound_position + num_frames) % MAX_NUM_SOUND_FRAMES; } void p_queue_sound(i64 delay_in_samples, i64 num_samples, f32 *frames) { @@ -1020,7 +1060,7 @@ void p_queue_sound(i64 delay_in_samples, i64 num_samples, f32 *frames) { return; if (delay_in_samples < 0) { - frames += -delay_in_samples * AUDIO_NUM_CHANNELS; + frames += -delay_in_samples * NUM_SOUND_CHANNELS; num_samples -= delay_in_samples; delay_in_samples = 0; } @@ -1028,22 +1068,22 @@ void p_queue_sound(i64 delay_in_samples, i64 num_samples, f32 *frames) { if (num_samples <= 0) return; - i64 num_frames = num_samples * AUDIO_NUM_CHANNELS; + i64 num_frames = num_samples * NUM_SOUND_CHANNELS; - if (num_frames > MAX_NUM_AUDIO_FRAMES) { + if (num_frames > MAX_NUM_SOUND_FRAMES) { fprintf(stderr, "%s:%d, %s: Sound buffer overflow.\n", __FILE__, __LINE__, __func__); return; } sound_init(); - i64 begin = (_sound_position + delay_in_samples) % MAX_NUM_AUDIO_FRAMES; + i64 begin = (_sound_position + delay_in_samples) % MAX_NUM_SOUND_FRAMES; - if (num_frames <= MAX_NUM_AUDIO_FRAMES - begin) + if (num_frames <= MAX_NUM_SOUND_FRAMES - begin) for (i64 i = 0; i < num_frames; ++i) _sound_ring[begin + i] += frames[i]; else { - i64 part_one = MAX_NUM_AUDIO_FRAMES - begin; + i64 part_one = MAX_NUM_SOUND_FRAMES - begin; i64 part_two = num_frames - part_one; for (i64 i = 0; i < part_one; ++i) @@ -1070,7 +1110,7 @@ void p_queue_sound(i64 delay_in_samples, i64 num_samples, f32 *frames) { static i16 _key_table[MAX_NUM_KEYS] = {0}; static b8 _key_repeat[MAX_NUM_KEYS] = {0}; static i64 _frame_time = 0; -static vec3_f32 _pixels[MAX_NUM_PIXELS] = {0}; +static vec4_f32 _pixels[MAX_NUM_PIXELS] = {0}; static i32 _pixel_size = 0; static u32 _pixels_scaled[MAX_NUM_PIXELS] = {0}; static u32 _pixels_internal[MAX_NUM_PIXELS] = {0}; @@ -1568,13 +1608,20 @@ i32 p_handle_events(void) { i32 width = _image.width; i32 height = _image.height; - if (_pixel_size <= 1) - _pixel_size = 1; - else { + if (_pixel_size <= MIN_PIXEL_SIZE) + _pixel_size = MIN_PIXEL_SIZE; + + if (_pixel_size > 1) { width = (i32) floor(((f64) _image.width) / _pixel_size + .5); height = (i32) floor(((f64) _image.height) / _pixel_size + .5); } + if (g_platform.real_width != _image.width || g_platform.real_height != _image.height) { + ++num_events; + g_platform.real_width = _image.width; + g_platform.real_height = _image.height; + } + if (g_platform.frame_width != width || g_platform.frame_height != height) { ++num_events; g_platform.frame_width = width; @@ -1605,11 +1652,11 @@ void p_render_frame(void) { if (g_platform.frame_width == _image.width && g_platform.frame_height == _image.height) { i64 size = g_platform.frame_width * g_platform.frame_height; for (i64 i = 0; i < size; ++i) - _pixels_internal[i] = rgb_u32_from_f32(_pixels[i]); + _pixels_internal[i] = rgb_u32_from_f32((vec3_f32) { _pixels[i].x, _pixels[i].y, _pixels[i].z }); } else { i64 size = g_platform.frame_width * g_platform.frame_height; for (i64 i = 0; i < size; ++i) - _pixels_scaled[i] = rgb_u32_from_f32(_pixels[i]); + _pixels_scaled[i] = rgb_u32_from_f32((vec3_f32) { _pixels[i].x, _pixels[i].y, _pixels[i].z }); for (i64 j = 0; j < _image.height; ++j) { i64 j0 = (j * g_platform.frame_height) / _image.height; @@ -1628,7 +1675,7 @@ void p_render_frame(void) { if (_pixel_size < MAX_PIXEL_SIZE && frame_duration > MAX_FRAME_DURATION) ++_pixel_size; - else if (_pixel_size > 1 && frame_duration < MIN_FRAME_DURATION) + else if (_pixel_size > MIN_PIXEL_SIZE && frame_duration < MIN_FRAME_DURATION) --_pixel_size; } } @@ -1670,8 +1717,8 @@ static b8 _key_pressed[MAX_NUM_KEYS] = {0}; static b8 _wait_events = 0; i64 _sound_position = 0; i64 _sound_read = 0; -f32 _sound_ring[MAX_NUM_AUDIO_FRAMES] = {0}; -f32 _sound_buffer[MAX_NUM_AUDIO_FRAMES] = {0}; +f32 _sound_ring[MAX_NUM_SOUND_FRAMES] = {0}; +f32 _sound_buffer[MAX_NUM_SOUND_FRAMES] = {0}; i32 p_time_impl(void); @@ -1689,7 +1736,7 @@ void p_init(void) { g_platform.input = _input; g_platform.clipboard = _clipboard_buffer; g_platform.done = 1; - _sound_position = AUDIO_AVAIL_MIN % MAX_NUM_AUDIO_FRAMES; + _sound_position = SOUND_AVAIL_MIN % MAX_NUM_SOUND_FRAMES; _sound_read = 0; } @@ -1717,11 +1764,11 @@ void p_render_frame(void) { if (_frame_width == g_platform.frame_width && _frame_height == g_platform.frame_height) { i64 size = g_platform.frame_width * g_platform.frame_height; for (i64 i = 0; i < size; ++i) - _pixels_internal[i] = 0xff000000u | rgb_u32_from_f32(_pixels[i]); + _pixels_internal[i] = 0xff000000u | rgb_u32_from_f32((vec3_f32) { _pixels[i].x, _pixels[i].y, _pixels[i].z }); } else { i64 size = g_platform.frame_width * g_platform.frame_height; for (i64 i = 0; i < size; ++i) - _pixels_scaled[i] = rgb_u32_from_f32(_pixels[i]); + _pixels_scaled[i] = rgb_u32_from_f32((vec3_f32) { _pixels[i].x, _pixels[i].y, _pixels[i].z }); for (i64 j = 0; j < _frame_height; ++j) { i64 j0 = (j * g_platform.frame_height) / _frame_height; @@ -1752,7 +1799,7 @@ void p_handle_audio(i64 samples_elapsed) { if (samples_elapsed <= 0) return; - _sound_position = (_sound_position + samples_elapsed * AUDIO_NUM_CHANNELS) % MAX_NUM_AUDIO_FRAMES; + _sound_position = (_sound_position + samples_elapsed * NUM_SOUND_CHANNELS) % MAX_NUM_SOUND_FRAMES; } void p_queue_sound(i64 delay_in_samples, i64 num_samples, f32 *frames) { @@ -1760,7 +1807,7 @@ void p_queue_sound(i64 delay_in_samples, i64 num_samples, f32 *frames) { return; if (delay_in_samples < 0) { - frames += -delay_in_samples * AUDIO_NUM_CHANNELS; + frames += -delay_in_samples * NUM_SOUND_CHANNELS; num_samples -= delay_in_samples; delay_in_samples = 0; } @@ -1768,17 +1815,17 @@ void p_queue_sound(i64 delay_in_samples, i64 num_samples, f32 *frames) { if (num_samples <= 0) return; - i64 num_frames = num_samples * AUDIO_NUM_CHANNELS; - if (num_frames > MAX_NUM_AUDIO_FRAMES) + i64 num_frames = num_samples * NUM_SOUND_CHANNELS; + if (num_frames > MAX_NUM_SOUND_FRAMES) return; - i64 begin = (_sound_position + delay_in_samples * AUDIO_NUM_CHANNELS) % MAX_NUM_AUDIO_FRAMES; + i64 begin = (_sound_position + delay_in_samples * NUM_SOUND_CHANNELS) % MAX_NUM_SOUND_FRAMES; - if (num_frames <= MAX_NUM_AUDIO_FRAMES - begin) + if (num_frames <= MAX_NUM_SOUND_FRAMES - begin) for (i64 i = 0; i < num_frames; ++i) _sound_ring[begin + i] += frames[i]; else { - i64 part_one = MAX_NUM_AUDIO_FRAMES - begin; + i64 part_one = MAX_NUM_SOUND_FRAMES - begin; i64 part_two = num_frames - part_one; for (i64 i = 0; i < part_one; ++i) @@ -1810,13 +1857,20 @@ __attribute__((export_name("js_frame"))) void js_frame(i32 frame_width, i32 fram i32 width = _frame_width; i32 height = _frame_height; - if (_pixel_size <= 1) - _pixel_size = 1; - else { + if (_pixel_size <= MIN_PIXEL_SIZE) + _pixel_size = MIN_PIXEL_SIZE; + + if (_pixel_size > 1) { width = (i32) floor(((f64) _frame_width) / _pixel_size + .5); height = (i32) floor(((f64) _frame_height) / _pixel_size + .5); } + if (g_platform.real_width != _frame_width || g_platform.real_height != _frame_height) { + ++num_events; + g_platform.real_width = frame_width; + g_platform.real_height = frame_height; + } + if (g_platform.frame_width != width || g_platform.frame_height != height) { ++_num_events; g_platform.frame_width = width; @@ -1834,21 +1888,21 @@ __attribute__((export_name("js_frame"))) void js_frame(i32 frame_width, i32 fram } // Convert interleaved frames to non-interleaved. - for (i64 j = 0; j < AUDIO_NUM_CHANNELS; ++j) + for (i64 j = 0; j < NUM_SOUND_CHANNELS; ++j) for (i64 i = 0; i < num_samples; ++i) { - i64 n = (_sound_read + i * AUDIO_NUM_CHANNELS + j) % MAX_NUM_AUDIO_FRAMES; + i64 n = (_sound_read + i * NUM_SOUND_CHANNELS + j) % MAX_NUM_SOUND_FRAMES; _sound_buffer[j * num_samples + i] = _sound_ring[n]; _sound_ring [n] = 0.f; } - _sound_read = (_sound_read + num_samples * AUDIO_NUM_CHANNELS) % MAX_NUM_AUDIO_FRAMES; + _sound_read = (_sound_read + num_samples * NUM_SOUND_CHANNELS) % MAX_NUM_SOUND_FRAMES; if (!g_platform.exact_resolution && do_render) { i64 frame_duration = p_time() - frame_time; if (_pixel_size < MAX_PIXEL_SIZE && frame_duration > MAX_FRAME_DURATION) ++_pixel_size; - else if (_pixel_size > 1 && frame_duration < MIN_FRAME_DURATION) + else if (_pixel_size > MIN_PIXEL_SIZE && frame_duration < MIN_FRAME_DURATION) --_pixel_size; } } @@ -1907,15 +1961,15 @@ __attribute__((export_name("js_keyup"))) void js_keyup(u32 key, u32 mod) { } __attribute__((export_name("js_sample_rate"))) f64 js_sample_rate(void) { - return (f64) AUDIO_SAMPLE_RATE; + return (f64) SOUND_SAMPLE_RATE; } __attribute__((export_name("js_num_channels"))) i32 js_num_channels(void) { - return AUDIO_NUM_CHANNELS; + return NUM_SOUND_CHANNELS; } __attribute__((export_name("js_max_num_audio_frames"))) i32 js_max_num_audio_frames(void) { - return MAX_NUM_AUDIO_FRAMES; + return MAX_NUM_SOUND_FRAMES; } __attribute__((export_name("js_sound_buffer"))) void *js_sound_buffer(void) { |