summaryrefslogtreecommitdiff
path: root/graphics.c
diff options
context:
space:
mode:
Diffstat (limited to 'graphics.c')
-rw-r--r--graphics.c451
1 files changed, 409 insertions, 42 deletions
diff --git a/graphics.c b/graphics.c
index 5632712..a63f622 100644
--- a/graphics.c
+++ b/graphics.c
@@ -164,9 +164,9 @@ typedef struct {
//
// ================================================================
-Box font_text_area_dispatch(i32 font, vec2 scale, i64 num_chars, c32 *text);
+Box dispatch_font_text_area(i32 font, vec2 scale, i64 num_chars, c32 *text);
-void font_render_to_stencil_dispatch(Stencil_Buffer dst, i32 font, vec2 position, vec2 scale, i64 num_chars, c32 *text);
+void dispatch_font_render_to_stencil(Stencil_Buffer dst, i32 font, vec2 position, vec2 scale, i64 num_chars, c32 *text);
// ================================================================
@@ -431,8 +431,8 @@ vec3_f32 lch_mix(vec3_f32 a, vec3_f32 b, f32 t) {
vec3_f32 lch_lerp(vec3_f32 a, vec3_f32 b, f32 t) {
f32 delta_hue = b.z - a.z;
- if (delta_hue > M_PI) delta_hue -= M_PI * 2;
- if (delta_hue < -M_PI) delta_hue += M_PI * 2;
+ if (delta_hue > M_PI) delta_hue -= M_PI * 2.0;
+ if (delta_hue < -M_PI) delta_hue += M_PI * 2.0;
return (vec3_f32) {
.x = a.x + (b.x - a.x) * t,
@@ -442,9 +442,9 @@ vec3_f32 lch_lerp(vec3_f32 a, vec3_f32 b, f32 t) {
}
u32 rgb_u32_from_f32(vec3_f32 color) {
- i32 ir = (i32) floor(color.x * 255. + .5);
- i32 ig = (i32) floor(color.y * 255. + .5);
- i32 ib = (i32) floor(color.z * 255. + .5);
+ i32 ir = (i32) floor(color.x * 255.0 + 0.5);
+ i32 ig = (i32) floor(color.y * 255.0 + 0.5);
+ i32 ib = (i32) floor(color.z * 255.0 + 0.5);
u32 r = ir < 0 ? 0u : ir > 255 ? 255u : (u32) ir;
u32 g = ig < 0 ? 0u : ig > 255 ? 255u : (u32) ig;
@@ -455,17 +455,17 @@ u32 rgb_u32_from_f32(vec3_f32 color) {
vec3_f32 rgb_f32_from_u32(u32 color) {
return (vec3_f32) {
- .x = ((color & 0xff0000) >> 16) / 255.f,
- .y = ((color & 0x00ff00) >> 8) / 255.f,
- .z = (color & 0x0000ff) / 255.f,
+ .x = ((color & 0xff0000) >> 16) / 255.0f,
+ .y = ((color & 0x00ff00) >> 8) / 255.0f,
+ .z = (color & 0x0000ff) / 255.0f,
};
}
u32 rgba_u32_from_f32(vec4_f32 color) {
- i32 ir = (i32) floor(color.x * 255. + .5);
- i32 ig = (i32) floor(color.y * 255. + .5);
- i32 ib = (i32) floor(color.z * 255. + .5);
- i32 ia = (i32) floor(color.w * 255. + .5);
+ i32 ir = (i32) floor(color.x * 255.0 + 0.5);
+ i32 ig = (i32) floor(color.y * 255.0 + 0.5);
+ i32 ib = (i32) floor(color.z * 255.0 + 0.5);
+ i32 ia = (i32) floor(color.w * 255.0 + 0.5);
u32 r = ir < 0 ? 0u : ir > 255 ? 255u : (u32) ir;
u32 g = ig < 0 ? 0u : ig > 255 ? 255u : (u32) ig;
@@ -477,10 +477,10 @@ u32 rgba_u32_from_f32(vec4_f32 color) {
vec4_f32 rgba_f32_from_u32(u32 color) {
return (vec4_f32) {
- .x = ((color & 0x00ff0000) >> 16) / 255.f,
- .y = ((color & 0x0000ff00) >> 8) / 255.f,
- .z = (color & 0x000000ff) / 255.f,
- .w = ((color & 0xff000000) >> 24) / 255.f,
+ .x = ((color & 0x00ff0000) >> 16) / 255.0f,
+ .y = ((color & 0x0000ff00) >> 8) / 255.0f,
+ .z = (color & 0x000000ff) / 255.0f,
+ .w = ((color & 0xff000000) >> 24) / 255.0f,
};
}
@@ -621,30 +621,31 @@ void draw_pixels_to_buffer(Pixel_Buffer dst, Box area, Pixel_Buffer src) {
PROFILER_begin(PROFILE_DRAW_PIXELS);
- i64 i0 = (i64) floor(area.x + 0.5);
- i64 i1 = (i64) floor(area.x + area.width + 0.5);
- i64 j0 = (i64) floor(area.y + 0.5);
- i64 j1 = (i64) floor(area.y + area.height + 0.5);
+ i64 x0 = (i64) floor(area.x + 0.5);
+ i64 x1 = (i64) floor(area.x + area.width + 0.5);
+ i64 y0 = (i64) floor(area.y + 0.5);
+ i64 y1 = (i64) floor(area.y + area.height + 0.5);
- if (i0 < 0) i0 = 0;
- if (i1 > dst.width) i1 = dst.width;
- if (j0 < 0) j0 = 0;
- if (j1 > dst.height) j1 = dst.height;
+ i64 i0 = max2_i64_(x0, 0);
+ i64 i1 = min2_i64_(x1, dst.width);
+ i64 j0 = max2_i64_(y0, 0);
+ i64 j1 = min2_i64_(y1, dst.height);
+
+ u32 x_ratio = (src.width << 16) / (x1 - x0);
+ u32 y_ratio = (src.height << 16) / (y1 - y0);
- // FIXME, PERF: Use integer arithmetic.
+ u32 x_half = x_ratio / 2;
+ u32 y_half = y_ratio / 2;
- f64 di = src.width / area.width;
- f64 dj = src.height / area.height;
- f64 jj = (j0 - area.y) * dj + dj * .5;
- for (i64 j = j0; j < j1; ++j, jj += dj) {
- if (jj < 0 || jj >= src.height) continue;
+ for (i64 j = j0; j < j1; ++j) {
+ u32 src_j = ((j - y0) * y_ratio + y_half) >> 16;
vec4_f32 *d = dst.pixels + j * dst.stride + i0;
- vec4_f32 *d_end = d + i1 - i0;
- vec4_f32 *s = src.pixels + (i64) jj * src.stride;
- f64 ii = (i0 - area.x) * di + di * .5;
- if (ii < 0 || ii >= src.width) continue;
- for (; d < d_end; ++d, ii += di)
- put_pixel_(d, s[(i64) ii]);
+ vec4_f32 *d_end = d + (i1 - i0);
+ vec4_f32 *s = src.pixels + src_j * src.stride;
+ for (u32 i = i0 - x0; d < d_end; ++d, ++i) {
+ u32 src_i = (i * x_ratio + x_half) >> 16;
+ put_pixel_(d, s[src_i]);
+ }
}
PROFILER_end(PROFILE_DRAW_PIXELS);
@@ -1223,8 +1224,8 @@ static void lcd_draw_text_(Pixel_Buffer dst, vec4_f32 color, f64 x0, f64 y0, f64
}
}
-#if !defined(GRAPHICS_ENABLE_FONT_CUSTOM_DISPATCH)
-Box font_text_area_dispatch(i32 font, vec2 scale, i64 num_chars, c32 *text) {
+#if !defined(ENABLE_FONT_CUSTOM_DISPATCH)
+Box dispatch_font_text_area(i32 font, vec2 scale, i64 num_chars, c32 *text) {
// TODO
(void) font;
@@ -1235,7 +1236,7 @@ Box font_text_area_dispatch(i32 font, vec2 scale, i64 num_chars, c32 *text) {
return (Box) {0};
}
-void font_render_to_stencil_dispatch(Stencil_Buffer dst, i32 font, vec2 position, vec2 scale, i64 num_chars, c32 *text) {
+void dispatch_font_render_to_stencil(Stencil_Buffer dst, i32 font, vec2 position, vec2 scale, i64 num_chars, c32 *text) {
// TODO
(void) dst;
@@ -1245,7 +1246,7 @@ void font_render_to_stencil_dispatch(Stencil_Buffer dst, i32 font, vec2 position
(void) num_chars;
(void) text;
}
-#endif // !defined(GRAPHICS_ENABLE_FONT_CUSTOM_DISPATCH)
+#endif // !defined(ENABLE_FONT_CUSTOM_DISPATCH)
void draw_text_area_to_buffer(Pixel_Buffer dst, i32 font, vec4_f32 color, Box area, vec2 max_size, i64 num_chars, c32 *text) {
(void) font;
@@ -3058,6 +3059,372 @@ TEST("text selection backward") {
REQUIRE(ok);
}
+TEST("scale down exact") {
+ vec4_f32 src[16] = {
+ { 1.0, 0.0, 0.0, 1.0 },
+ { 1.0, 0.0, 0.0, 1.0 },
+ { 0.0, 1.0, 0.0, 1.0 },
+ { 0.0, 1.0, 0.0, 1.0 },
+
+ { 1.0, 0.0, 0.0, 1.0 },
+ { 1.0, 0.0, 0.0, 1.0 },
+ { 0.0, 1.0, 0.0, 1.0 },
+ { 0.0, 1.0, 0.0, 1.0 },
+
+ { 0.0, 0.0, 1.0, 1.0 },
+ { 0.0, 0.0, 1.0, 1.0 },
+ { 1.0, 1.0, 0.0, 1.0 },
+ { 1.0, 1.0, 0.0, 1.0 },
+
+ { 0.0, 0.0, 1.0, 1.0 },
+ { 0.0, 0.0, 1.0, 1.0 },
+ { 1.0, 1.0, 0.0, 1.0 },
+ { 1.0, 1.0, 0.0, 1.0 },
+ };
+
+ vec4_f32 dst[4];
+
+ vec4_f32 expect[4] = {
+ { 1.0, 0.0, 0.0, 1.0 },
+ { 0.0, 1.0, 0.0, 1.0 },
+
+ { 0.0, 0.0, 1.0, 1.0 },
+ { 1.0, 1.0, 0.0, 1.0 },
+ };
+
+ draw_pixels_to_buffer(
+ (Pixel_Buffer) { .width = 2, .height = 2, .stride = 2, .pixels = dst, },
+ (Box) { .x = 0.0, .y = 0.0, .width = 2.0, .height = 2.0, },
+ (Pixel_Buffer) { .width = 4, .height = 4, .stride = 4, .pixels = src, }
+ );
+
+ // for (i64 i = 0; i < (i64) (sizeof dst / sizeof *dst); ++i) {
+ // printf("%g, %g, %g, %g \n", dst[i].x, dst[i].y, dst[i].z, dst[i].w);
+ // }
+
+ b8 ok = 1;
+
+ for (i64 i = 0; i < 4; ++i)
+ ok = ok && dst[i].x == expect[i].x
+ && dst[i].y == expect[i].y
+ && dst[i].z == expect[i].z
+ && dst[i].w == expect[i].w;
+
+ REQUIRE(ok);
+}
+
+TEST("scale down aliasing overflow") {
+ vec4_f32 src[16] = {
+ { 1.0, 0.0, 0.0, 1.0 },
+ { 1.0, 0.0, 0.0, 1.0 },
+ { 0.0, 1.0, 0.0, 1.0 },
+ { 0.0, 1.0, 0.0, 1.0 },
+
+ { 1.0, 0.0, 0.0, 1.0 },
+ { 1.0, 0.0, 0.0, 1.0 },
+ { 0.0, 1.0, 0.0, 1.0 },
+ { 0.0, 1.0, 0.0, 1.0 },
+
+ { 0.0, 0.0, 1.0, 1.0 },
+ { 0.0, 0.0, 1.0, 1.0 },
+ { 1.0, 1.0, 0.0, 1.0 },
+ { 1.0, 1.0, 0.0, 1.0 },
+
+ { 0.0, 0.0, 1.0, 1.0 },
+ { 0.0, 0.0, 1.0, 1.0 },
+ { 1.0, 1.0, 0.0, 1.0 },
+ { 1.0, 1.0, 0.0, 1.0 },
+ };
+
+ vec4_f32 dst[4];
+
+ vec4_f32 expect[4] = {
+ { 1.0, 0.0, 0.0, 1.0 },
+ { 0.0, 1.0, 0.0, 1.0 },
+
+ { 0.0, 0.0, 1.0, 1.0 },
+ { 1.0, 1.0, 0.0, 1.0 },
+ };
+
+ draw_pixels_to_buffer(
+ (Pixel_Buffer) { .width = 2, .height = 2, .stride = 2, .pixels = dst, },
+ (Box) { .x = 0.4, .y = 0.4, .width = 2.0, .height = 2.0, },
+ (Pixel_Buffer) { .width = 4, .height = 4, .stride = 4, .pixels = src, }
+ );
+
+ // for (i64 i = 0; i < (i64) (sizeof dst / sizeof *dst); ++i) {
+ // printf("%g, %g, %g, %g \n", dst[i].x, dst[i].y, dst[i].z, dst[i].w);
+ // }
+
+ b8 ok = 1;
+
+ for (i64 i = 0; i < 4; ++i)
+ ok = ok && dst[i].x == expect[i].x
+ && dst[i].y == expect[i].y
+ && dst[i].z == expect[i].z
+ && dst[i].w == expect[i].w;
+
+ REQUIRE(ok);
+}
+
+TEST("scale down aliasing underflow") {
+ vec4_f32 src[16] = {
+ { 1.0, 0.0, 0.0, 1.0 },
+ { 1.0, 0.0, 0.0, 1.0 },
+ { 0.0, 1.0, 0.0, 1.0 },
+ { 0.0, 1.0, 0.0, 1.0 },
+
+ { 1.0, 0.0, 0.0, 1.0 },
+ { 1.0, 0.0, 0.0, 1.0 },
+ { 0.0, 1.0, 0.0, 1.0 },
+ { 0.0, 1.0, 0.0, 1.0 },
+
+ { 0.0, 0.0, 1.0, 1.0 },
+ { 0.0, 0.0, 1.0, 1.0 },
+ { 1.0, 1.0, 0.0, 1.0 },
+ { 1.0, 1.0, 0.0, 1.0 },
+
+ { 0.0, 0.0, 1.0, 1.0 },
+ { 0.0, 0.0, 1.0, 1.0 },
+ { 1.0, 1.0, 0.0, 1.0 },
+ { 1.0, 1.0, 0.0, 1.0 },
+ };
+
+ vec4_f32 dst[4];
+
+ vec4_f32 expect[4] = {
+ { 1.0, 0.0, 0.0, 1.0 },
+ { 0.0, 1.0, 0.0, 1.0 },
+
+ { 0.0, 0.0, 1.0, 1.0 },
+ { 1.0, 1.0, 0.0, 1.0 },
+ };
+
+ draw_pixels_to_buffer(
+ (Pixel_Buffer) { .width = 2, .height = 2, .stride = 2, .pixels = dst, },
+ (Box) { .x = -0.4, .y = -0.4, .width = 2.0, .height = 2.0, },
+ (Pixel_Buffer) { .width = 4, .height = 4, .stride = 4, .pixels = src, }
+ );
+
+ // for (i64 i = 0; i < (i64) (sizeof dst / sizeof *dst); ++i) {
+ // printf("%g, %g, %g, %g \n", dst[i].x, dst[i].y, dst[i].z, dst[i].w);
+ // }
+
+ b8 ok = 1;
+
+ for (i64 i = 0; i < 4; ++i)
+ ok = ok && dst[i].x == expect[i].x
+ && dst[i].y == expect[i].y
+ && dst[i].z == expect[i].z
+ && dst[i].w == expect[i].w;
+
+ REQUIRE(ok);
+}
+
+TEST("scale down aliasing middle") {
+ vec4_f32 src[9] = {
+ { 1.0, 0.0, 0.0, 1.0 },
+ { 0.0, 1.0, 0.0, 1.0 },
+ { 0.0, 0.0, 1.0, 1.0 },
+
+ { 1.0, 1.0, 0.0, 1.0 },
+ { 0.0, 1.0, 1.0, 1.0 },
+ { 1.0, 0.0, 1.0, 1.0 },
+
+ { 1.0, 0.0, 0.5, 1.0 },
+ { 0.5, 1.0, 0.0, 1.0 },
+ { 0.0, 0.5, 1.0, 1.0 },
+ };
+
+ vec4_f32 dst[1];
+
+ vec4_f32 expect[1] = {
+ { 0.0, 1.0, 1.0, 1.0 },
+ };
+
+ draw_pixels_to_buffer(
+ (Pixel_Buffer) { .width = 1, .height = 1, .stride = 1, .pixels = dst, },
+ (Box) { .x = 0.0, .y = 0.0, .width = 1.0, .height = 1.0, },
+ (Pixel_Buffer) { .width = 3, .height = 3, .stride = 3, .pixels = src, }
+ );
+
+ // for (i64 i = 0; i < (i64) (sizeof dst / sizeof *dst); ++i) {
+ // printf("%g, %g, %g, %g \n", dst[i].x, dst[i].y, dst[i].z, dst[i].w);
+ // }
+
+ b8 ok = 1;
+
+ for (i64 i = 0; i < 1; ++i)
+ ok = ok && dst[i].x == expect[i].x
+ && dst[i].y == expect[i].y
+ && dst[i].z == expect[i].z
+ && dst[i].w == expect[i].w;
+
+ REQUIRE(ok);
+}
+
+TEST("scale up exact") {
+ vec4_f32 src[4] = {
+ { 1.0, 0.0, 0.0, 1.0 },
+ { 0.0, 1.0, 0.0, 1.0 },
+
+ { 0.0, 0.0, 1.0, 1.0 },
+ { 1.0, 1.0, 0.0, 1.0 },
+ };
+
+ vec4_f32 dst[16];
+
+ vec4_f32 expect[16] = {
+ { 1.0, 0.0, 0.0, 1.0 },
+ { 1.0, 0.0, 0.0, 1.0 },
+ { 0.0, 1.0, 0.0, 1.0 },
+ { 0.0, 1.0, 0.0, 1.0 },
+
+ { 1.0, 0.0, 0.0, 1.0 },
+ { 1.0, 0.0, 0.0, 1.0 },
+ { 0.0, 1.0, 0.0, 1.0 },
+ { 0.0, 1.0, 0.0, 1.0 },
+
+ { 0.0, 0.0, 1.0, 1.0 },
+ { 0.0, 0.0, 1.0, 1.0 },
+ { 1.0, 1.0, 0.0, 1.0 },
+ { 1.0, 1.0, 0.0, 1.0 },
+
+ { 0.0, 0.0, 1.0, 1.0 },
+ { 0.0, 0.0, 1.0, 1.0 },
+ { 1.0, 1.0, 0.0, 1.0 },
+ { 1.0, 1.0, 0.0, 1.0 },
+ };
+
+ draw_pixels_to_buffer(
+ (Pixel_Buffer) { .width = 4, .height = 4, .stride = 4, .pixels = dst, },
+ (Box) { .x = 0.0, .y = 0.0, .width = 4.0, .height = 4.0, },
+ (Pixel_Buffer) { .width = 2, .height = 2, .stride = 2, .pixels = src, }
+ );
+
+ // for (i64 i = 0; i < (i64) (sizeof dst / sizeof *dst); ++i) {
+ // printf("%g, %g, %g, %g \n", dst[i].x, dst[i].y, dst[i].z, dst[i].w);
+ // }
+
+ b8 ok = 1;
+
+ for (i64 i = 0; i < 16; ++i)
+ ok = ok && dst[i].x == expect[i].x
+ && dst[i].y == expect[i].y
+ && dst[i].z == expect[i].z
+ && dst[i].w == expect[i].w;
+
+ REQUIRE(ok);
+}
+
+TEST("scale up aliasing overflow") {
+ vec4_f32 src[4] = {
+ { 1.0, 0.0, 0.0, 1.0 },
+ { 0.0, 1.0, 0.0, 1.0 },
+
+ { 0.0, 0.0, 1.0, 1.0 },
+ { 1.0, 1.0, 0.0, 1.0 },
+ };
+
+ vec4_f32 dst[16];
+
+ vec4_f32 expect[16] = {
+ { 1.0, 0.0, 0.0, 1.0 },
+ { 1.0, 0.0, 0.0, 1.0 },
+ { 0.0, 1.0, 0.0, 1.0 },
+ { 0.0, 1.0, 0.0, 1.0 },
+
+ { 1.0, 0.0, 0.0, 1.0 },
+ { 1.0, 0.0, 0.0, 1.0 },
+ { 0.0, 1.0, 0.0, 1.0 },
+ { 0.0, 1.0, 0.0, 1.0 },
+
+ { 0.0, 0.0, 1.0, 1.0 },
+ { 0.0, 0.0, 1.0, 1.0 },
+ { 1.0, 1.0, 0.0, 1.0 },
+ { 1.0, 1.0, 0.0, 1.0 },
+
+ { 0.0, 0.0, 1.0, 1.0 },
+ { 0.0, 0.0, 1.0, 1.0 },
+ { 1.0, 1.0, 0.0, 1.0 },
+ { 1.0, 1.0, 0.0, 1.0 },
+ };
+
+ draw_pixels_to_buffer(
+ (Pixel_Buffer) { .width = 4, .height = 4, .stride = 4, .pixels = dst, },
+ (Box) { .x = 0.4, .y = 0.4, .width = 4.0, .height = 4.0, },
+ (Pixel_Buffer) { .width = 2, .height = 2, .stride = 2, .pixels = src, }
+ );
+
+ // for (i64 i = 0; i < (i64) (sizeof dst / sizeof *dst); ++i) {
+ // printf("%g, %g, %g, %g \n", dst[i].x, dst[i].y, dst[i].z, dst[i].w);
+ // }
+
+ b8 ok = 1;
+
+ for (i64 i = 0; i < 16; ++i)
+ ok = ok && dst[i].x == expect[i].x
+ && dst[i].y == expect[i].y
+ && dst[i].z == expect[i].z
+ && dst[i].w == expect[i].w;
+
+ REQUIRE(ok);
+}
+
+TEST("scale up aliasing underflow") {
+ vec4_f32 src[4] = {
+ { 1.0, 0.0, 0.0, 1.0 },
+ { 0.0, 1.0, 0.0, 1.0 },
+
+ { 0.0, 0.0, 1.0, 1.0 },
+ { 1.0, 1.0, 0.0, 1.0 },
+ };
+
+ vec4_f32 dst[16];
+
+ vec4_f32 expect[16] = {
+ { 1.0, 0.0, 0.0, 1.0 },
+ { 1.0, 0.0, 0.0, 1.0 },
+ { 0.0, 1.0, 0.0, 1.0 },
+ { 0.0, 1.0, 0.0, 1.0 },
+
+ { 1.0, 0.0, 0.0, 1.0 },
+ { 1.0, 0.0, 0.0, 1.0 },
+ { 0.0, 1.0, 0.0, 1.0 },
+ { 0.0, 1.0, 0.0, 1.0 },
+
+ { 0.0, 0.0, 1.0, 1.0 },
+ { 0.0, 0.0, 1.0, 1.0 },
+ { 1.0, 1.0, 0.0, 1.0 },
+ { 1.0, 1.0, 0.0, 1.0 },
+
+ { 0.0, 0.0, 1.0, 1.0 },
+ { 0.0, 0.0, 1.0, 1.0 },
+ { 1.0, 1.0, 0.0, 1.0 },
+ { 1.0, 1.0, 0.0, 1.0 },
+ };
+
+ draw_pixels_to_buffer(
+ (Pixel_Buffer) { .width = 4, .height = 4, .stride = 4, .pixels = dst, },
+ (Box) { .x = -0.4, .y = -0.4, .width = 4.0, .height = 4.0, },
+ (Pixel_Buffer) { .width = 2, .height = 2, .stride = 2, .pixels = src, }
+ );
+
+ // for (i64 i = 0; i < (i64) (sizeof dst / sizeof *dst); ++i) {
+ // printf("%g, %g, %g, %g \n", dst[i].x, dst[i].y, dst[i].z, dst[i].w);
+ // }
+
+ b8 ok = 1;
+
+ for (i64 i = 0; i < 16; ++i)
+ ok = ok && dst[i].x == expect[i].x
+ && dst[i].y == expect[i].y
+ && dst[i].z == expect[i].z
+ && dst[i].w == expect[i].w;
+
+ REQUIRE(ok);
+}
+
#undef TEST_FILE
#endif // ENABLE_TESTING