raster

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

commit df591f57ff459fcfbe08ea4cbc7335fa448b7a73
parent 77b8f4b38b90b00ece693bb82dcbbfbfd07ef223
Author: Robert Russell <robertrussell.72001@gmail.com>
Date:   Wed, 10 Jul 2024 15:09:11 -0700

Fix some bugs

- Blending was wrong
- Calculation of p and q used wrong x value
- I mixed up black and white in the test (this took embarrassingly
  long to realize)

Diffstat:
Mmain.c | 84++++++++++++++++++++++++++++++++++++++++++-------------------------------------
1 file changed, 45 insertions(+), 39 deletions(-)

diff --git a/main.c b/main.c @@ -58,7 +58,7 @@ void vec4_mul(Vec4 *r, f32 a, Vec4 v) { for (usize i = 0; i < 4; i++) (*r)[i] = void aff3_mul(Aff3 *r, Aff3 a, Aff3 b) { - Aff3 rr = { + SET(*r, ((Aff3){ { a[0][0] * b[0][0] + a[0][1] * b[1][0], a[0][0] * b[0][1] + a[0][1] * b[1][1], @@ -68,35 +68,31 @@ aff3_mul(Aff3 *r, Aff3 a, Aff3 b) { a[1][0] * b[0][1] + a[1][1] * b[1][1], a[1][0] * b[0][2] + a[1][1] * b[1][2] + a[1][2], } - }; - memcpy(r, rr, sizeof *r); + })); } void aff3_app(Vec2 *r, Aff3 a, Vec2 b) { - Vec2 rr = { + SET(*r, ((Vec2){ a[0][0] * b[0] + a[0][1] * b[1] + a[0][2], a[1][0] * b[0] + a[1][1] * b[1] + a[1][2], - }; - memcpy(r, rr, sizeof *r); + })); } void aff3_scale(Aff3 *r, f32 x, f32 y) { - Aff3 rr = { + SET(*r, ((Aff3){ { x, 0.0f, 0.0f }, { 0.0f, y, 0.0f }, - }; - memcpy(r, rr, sizeof *r); + })); } void aff3_translate(Aff3 *r, f32 x, f32 y) { - Aff3 rr = { + SET(*r, ((Aff3){ { 1.0f, 0.0f, x }, { 0.0f, 1.0f, y }, - }; - memcpy(r, rr, sizeof *r); + })); } // Find the affine transformation that maps src to dst. @@ -125,20 +121,18 @@ quad2_transform(Quad2 *r, Aff3 t, Quad2 q) { Vec2 v0, v1; aff3_app(&v0, t, q[0]); aff3_app(&v1, t, q[1]); - Quad2 rr = { + SET(*r, ((Quad2){ { MIN(v0[0], v1[0]), MIN(v0[1], v1[1]) }, { MAX(v0[0], v1[0]), MAX(v0[1], v1[1]) }, - }; - memcpy(r, rr, sizeof *r); + })); } void image2_bbox(Quad2 *r, Image2 image) { - Quad2 rr = { + SET(*r, ((Quad2){ { 0.0f, 0.0f }, { (f32)image.w, (f32)image.h }, - }; - memcpy(r, rr, sizeof *r); + })); } int @@ -165,7 +159,7 @@ fill(Image2 image, Quad2 src_view, Cycle2 cycle, Vec4 color) { { (f32)image.w, (f32)(image.h - i) }, }; - memset(values, 0, image.w * sizeof *deltas); + memset(values, 0, image.w * sizeof *values); memset(deltas, 0, (image.w + 1) * sizeof *deltas); for (usize k = 0; k < cycle.len; k++) { @@ -225,7 +219,7 @@ fill(Image2 image, Quad2 src_view, Cycle2 cycle, Vec4 color) { else cl = c[1], cr = c[0]; - // ┌ ┐ ┘ └ + // TODO: Diagrams for each case. ┌ ┐ ┘ └ // Case 1: The clipped edge is entirely to the right of the image. if (row_bbox[1][0] <= cl[0]) { @@ -278,7 +272,7 @@ fill(Image2 image, Quad2 src_view, Cycle2 cycle, Vec4 color) { // Case 4: { Vec2 p; - p[0] = MAX(cl[0], row_bbox[0][0]); + p[0] = MAX(coll + 1.0f, row_bbox[0][0]); p[1] = dy_dx * (p[0] - cl[0]) + cl[1]; // Assume e goes from SW to NE. Then we can multiply areas @@ -299,7 +293,7 @@ fill(Image2 image, Quad2 src_view, Cycle2 cycle, Vec4 color) { values[j] += sign * area; } - f32 rect_area = p[0] - cl[0]; + f32 rect_area = p[1] - cl[1]; // Account for the trapezoids in the middle pixels. f32 trap_coll = MAX(coll + 1.0f, 0.0f); @@ -316,7 +310,7 @@ fill(Image2 image, Quad2 src_view, Cycle2 cycle, Vec4 color) { // Account for the pentagon in the right-most pixel. if (cr[0] < row_bbox[1][0]) { Vec2 q; - q[0] = cr[0]; + q[0] = colr; q[1] = dy_dx * (q[0] - cr[0]) + cr[1]; // Compute area of trapezoid on top of the rectangle. @@ -338,10 +332,13 @@ fill(Image2 image, Quad2 src_view, Cycle2 cycle, Vec4 color) { for (usize j = 0; j < image.w; j++) { s += deltas[j]; f32 a = clampf(values[j] + s, 0.0f, 1.0f); - Vec4 *p = &image.pixels[i * image.h + j]; + // TODO: More blending options. - vec4_mul(p, 1.0f - a, *p); - vec4_add(p, color, *p); + 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); } } @@ -352,23 +349,23 @@ fill(Image2 image, Quad2 src_view, Cycle2 cycle, Vec4 color) { void image2_write_farbfeld(FILE *f, Image2 image) { - usize w = image.w; - usize h = image.h; - char header[16]; memcpy(header, "farbfeld", 8); - r_writeb32(header + 8, w); - r_writeb32(header + 12, h); + r_writeb32(header + 8, image.w); + r_writeb32(header + 12, image.h); if (!fwrite(header, sizeof header, 1, f)) r_fatalf("image2_write_farbfeld: failed to write header"); - for (usize i = 0; i < h; i++) { - for (usize j = 0; j < w; j++) { - Vec4 *p = &image.pixels[i * w + j]; + for (usize i = 0; i < image.h; i++) { + for (usize j = 0; j < image.w; j++) { + Vec4 *p = &image.pixels[i * image.w + j]; + // TODO: Clamp pixel values? u16 buf[4]; - for (usize k = 0; k < 3; k++) - buf[k] = r_htob16(((*p)[k] / (*p)[3]) * (f32)U16_MAX); + for (usize k = 0; k < 3; k++) { + f32 a = (*p)[k] / (*p)[3]; // Un-premultiply alpha + buf[k] = r_htob16(a * (f32)U16_MAX); + } buf[3] = r_htob16((*p)[3] * (f32)U16_MAX); if (!fwrite(buf, sizeof buf, 1, f)) @@ -379,8 +376,8 @@ image2_write_farbfeld(FILE *f, Image2 image) { int main(void) { - Vec4 white = { 0.0f, 0.0f, 0.0f, 1.0f }; - Vec4 black = { 1.0f, 1.0f, 1.0f, 1.0f }; + Vec4 black = { 0.0f, 0.0f, 0.0f, 1.0f }; + Vec4 white = { 1.0f, 1.0f, 1.0f, 1.0f }; Cycle2 cycle = { .len = 3, @@ -390,6 +387,15 @@ main(void) { { 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 }, + // }, + //}; Quad2 cycle_view = { { 0.0f, 0.0f }, { 1.0f, 1.0f }, @@ -402,7 +408,7 @@ main(void) { }; for (usize i = 0; i < image.h; i++) { for (usize j = 0; j < image.w; j++) - memcpy(&image.pixels[i * image.h + j], white, sizeof white); + SET(image.pixels[i * image.h + j], white); } if (fill(image, cycle_view, cycle, black) < 0)