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 }