raster

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

main.c (14230B)


      1 #include <math.h>
      2 #include <stdio.h>
      3 #include <string.h>
      4 #include <rcx/all.h>
      5 
      6 typedef f32 Vec2[2];
      7 typedef f32 Vec3[3];
      8 typedef f32 Vec4[4];
      9 typedef f32 Mat3[3][3];
     10 typedef f32 Aff3[2][3];
     11 typedef Vec2 Edge2[2];
     12 typedef Vec2 Quad2[2];
     13 typedef struct cycle2 Cycle2;
     14 typedef struct image2 Image2;
     15 typedef struct linear_gradient LinearGradient;
     16 typedef void Shader(Vec4 *pixel, Vec2 pos, f32 alpha, void *arg);
     17 
     18 struct cycle2 {
     19 	usize len;
     20 	Vec2 *verts;
     21 };
     22 
     23 // TODO: More format options (not only f32 RGBA)
     24 struct image2 {
     25 	usize w, h;
     26 	Vec4 *pixels; // Row major order, starting in upper left
     27 };
     28 
     29 struct linear_gradient {
     30 	Vec2 pos0;
     31 	Vec4 color0;
     32 	Vec2 pos1;
     33 	Vec4 color1;
     34 };
     35 
     36 f32 clampf(f32 x, f32 min, f32 max);
     37 void vec2_add(Vec2 *r, Vec2 u, Vec2 v);
     38 void vec3_add(Vec3 *r, Vec3 u, Vec3 v);
     39 void vec4_add(Vec4 *r, Vec4 u, Vec4 v);
     40 void vec2_sub(Vec2 *r, Vec2 u, Vec2 v);
     41 void vec3_sub(Vec3 *r, Vec3 u, Vec3 v);
     42 void vec4_sub(Vec4 *r, Vec4 u, Vec4 v);
     43 void vec2_mul(Vec2 *r, f32 a, Vec2 v);
     44 void vec3_mul(Vec3 *r, f32 a, Vec3 v);
     45 void vec4_mul(Vec4 *r, f32 a, Vec4 v);
     46 f32 vec2_dot(Vec2 u, Vec2 v);
     47 f32 vec3_dot(Vec3 u, Vec3 v);
     48 f32 vec4_dot(Vec4 u, Vec4 v);
     49 f32 vec2_hypot(Vec2 v);
     50 f32 vec2_dist(Vec2 u, Vec2 v);
     51 void aff3_mul(Aff3 *r, Aff3 a, Aff3 b);
     52 void aff3_app(Vec2 *r, Aff3 a, Vec2 b);
     53 void aff3_scale(Aff3 *r, f32 x, f32 y);
     54 void aff3_translate(Aff3 *r, f32 x, f32 y);
     55 void aff3_view(Aff3 *r, Quad2 dst, Quad2 src);
     56 f32 quad2_width(Quad2 q);
     57 f32 quad2_height(Quad2 q);
     58 void quad2_transform(Quad2 *r, Aff3 t, Quad2 q);
     59 void image2_bbox(Quad2 *r, Image2 image);
     60 int fill(Image2 image, Quad2 src_view, Cycle2 cycle, Shader shader, void *shader_arg);
     61 
     62 f32
     63 clampf(f32 x, f32 min, f32 max) {
     64 	x = x < min ? min : x;
     65 	x = x > max ? max : x;
     66 	return x;
     67 }
     68 
     69 void vec2_add(Vec2 *r, Vec2 u, Vec2 v) { for (usize i = 0; i < 2; i++) (*r)[i] = u[i] + v[i]; }
     70 void vec3_add(Vec3 *r, Vec3 u, Vec3 v) { for (usize i = 0; i < 3; i++) (*r)[i] = u[i] + v[i]; }
     71 void vec4_add(Vec4 *r, Vec4 u, Vec4 v) { for (usize i = 0; i < 4; i++) (*r)[i] = u[i] + v[i]; }
     72 
     73 void vec2_sub(Vec2 *r, Vec2 u, Vec2 v) { for (usize i = 0; i < 2; i++) (*r)[i] = u[i] - v[i]; }
     74 void vec3_sub(Vec3 *r, Vec3 u, Vec3 v) { for (usize i = 0; i < 3; i++) (*r)[i] = u[i] - v[i]; }
     75 void vec4_sub(Vec4 *r, Vec4 u, Vec4 v) { for (usize i = 0; i < 4; i++) (*r)[i] = u[i] - v[i]; }
     76 
     77 void vec2_mul(Vec2 *r, f32 a, Vec2 v) { for (usize i = 0; i < 2; i++) (*r)[i] = a * v[i]; }
     78 void vec3_mul(Vec3 *r, f32 a, Vec3 v) { for (usize i = 0; i < 3; i++) (*r)[i] = a * v[i]; }
     79 void vec4_mul(Vec4 *r, f32 a, Vec4 v) { for (usize i = 0; i < 4; i++) (*r)[i] = a * v[i]; }
     80 
     81 f32 vec2_dot(Vec2 u, Vec2 v) { return u[0] * v[0] + u[1] * v[1]; }
     82 f32 vec3_dot(Vec3 u, Vec3 v) { return u[0] * v[0] + u[1] * v[1] + u[2] * v[2]; }
     83 f32 vec4_dot(Vec4 u, Vec4 v) { return u[0] * v[0] + u[1] * v[1] + u[2] * v[2] + u[3] * v[3]; }
     84 
     85 f32 vec2_hypot(Vec2 v) { return hypotf(v[0], v[1]); }
     86 f32 vec2_dist(Vec2 u, Vec2 v) { return hypotf(v[0] - u[0], v[1] - u[1]); }
     87 
     88 void
     89 aff3_mul(Aff3 *r, Aff3 a, Aff3 b) {
     90 	SET(*r, ((Aff3){
     91 		{
     92 			a[0][0] * b[0][0] + a[0][1] * b[1][0],
     93 			a[0][0] * b[0][1] + a[0][1] * b[1][1],
     94 			a[0][0] * b[0][2] + a[0][1] * b[1][2] + a[0][2],
     95 		}, {
     96 			a[1][0] * b[0][0] + a[1][1] * b[1][0],
     97 			a[1][0] * b[0][1] + a[1][1] * b[1][1],
     98 			a[1][0] * b[0][2] + a[1][1] * b[1][2] + a[1][2],
     99 		}
    100 	}));
    101 }
    102 
    103 void
    104 aff3_app(Vec2 *r, Aff3 a, Vec2 b) {
    105 	SET(*r, ((Vec2){
    106 		a[0][0] * b[0] + a[0][1] * b[1] + a[0][2],
    107 		a[1][0] * b[0] + a[1][1] * b[1] + a[1][2],
    108 	}));
    109 }
    110 
    111 void
    112 aff3_scale(Aff3 *r, f32 x, f32 y) {
    113 	SET(*r, ((Aff3){
    114 		{    x, 0.0f, 0.0f },
    115 		{ 0.0f,    y, 0.0f },
    116 	}));
    117 }
    118 
    119 void
    120 aff3_translate(Aff3 *r, f32 x, f32 y) {
    121 	SET(*r, ((Aff3){
    122 		{ 1.0f, 0.0f, x },
    123 		{ 0.0f, 1.0f, y },
    124 	}));
    125 }
    126 
    127 // Find the affine transformation that maps src to dst.
    128 void
    129 aff3_view(Aff3 *r, Quad2 dst, Quad2 src) {
    130 	f32 dstw = quad2_width(dst);
    131 	f32 dsth = quad2_height(dst);
    132 	f32 srcw = quad2_width(src);
    133 	f32 srch = quad2_height(src);
    134 
    135 	ASSERT(dstw > 0 && dsth > 0 && srcw > 0 && srch > 0);
    136 
    137 	Aff3 t0, s, t1;
    138 	aff3_translate(&t0, -src[0][0], -src[0][0]);
    139 	aff3_scale(&s, dstw / srcw, dsth / srch);
    140 	aff3_translate(&t1, dst[0][0], dst[0][1]);
    141 	aff3_mul(r, s, t0);
    142 	aff3_mul(r, t1, *r);
    143 }
    144 
    145 f32 quad2_width(Quad2 q) { return q[1][0] - q[0][0]; }
    146 f32 quad2_height(Quad2 q) { return q[1][1] - q[0][1]; }
    147 
    148 void
    149 quad2_transform(Quad2 *r, Aff3 t, Quad2 q) {
    150 	Vec2 v0, v1;
    151 	aff3_app(&v0, t, q[0]);
    152 	aff3_app(&v1, t, q[1]);
    153 	SET(*r, ((Quad2){
    154 		{ MIN(v0[0], v1[0]), MIN(v0[1], v1[1]) },
    155 		{ MAX(v0[0], v1[0]), MAX(v0[1], v1[1]) },
    156 	}));
    157 }
    158 
    159 void
    160 image2_bbox(Quad2 *r, Image2 image) {
    161 	SET(*r, ((Quad2){
    162 		{ 0.0f, 0.0f },
    163 		{ (f32)image.w, (f32)image.h },
    164 	}));
    165 }
    166 
    167 // TODO: Support filling collections of cycles (like Postscript), not only one
    168 int
    169 fill(Image2 image, Quad2 src_view, Cycle2 cycle, Shader shader, void *shader_arg) {
    170 	if (cycle.len < 3) return -1;
    171 
    172 	Quad2 dst_view;
    173 	image2_bbox(&dst_view, image);
    174 	Aff3 view_matrix;
    175 	aff3_view(&view_matrix, dst_view, src_view);
    176 
    177 	// TODO: Copy cycle, apply view_matrix to it (to avoid recalculuations),
    178 	// and sort by top vertex (to avoid repeatedly considering irrelevant
    179 	// edges). Benchmark the difference.
    180 
    181 	f32 *mem = r_alloc((2 * image.w + 1) * sizeof *mem);
    182 	if (!mem) return -1;
    183 	f32 *values = mem;
    184 	f32 *deltas = mem + image.w;
    185 
    186 	for (usize i = 0; i < image.h; i++) {
    187 		Quad2 row_bbox = {
    188 			{ 0.0f, (f32)(image.h - i - 1) },
    189 			{ (f32)image.w, (f32)(image.h - i) },
    190 		};
    191 
    192 		memset(values, 0, image.w * sizeof *values);
    193 		memset(deltas, 0, (image.w + 1) * sizeof *deltas);
    194 
    195 		for (usize k = 0; k < cycle.len; k++) {
    196 			Edge2 e;
    197 			aff3_app(&e[0], view_matrix, cycle.verts[k]);
    198 			aff3_app(&e[1], view_matrix, cycle.verts[(k+1)%cycle.len]);
    199 
    200 			// c, initialized below, is e vertically clipped to the row. We
    201 			// can't horizontally clip e to the row, because, e.g., e could be
    202 			// entirely to the left of the image while still contributing area
    203 			// (see case 1 below).
    204 			Edge2 c;
    205 
    206 			// Compute pointers to the bottom-most (b) and top-most (t)
    207 			// vertices of e and c.
    208 			f32 *eb, *et;
    209 			f32 *cb, *ct;
    210 			if (e[0][1] < e[1][1]) {
    211 				eb = e[0], et = e[1];
    212 				cb = c[0], ct = c[1];
    213 			} else {
    214 				eb = e[1], et = e[0];
    215 				cb = c[1], ct = c[0];
    216 			}
    217 
    218 			if (et[1] <= row_bbox[0][1]) continue; // e is below the row
    219 			if (row_bbox[1][1] <= eb[1]) continue; // e is above the row
    220 
    221 			f32 e_dx = e[1][0] - e[0][0];
    222 			f32 e_dy = e[1][1] - e[0][1];
    223 			f32 dx_dy = e_dx / e_dy;
    224 			f32 dy_dx = e_dy / e_dx;
    225 
    226 			// Initialize c (through cb and ct).
    227 			if (eb[1] < row_bbox[0][1]) {
    228 				// Intersect e with the bottom of the row.
    229 				cb[0] = dx_dy * (row_bbox[0][1] - eb[1]) + eb[0];
    230 				cb[1] = row_bbox[0][1];
    231 			} else {
    232 				cb[0] = eb[0];
    233 				cb[1] = eb[1];
    234 			}
    235 			if (et[1] > row_bbox[1][1]) {
    236 				// Intersect e with the top of the row.
    237 				ct[0] = dx_dy * (row_bbox[1][1] - et[1]) + et[0];
    238 				ct[1] = row_bbox[1][1];
    239 			} else {
    240 				ct[0] = et[0];
    241 				ct[1] = et[1];
    242 			}
    243 
    244 			// Compute pointers to the left-most (l) and right-most (r)
    245 			// vertices of c.
    246 			f32 *cl, *cr;
    247 			if (e[0][0] < e[1][0])
    248 				cl = c[0], cr = c[1];
    249 			else
    250 				cl = c[1], cr = c[0];
    251 
    252 			// TODO: Diagrams for each case. ┌ ┐ ┘ └
    253 
    254 			// Case 1: The clipped edge is entirely to the right of the image.
    255 			if (row_bbox[1][0] <= cl[0]) {
    256 				// No pixels are right of the edge, so we do nothing.
    257 				continue;
    258 			}
    259 
    260 			// Case 2: The clipped edge is entirely to the left of the image.
    261 			//             ╔═════════⋯═════════╗
    262 			//             ⋮                   ⋮          
    263 			//         *   ║▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒║╮
    264 			//        /    ║▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒║├ Filled on previous iters
    265 			//       /     ║▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒║╯
    266 			//      /      ║▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒║← Filled on current iter
    267 			//     /       ║▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒║╮
    268 			//    /        ║▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒║├ Filled on following iters
    269 			//   *         ║▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒║╯
    270 			//             ⋮                   ⋮          
    271 			//             ╚═════════⋯═════════╝
    272 			// TODO: Misleading picture: the edge is clipped to the row.
    273 			if (cr[0] <= row_bbox[0][0]) {
    274 				deltas[0] += c[0][1] - c[1][1]; // Note the sign.
    275 				continue;
    276 			}
    277 
    278 			// The indices of the left-most and right-most pixels intersecting
    279 			// the edge. These could be out of bounds.
    280 			f32 coll = floorf(cl[0]);
    281 			f32 colr = floorf(cr[0]);
    282 
    283 			// Case 3: The clipped edge intersects exactly one pixel in the
    284 			// current row.
    285 			// TODO: Explain why we must separate this case.
    286 			if (coll == colr) {
    287 				// Compute the area of the trapezoid covering the pixel that
    288 				// intersects the edge.
    289 				f32 w0 = (coll + 1.0f) - c[0][0];
    290 				f32 w1 = (coll + 1.0f) - c[1][0];
    291 				f32 h = c[0][1] - c[1][1]; // Note the sign.
    292 				f32 area = (w0 + w1) / 2.0f * h;
    293 
    294 				usize j = coll;
    295 				ASSERT(j < image.w);
    296 				values[j] += area;
    297 				deltas[j + 1] += h;
    298 
    299 				continue;
    300 			}
    301 
    302 			// Case 4: 
    303 			{
    304 				Vec2 p;
    305 				p[0] = MAX(coll + 1.0f, row_bbox[0][0]);
    306 				p[1] = dy_dx * (p[0] - cl[0]) + cl[1];
    307 
    308 				// Assume e goes from SW to NE. Then we can multiply areas
    309 				// by this sign for correctness in the other three possible
    310 				// configurations.
    311 				// TODO: Explain with diagram.
    312 				// TODO: Use this convention in other cases too?
    313 				f32 sign = cl == c[0] ? -1.0f : +1.0f;
    314 
    315 				// Account for the triangle in the left-most pixel.
    316 				if (row_bbox[0][0] <= cl[0]) {
    317 					f32 w = p[0] - cl[0];
    318 					f32 h = p[1] - cl[1];
    319 					f32 area = w * h / 2.0f;
    320 
    321 					usize j = coll;
    322 					ASSERT(j < image.w);
    323 					values[j] += sign * area;
    324 				}
    325 
    326 				f32 rect_area = p[1] - cl[1];
    327 
    328 				// Account for the trapezoids in the middle pixels.
    329 				f32 trap_coll = MAX(coll + 1.0f, 0.0f);
    330 				f32 trap_colr = MIN(colr - 1.0f, (f32)(image.w - 1));
    331 				if (trap_coll <= trap_colr) {
    332 					usize jl = trap_coll;
    333 					usize jr = trap_colr;
    334 					for (usize j = jl; j <= jr; j++) {
    335 						values[j] += sign * (rect_area + dy_dx / 2.0f);
    336 						rect_area += dy_dx;
    337 					}
    338 				}
    339 
    340 				// Account for the pentagon in the right-most pixel.
    341 				if (cr[0] < row_bbox[1][0]) {
    342 					Vec2 q;
    343 					q[0] = colr;
    344 					q[1] = dy_dx * (q[0] - cr[0]) + cr[1];
    345 
    346 					// Compute area of trapezoid on top of the rectangle.
    347 					// TODO: Diagram.
    348 					f32 w0 = (colr + 1.0f) - cr[0];
    349 					f32 w1 = 1.0f;
    350 					f32 h = cr[1] - q[1];
    351 					f32 trap_area = (w0 + w1) / 2.0f * h;
    352 
    353 					usize j = colr;
    354 					ASSERT(j < image.w);
    355 					values[j] += sign * (rect_area + trap_area);
    356 					deltas[j + 1] += sign * (cr[1] - cl[1]);
    357 				}
    358 			}
    359 		}
    360 
    361 		f32 y = ((f32)(image.h - i) - 0.5f) / (f32)image.h;
    362 		f32 s = 0.0f;
    363 		for (usize j = 0; j < image.w; j++) {
    364 			s += deltas[j];
    365 			f32 alpha = clampf(values[j] + s, 0.0f, 1.0f);
    366 			Vec4 *pixel = &image.pixels[i * image.h + j];
    367 			f32 x = ((f32)j + 0.5f) / (f32)image.w;
    368 			shader(pixel, (Vec2){x, y}, alpha, shader_arg);
    369 		}
    370 	}
    371 
    372 	free(mem);
    373 
    374 	return 0;
    375 }
    376 
    377 void
    378 shader_over(Vec4 *pixel, Vec2 pos, f32 alpha, void *arg) {
    379 	Vec4 *color = arg;
    380 
    381 	Vec4 c;
    382 	vec4_mul(&c, alpha, *color);
    383 	vec4_mul(pixel, 1.0f - c[3], *pixel);
    384 	vec4_add(pixel, c, *pixel);
    385 }
    386 
    387 void
    388 shader_linear_gradient(Vec4 *pixel, Vec2 pos, f32 alpha, void *arg) {
    389 	LinearGradient *lg = arg;
    390 
    391 	// Project pos onto the gradient's axis.
    392 	// TODO: This is wrong somehow.
    393 	Vec2 v; vec2_sub(&v, lg->pos1, lg->pos0);
    394 	Vec2 w; vec2_sub(&w, pos, lg->pos0);
    395 	f32 t = vec2_dot(v, w) / vec2_hypot(v);
    396 
    397 	Vec4 color0; vec4_mul(&color0, 1.0f - t, lg->color0);
    398 	Vec4 color1; vec4_mul(&color1, t, lg->color1);
    399 	Vec4 color; vec4_add(&color, color0, color1);
    400 
    401 	vec4_mul(&color, alpha, color);
    402 	vec4_mul(pixel, 1.0f - color[3], *pixel);
    403 	vec4_add(pixel, color, *pixel);
    404 }
    405 
    406 void
    407 write_farbfeld(FILE *f, Image2 image) {
    408 	char header[16];
    409 	memcpy(header, "farbfeld", 8);
    410 	r_writeb32(header + 8, image.w);
    411 	r_writeb32(header + 12, image.h);
    412 	if (!fwrite(header, sizeof header, 1, f))
    413 		r_fatalf("write_farbfeld: failed to write header");
    414 
    415 	for (usize i = 0; i < image.h; i++) {
    416 		for (usize j = 0; j < image.w; j++) {
    417 			Vec4 *p = &image.pixels[i * image.w + j];
    418 
    419 			// TODO: Clamp pixel values?
    420 			u16 buf[4];
    421 			for (usize k = 0; k < 3; k++) {
    422 				f32 a = (*p)[k] / (*p)[3]; // Un-premultiply alpha
    423 				buf[k] = r_htob16(a * (f32)U16_MAX);
    424 			}
    425 			buf[3] = r_htob16((*p)[3] * (f32)U16_MAX);
    426 
    427 			if (!fwrite(buf, sizeof buf, 1, f))
    428 				r_fatalf("write_farbfeld: failed to write pixel data");
    429 		}
    430 	}
    431 }
    432 
    433 void
    434 fill_under(Image2 image, f32 f(f32), usize n, Shader shader, void *shader_arg) {
    435 	Cycle2 cycle = {
    436 		.len = n + 3,
    437 		.verts = r_ealloc((n + 3) * sizeof(Vec2)),
    438 	};
    439 
    440 	SET(cycle.verts[0], ((Vec2){0.0f, 0.0f}));
    441 	SET(cycle.verts[1], ((Vec2){1.0f, 0.0f}));
    442 	for (usize i = 0; i <= n; i++) {
    443 		f32 x = 1.0f - (f32)i / (f32)n;
    444 		SET(cycle.verts[i+2], ((Vec2){x, f(x)}));
    445 	}
    446 
    447 	Quad2 cycle_view = {
    448 		{ 0.0f, 0.0f },
    449 		{ 1.0f, 1.0f },
    450 	};
    451 
    452 	if (fill(image, cycle_view, cycle, shader, shader_arg) < 0)
    453 		r_fatalf("fill error");
    454 
    455 	free(cycle.verts);
    456 }
    457 
    458 static f32 pi = acosf(-1.0f);
    459 f32 waves(f32 x) { return sinf(50.0f * pi * x) * 0.01f + 0.4f; }
    460 f32 mountains(f32 x) { return sinf(3.0f * pi * x) * 0.2f + sinf(100.0f * pi * x) * 0.003f + 0.4f; }
    461 
    462 int
    463 main(void) {
    464 	Image2 image = {
    465 		.w = 1000,
    466 		.h = 1000,
    467 		.pixels = r_ealloc(1000 * 1000 * sizeof(Vec4)),
    468 	};
    469 
    470 	Vec4 bg = { 0.6f, 0.6f, 0.65f, 1.0f };
    471 	for (usize i = 0; i < image.h; i++) {
    472 		for (usize j = 0; j < image.w; j++)
    473 			SET(image.pixels[i * image.h + j], bg);
    474 	}
    475 
    476 	fill_under(image, waves, 1000, shader_linear_gradient,
    477 		&(LinearGradient){
    478 			.pos0 = { 0.0f, 0.0f },
    479 			.color0 = { 0.0f, 0.0f, 0.0f, 1.0f },
    480 			.pos1 = { 0.0f, 0.5f },
    481 			.color1 = { 0.3f, 0.3f, 0.9f, 1.0f },
    482 		});
    483 
    484 	fill_under(image, mountains, 1000, shader_linear_gradient,
    485 		&(LinearGradient){
    486 			.pos0 = { 0.0f, 0.0f },
    487 			.color0 = { 0.05f, 0.15f, 0.05f, 1.0f },
    488 			.pos1 = { 0.0f, 1.0f },
    489 			.color1 = { 0.15f, 0.5f, 0.15f, 1.0f },
    490 		});
    491 
    492 	write_farbfeld(stdout, image);
    493 }