raster

2D vector graphics rasterizer
git clone git://git.rr3.xyz/raster
Log | Files | Refs | README | LICENSE

commit 5571e58e66502b61a2a0624ee3f14f355d430363
parent 9cfeff00354c5e27f4f1c878beae84e0e337d07b
Author: Robert Russell <robert@rr3.xyz>
Date:   Fri, 30 Aug 2024 20:51:35 -0700

Play around with gradients

Diffstat:
Mmain.c | 150+++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
1 file changed, 104 insertions(+), 46 deletions(-)

diff --git a/main.c b/main.c @@ -12,6 +12,8 @@ typedef Vec2 Edge2[2]; typedef Vec2 Quad2[2]; typedef struct cycle2 Cycle2; typedef struct image2 Image2; +typedef struct linear_gradient LinearGradient; +typedef void Shader(Vec4 *pixel, Vec2 pos, f32 alpha, void *arg); struct cycle2 { usize len; @@ -24,13 +26,28 @@ struct image2 { Vec4 *pixels; // Row major order, starting in upper left }; +struct linear_gradient { + Vec2 pos0; + Vec4 color0; + Vec2 pos1; + Vec4 color1; +}; + f32 clampf(f32 x, f32 min, f32 max); void vec2_add(Vec2 *r, Vec2 u, Vec2 v); void vec3_add(Vec3 *r, Vec3 u, Vec3 v); void vec4_add(Vec4 *r, Vec4 u, Vec4 v); +void vec2_sub(Vec2 *r, Vec2 u, Vec2 v); +void vec3_sub(Vec3 *r, Vec3 u, Vec3 v); +void vec4_sub(Vec4 *r, Vec4 u, Vec4 v); void vec2_mul(Vec2 *r, f32 a, Vec2 v); void vec3_mul(Vec3 *r, f32 a, Vec3 v); void vec4_mul(Vec4 *r, f32 a, Vec4 v); +f32 vec2_dot(Vec2 u, Vec2 v); +f32 vec3_dot(Vec3 u, Vec3 v); +f32 vec4_dot(Vec4 u, Vec4 v); +f32 vec2_hypot(Vec2 v); +f32 vec2_dist(Vec2 u, Vec2 v); void aff3_mul(Aff3 *r, Aff3 a, Aff3 b); void aff3_app(Vec2 *r, Aff3 a, Vec2 b); void aff3_scale(Aff3 *r, f32 x, f32 y); @@ -40,7 +57,7 @@ f32 quad2_width(Quad2 q); f32 quad2_height(Quad2 q); void quad2_transform(Quad2 *r, Aff3 t, Quad2 q); void image2_bbox(Quad2 *r, Image2 image); -int fill(Image2 image, Quad2 src_view, Cycle2 cycle, Vec4 color); +int fill(Image2 image, Quad2 src_view, Cycle2 cycle, Shader shader, void *shader_arg); f32 clampf(f32 x, f32 min, f32 max) { @@ -53,10 +70,21 @@ void vec2_add(Vec2 *r, Vec2 u, Vec2 v) { for (usize i = 0; i < 2; i++) (*r)[i] = void vec3_add(Vec3 *r, Vec3 u, Vec3 v) { for (usize i = 0; i < 3; i++) (*r)[i] = u[i] + v[i]; } void vec4_add(Vec4 *r, Vec4 u, Vec4 v) { for (usize i = 0; i < 4; i++) (*r)[i] = u[i] + v[i]; } +void vec2_sub(Vec2 *r, Vec2 u, Vec2 v) { for (usize i = 0; i < 2; i++) (*r)[i] = u[i] - v[i]; } +void vec3_sub(Vec3 *r, Vec3 u, Vec3 v) { for (usize i = 0; i < 3; i++) (*r)[i] = u[i] - v[i]; } +void vec4_sub(Vec4 *r, Vec4 u, Vec4 v) { for (usize i = 0; i < 4; i++) (*r)[i] = u[i] - v[i]; } + void vec2_mul(Vec2 *r, f32 a, Vec2 v) { for (usize i = 0; i < 2; i++) (*r)[i] = a * v[i]; } void vec3_mul(Vec3 *r, f32 a, Vec3 v) { for (usize i = 0; i < 3; i++) (*r)[i] = a * v[i]; } void vec4_mul(Vec4 *r, f32 a, Vec4 v) { for (usize i = 0; i < 4; i++) (*r)[i] = a * v[i]; } +f32 vec2_dot(Vec2 u, Vec2 v) { return u[0] * v[0] + u[1] * v[1]; } +f32 vec3_dot(Vec3 u, Vec3 v) { return u[0] * v[0] + u[1] * v[1] + u[2] * v[2]; } +f32 vec4_dot(Vec4 u, Vec4 v) { return u[0] * v[0] + u[1] * v[1] + u[2] * v[2] + u[3] * v[3]; } + +f32 vec2_hypot(Vec2 v) { return hypotf(v[0], v[1]); } +f32 vec2_dist(Vec2 u, Vec2 v) { return hypotf(v[0] - u[0], v[1] - u[1]); } + void aff3_mul(Aff3 *r, Aff3 a, Aff3 b) { SET(*r, ((Aff3){ @@ -138,7 +166,7 @@ image2_bbox(Quad2 *r, Image2 image) { // TODO: Support filling collections of cycles (like Postscript), not only one int -fill(Image2 image, Quad2 src_view, Cycle2 cycle, Vec4 color) { +fill(Image2 image, Quad2 src_view, Cycle2 cycle, Shader shader, void *shader_arg) { if (cycle.len < 3) return -1; Quad2 dst_view; @@ -330,17 +358,14 @@ fill(Image2 image, Quad2 src_view, Cycle2 cycle, Vec4 color) { } } + f32 y = ((f32)(image.h - i) - 0.5f) / (f32)image.h; f32 s = 0.0f; for (usize j = 0; j < image.w; j++) { s += deltas[j]; - f32 a = clampf(values[j] + s, 0.0f, 1.0f); - - // TODO: More blending options. - Vec4 c; - vec4_mul(&c, a, color); - Vec4 *p = &image.pixels[i * image.h + j]; - vec4_mul(p, 1.0f - c[3], *p); - vec4_add(p, c, *p); + f32 alpha = clampf(values[j] + s, 0.0f, 1.0f); + Vec4 *pixel = &image.pixels[i * image.h + j]; + f32 x = ((f32)j + 0.5f) / (f32)image.w; + shader(pixel, (Vec2){x, y}, alpha, shader_arg); } } @@ -350,6 +375,35 @@ fill(Image2 image, Quad2 src_view, Cycle2 cycle, Vec4 color) { } void +shader_over(Vec4 *pixel, Vec2 pos, f32 alpha, void *arg) { + Vec4 *color = arg; + + Vec4 c; + vec4_mul(&c, alpha, *color); + vec4_mul(pixel, 1.0f - c[3], *pixel); + vec4_add(pixel, c, *pixel); +} + +void +shader_linear_gradient(Vec4 *pixel, Vec2 pos, f32 alpha, void *arg) { + LinearGradient *lg = arg; + + // Project pos onto the gradient's axis. + // TODO: This is wrong somehow. + Vec2 v; vec2_sub(&v, lg->pos1, lg->pos0); + Vec2 w; vec2_sub(&w, pos, lg->pos0); + f32 t = vec2_dot(v, w) / vec2_hypot(v); + + Vec4 color0; vec4_mul(&color0, 1.0f - t, lg->color0); + Vec4 color1; vec4_mul(&color1, t, lg->color1); + Vec4 color; vec4_add(&color, color0, color1); + + vec4_mul(&color, alpha, color); + vec4_mul(pixel, 1.0f - color[3], *pixel); + vec4_add(pixel, color, *pixel); +} + +void write_farbfeld(FILE *f, Image2 image) { char header[16]; memcpy(header, "farbfeld", 8); @@ -376,60 +430,64 @@ write_farbfeld(FILE *f, Image2 image) { } } -int -main(void) { - //Cycle2 cycle = { - // .len = 3, - // .verts = (Vec2[]){ - // { 0.0f, 0.0f }, - // { 1.0f, 0.0f }, - // { 0.5f, 1.0f }, - // }, - //}; - - //Cycle2 cycle = { - // .len = 4, - // .verts = (Vec2[]){ - // { 0.2f, 0.2f }, - // { 0.8f, 0.2f }, - // { 0.8f, 0.8f }, - // { 0.2f, 0.8f }, - // }, - //}; - +void +fill_under(Image2 image, f32 f(f32), usize n, Shader shader, void *shader_arg) { Cycle2 cycle = { - .len = 6, - .verts = (Vec2[]){ - { 0.1f, 0.1f }, - { 0.5f, 0.3f }, - { 0.9f, 0.1f }, - { 0.8f, 0.9f }, - { 0.5f, 0.5f }, - { 0.2f, 0.9f }, - }, + .len = n + 3, + .verts = r_ealloc((n + 3) * sizeof(Vec2)), }; + SET(cycle.verts[0], ((Vec2){0.0f, 0.0f})); + SET(cycle.verts[1], ((Vec2){1.0f, 0.0f})); + for (usize i = 0; i <= n; i++) { + f32 x = 1.0f - (f32)i / (f32)n; + SET(cycle.verts[i+2], ((Vec2){x, f(x)})); + } + Quad2 cycle_view = { { 0.0f, 0.0f }, { 1.0f, 1.0f }, }; + if (fill(image, cycle_view, cycle, shader, shader_arg) < 0) + r_fatalf("fill error"); + + free(cycle.verts); +} + +static f32 pi = acosf(-1.0f); +f32 waves(f32 x) { return sinf(50.0f * pi * x) * 0.01f + 0.4f; } +f32 mountains(f32 x) { return sinf(3.0f * pi * x) * 0.2f + sinf(100.0f * pi * x) * 0.003f + 0.4f; } + +int +main(void) { Image2 image = { .w = 1000, .h = 1000, .pixels = r_ealloc(1000 * 1000 * sizeof(Vec4)), }; - Vec4 white = { 1.0f, 1.0f, 1.0f, 1.0f }; + Vec4 bg = { 0.6f, 0.6f, 0.65f, 1.0f }; for (usize i = 0; i < image.h; i++) { for (usize j = 0; j < image.w; j++) - SET(image.pixels[i * image.h + j], white); + SET(image.pixels[i * image.h + j], bg); } - f32 a = 1.0f; - Vec4 color = { 0.1f * a, 0.6f * a, 0.3f * a, a }; - if (fill(image, cycle_view, cycle, color) < 0) - r_fatalf("fill error"); + fill_under(image, waves, 1000, shader_linear_gradient, + &(LinearGradient){ + .pos0 = { 0.0f, 0.0f }, + .color0 = { 0.0f, 0.0f, 0.0f, 1.0f }, + .pos1 = { 0.0f, 0.5f }, + .color1 = { 0.3f, 0.3f, 0.9f, 1.0f }, + }); + + fill_under(image, mountains, 1000, shader_linear_gradient, + &(LinearGradient){ + .pos0 = { 0.0f, 0.0f }, + .color0 = { 0.05f, 0.15f, 0.05f, 1.0f }, + .pos1 = { 0.0f, 1.0f }, + .color1 = { 0.15f, 0.5f, 0.15f, 1.0f }, + }); write_farbfeld(stdout, image); }