summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMitya Selivanov <automainint@guattari.tech>2025-01-14 04:13:45 +0100
committerMitya Selivanov <automainint@guattari.tech>2025-01-14 04:13:45 +0100
commitc9208089c6074575342d529f494295c13269a1aa (patch)
treee87af3b94f573ab48989a265561dc6016d69812b
parenta3cf8790cb4547288c9d395609738c4a1eb838a2 (diff)
downloadreduced_system_layer-c9208089c6074575342d529f494295c13269a1aa.zip
Rectangle anti-aliasing
-rw-r--r--Dockerfile2
-rwxr-xr-xbuild_all.sh19
-rw-r--r--examples/pixels.c44
-rw-r--r--graphics.c518
-rw-r--r--index.htm1
-rwxr-xr-xreduced_system_layer.c186
6 files changed, 607 insertions, 163 deletions
diff --git a/Dockerfile b/Dockerfile
index 407cb16..67e7fe8 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -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;
+}
diff --git a/graphics.c b/graphics.c
index a853e39..6a61ac3 100644
--- a/graphics.c
+++ b/graphics.c
@@ -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_
diff --git a/index.htm b/index.htm
index ed5ff78..e06e63b 100644
--- a/index.htm
+++ b/index.htm
@@ -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) {