| 1 | /* |
|---|
| 2 | * libcaca Colour ASCII-Art library |
|---|
| 3 | * Copyright (c) 2002-2010 Sam Hocevar <sam@hocevar.net> |
|---|
| 4 | * All Rights Reserved |
|---|
| 5 | * |
|---|
| 6 | * This library is free software. It comes without any warranty, to |
|---|
| 7 | * the extent permitted by applicable law. You can redistribute it |
|---|
| 8 | * and/or modify it under the terms of the Do What The Fuck You Want |
|---|
| 9 | * To Public License, Version 2, as published by Sam Hocevar. See |
|---|
| 10 | * http://sam.zoy.org/wtfpl/COPYING for more details. |
|---|
| 11 | */ |
|---|
| 12 | |
|---|
| 13 | /* |
|---|
| 14 | * This file contains triangle drawing functions, both filled and outline. |
|---|
| 15 | */ |
|---|
| 16 | |
|---|
| 17 | #include "config.h" |
|---|
| 18 | |
|---|
| 19 | #if !defined(__KERNEL__) |
|---|
| 20 | # include <stdlib.h> |
|---|
| 21 | #endif |
|---|
| 22 | |
|---|
| 23 | #include "caca.h" |
|---|
| 24 | #include "caca_internals.h" |
|---|
| 25 | |
|---|
| 26 | /** \brief Draw a triangle on the canvas using the given character. |
|---|
| 27 | * |
|---|
| 28 | * This function never fails. |
|---|
| 29 | * |
|---|
| 30 | * \param cv The handle to the libcaca canvas. |
|---|
| 31 | * \param x1 X coordinate of the first point. |
|---|
| 32 | * \param y1 Y coordinate of the first point. |
|---|
| 33 | * \param x2 X coordinate of the second point. |
|---|
| 34 | * \param y2 Y coordinate of the second point. |
|---|
| 35 | * \param x3 X coordinate of the third point. |
|---|
| 36 | * \param y3 Y coordinate of the third point. |
|---|
| 37 | * \param ch UTF-32 character to be used to draw the triangle outline. |
|---|
| 38 | * \return This function always returns 0. |
|---|
| 39 | */ |
|---|
| 40 | int caca_draw_triangle(caca_canvas_t * cv, int x1, int y1, int x2, int y2, |
|---|
| 41 | int x3, int y3, uint32_t ch) |
|---|
| 42 | { |
|---|
| 43 | caca_draw_line(cv, x1, y1, x2, y2, ch); |
|---|
| 44 | caca_draw_line(cv, x2, y2, x3, y3, ch); |
|---|
| 45 | caca_draw_line(cv, x3, y3, x1, y1, ch); |
|---|
| 46 | |
|---|
| 47 | return 0; |
|---|
| 48 | } |
|---|
| 49 | |
|---|
| 50 | /** \brief Draw a thin triangle on the canvas. |
|---|
| 51 | * |
|---|
| 52 | * This function never fails. |
|---|
| 53 | * |
|---|
| 54 | * \param cv The handle to the libcaca canvas. |
|---|
| 55 | * \param x1 X coordinate of the first point. |
|---|
| 56 | * \param y1 Y coordinate of the first point. |
|---|
| 57 | * \param x2 X coordinate of the second point. |
|---|
| 58 | * \param y2 Y coordinate of the second point. |
|---|
| 59 | * \param x3 X coordinate of the third point. |
|---|
| 60 | * \param y3 Y coordinate of the third point. |
|---|
| 61 | * \return This function always returns 0. |
|---|
| 62 | */ |
|---|
| 63 | int caca_draw_thin_triangle(caca_canvas_t * cv, int x1, int y1, |
|---|
| 64 | int x2, int y2, int x3, int y3) |
|---|
| 65 | { |
|---|
| 66 | caca_draw_thin_line(cv, x1, y1, x2, y2); |
|---|
| 67 | caca_draw_thin_line(cv, x2, y2, x3, y3); |
|---|
| 68 | caca_draw_thin_line(cv, x3, y3, x1, y1); |
|---|
| 69 | |
|---|
| 70 | return 0; |
|---|
| 71 | } |
|---|
| 72 | |
|---|
| 73 | /** \brief Fill a triangle on the canvas using the given character. |
|---|
| 74 | * |
|---|
| 75 | * This function never fails. |
|---|
| 76 | * |
|---|
| 77 | * \param cv The handle to the libcaca canvas. |
|---|
| 78 | * \param x1 X coordinate of the first point. |
|---|
| 79 | * \param y1 Y coordinate of the first point. |
|---|
| 80 | * \param x2 X coordinate of the second point. |
|---|
| 81 | * \param y2 Y coordinate of the second point. |
|---|
| 82 | * \param x3 X coordinate of the third point. |
|---|
| 83 | * \param y3 Y coordinate of the third point. |
|---|
| 84 | * \param ch UTF-32 character to be used to fill the triangle. |
|---|
| 85 | * \return This function always returns 0. |
|---|
| 86 | */ |
|---|
| 87 | int caca_fill_triangle(caca_canvas_t * cv, int x1, int y1, int x2, int y2, |
|---|
| 88 | int x3, int y3, uint32_t ch) |
|---|
| 89 | { |
|---|
| 90 | int x, y, xmin, xmax, ymin, ymax; |
|---|
| 91 | int xx1, xx2, xa, xb, sl21, sl31, sl32; |
|---|
| 92 | |
|---|
| 93 | /* Bubble-sort y1 <= y2 <= y3 */ |
|---|
| 94 | if (y1 > y2) |
|---|
| 95 | return caca_fill_triangle(cv, x2, y2, x1, y1, x3, y3, ch); |
|---|
| 96 | |
|---|
| 97 | if (y2 > y3) |
|---|
| 98 | return caca_fill_triangle(cv, x1, y1, x3, y3, x2, y2, ch); |
|---|
| 99 | |
|---|
| 100 | /* Compute slopes and promote precision */ |
|---|
| 101 | sl21 = (y2 == y1) ? 0 : (x2 - x1) * 0x10000 / (y2 - y1); |
|---|
| 102 | sl31 = (y3 == y1) ? 0 : (x3 - x1) * 0x10000 / (y3 - y1); |
|---|
| 103 | sl32 = (y3 == y2) ? 0 : (x3 - x2) * 0x10000 / (y3 - y2); |
|---|
| 104 | |
|---|
| 105 | x1 *= 0x10000; |
|---|
| 106 | x2 *= 0x10000; |
|---|
| 107 | x3 *= 0x10000; |
|---|
| 108 | |
|---|
| 109 | ymin = y1 < 0 ? 0 : y1; |
|---|
| 110 | ymax = y3 + 1 < cv->height ? y3 + 1 : cv->height; |
|---|
| 111 | |
|---|
| 112 | if (ymin < y2) |
|---|
| 113 | { |
|---|
| 114 | xa = x1 + sl21 * (ymin - y1); |
|---|
| 115 | xb = x1 + sl31 * (ymin - y1); |
|---|
| 116 | } |
|---|
| 117 | else if (ymin == y2) |
|---|
| 118 | { |
|---|
| 119 | xa = x2; |
|---|
| 120 | xb = (y1 == y3) ? x3 : x1 + sl31 * (ymin - y1); |
|---|
| 121 | } |
|---|
| 122 | else /* (ymin > y2) */ |
|---|
| 123 | { |
|---|
| 124 | xa = x3 + sl32 * (ymin - y3); |
|---|
| 125 | xb = x3 + sl31 * (ymin - y3); |
|---|
| 126 | } |
|---|
| 127 | |
|---|
| 128 | /* Rasterize our triangle */ |
|---|
| 129 | for (y = ymin; y < ymax; y++) |
|---|
| 130 | { |
|---|
| 131 | /* Rescale xa and xb, recentering the division */ |
|---|
| 132 | if (xa < xb) |
|---|
| 133 | { |
|---|
| 134 | xx1 = (xa + 0x800) / 0x10000; |
|---|
| 135 | xx2 = (xb + 0x801) / 0x10000; |
|---|
| 136 | } |
|---|
| 137 | else |
|---|
| 138 | { |
|---|
| 139 | xx1 = (xb + 0x800) / 0x10000; |
|---|
| 140 | xx2 = (xa + 0x801) / 0x10000; |
|---|
| 141 | } |
|---|
| 142 | |
|---|
| 143 | xmin = xx1 < 0 ? 0 : xx1; |
|---|
| 144 | xmax = xx2 + 1 < cv->width ? xx2 + 1 : cv->width; |
|---|
| 145 | |
|---|
| 146 | for (x = xmin; x < xmax; x++) |
|---|
| 147 | caca_put_char(cv, x, y, ch); |
|---|
| 148 | |
|---|
| 149 | xa += y < y2 ? sl21 : sl32; |
|---|
| 150 | xb += sl31; |
|---|
| 151 | } |
|---|
| 152 | |
|---|
| 153 | return 0; |
|---|
| 154 | } |
|---|
| 155 | |
|---|
| 156 | /* This function actually renders the triangle, but is not exported due to |
|---|
| 157 | sam's pedantic will. */ |
|---|
| 158 | static int caca_fill_triangle_textured_l(caca_canvas_t * cv, |
|---|
| 159 | int x1, int y1, |
|---|
| 160 | int x2, int y2, |
|---|
| 161 | int x3, int y3, |
|---|
| 162 | caca_canvas_t * tex, |
|---|
| 163 | float u1, float v1, |
|---|
| 164 | float u2, float v2, |
|---|
| 165 | float u3, float v3) |
|---|
| 166 | { |
|---|
| 167 | uint32_t savedattr; |
|---|
| 168 | |
|---|
| 169 | #define SWAP_F(a, b) {float c = a; a = b; b = c; } |
|---|
| 170 | |
|---|
| 171 | /* (very) Naive and (very) float-based affine and (very) non-clipped and |
|---|
| 172 | (very) non-corrected triangle mapper Accepts arbitrary texture sizes |
|---|
| 173 | Coordinates clamped to [0.0 - 1.0] (no repeat) */ |
|---|
| 174 | if (!cv || !tex) |
|---|
| 175 | return -1; |
|---|
| 176 | |
|---|
| 177 | /* Bubble-sort y1 <= y2 <= y3 */ |
|---|
| 178 | if (y1 > y2) |
|---|
| 179 | return caca_fill_triangle_textured_l(cv, |
|---|
| 180 | x2, y2, x1, y1, x3, y3, |
|---|
| 181 | tex, u2, v2, u1, v1, u3, v3); |
|---|
| 182 | if (y2 > y3) |
|---|
| 183 | return caca_fill_triangle_textured_l(cv, |
|---|
| 184 | x1, y1, x3, y3, x2, y2, |
|---|
| 185 | tex, u1, v1, u3, v3, u2, v2); |
|---|
| 186 | |
|---|
| 187 | savedattr = caca_get_attr(cv, -1, -1); |
|---|
| 188 | |
|---|
| 189 | /* Clip texture coordinates */ |
|---|
| 190 | if (u1 < 0.0f) |
|---|
| 191 | u1 = 0.0f; |
|---|
| 192 | if (v1 < 0.0f) |
|---|
| 193 | v1 = 0.0f; |
|---|
| 194 | if (u2 < 0.0f) |
|---|
| 195 | u2 = 0.0f; |
|---|
| 196 | if (v2 < 0.0f) |
|---|
| 197 | v2 = 0.0f; |
|---|
| 198 | if (u3 < 0.0f) |
|---|
| 199 | u3 = 0.0f; |
|---|
| 200 | if (v3 < 0.0f) |
|---|
| 201 | v3 = 0.0f; |
|---|
| 202 | if (u1 > 1.0f) |
|---|
| 203 | u1 = 1.0f; |
|---|
| 204 | if (v1 > 1.0f) |
|---|
| 205 | v1 = 1.0f; |
|---|
| 206 | if (u2 > 1.0f) |
|---|
| 207 | u2 = 1.0f; |
|---|
| 208 | if (v2 > 1.0f) |
|---|
| 209 | v2 = 1.0f; |
|---|
| 210 | if (u3 > 1.0f) |
|---|
| 211 | u3 = 1.0f; |
|---|
| 212 | if (v3 > 1.0f) |
|---|
| 213 | v3 = 1.0f; |
|---|
| 214 | |
|---|
| 215 | /* Convert relative tex coordinates to absolute */ |
|---|
| 216 | int tw = caca_get_canvas_width(tex); |
|---|
| 217 | int th = caca_get_canvas_height(tex); |
|---|
| 218 | |
|---|
| 219 | u1 *= (float)tw; |
|---|
| 220 | u2 *= (float)tw; |
|---|
| 221 | u3 *= (float)tw; |
|---|
| 222 | v1 *= (float)th; |
|---|
| 223 | v2 *= (float)th; |
|---|
| 224 | v3 *= (float)th; |
|---|
| 225 | |
|---|
| 226 | int x, y; |
|---|
| 227 | float y2y1 = y2 - y1; |
|---|
| 228 | float y3y1 = y3 - y1; |
|---|
| 229 | float y3y2 = y3 - y2; |
|---|
| 230 | |
|---|
| 231 | /* Compute slopes, making sure we don't divide by zero */ |
|---|
| 232 | /* (in this case, we don't need the value anyway) */ |
|---|
| 233 | /* FIXME : only compute needed slopes */ |
|---|
| 234 | float sl12 = ((float)x2 - x1) / (y2y1 == 0 ? 1 : y2y1); |
|---|
| 235 | float sl13 = ((float)x3 - x1) / (y3y1 == 0 ? 1 : y3y1); |
|---|
| 236 | float sl23 = ((float)x3 - x2) / (y3y2 == 0 ? 1 : y3y2); |
|---|
| 237 | |
|---|
| 238 | float usl12 = (u2 - u1) / (y2y1 == 0 ? 1 : y2y1); |
|---|
| 239 | float usl13 = (u3 - u1) / (y3y1 == 0 ? 1 : y3y1); |
|---|
| 240 | float usl23 = (u3 - u2) / (y3y2 == 0 ? 1 : y3y2); |
|---|
| 241 | float vsl12 = (v2 - v1) / (y2y1 == 0 ? 1 : y2y1); |
|---|
| 242 | float vsl13 = (v3 - v1) / (y3y1 == 0 ? 1 : y3y1); |
|---|
| 243 | float vsl23 = (v3 - v2) / (y3y2 == 0 ? 1 : y3y2); |
|---|
| 244 | |
|---|
| 245 | float xa = (float)x1, xb = (float)x1; |
|---|
| 246 | float ua = u1, ub = u1; |
|---|
| 247 | float va = v1, vb = v1; |
|---|
| 248 | float u, v; |
|---|
| 249 | |
|---|
| 250 | int s = 0; |
|---|
| 251 | |
|---|
| 252 | /* Top */ |
|---|
| 253 | for (y = y1; y < y2; y++) |
|---|
| 254 | { |
|---|
| 255 | |
|---|
| 256 | if (xb < xa) |
|---|
| 257 | { |
|---|
| 258 | SWAP_F(xb, xa); |
|---|
| 259 | SWAP_F(sl13, sl12); |
|---|
| 260 | SWAP_F(ua, ub); |
|---|
| 261 | SWAP_F(va, vb); |
|---|
| 262 | SWAP_F(usl13, usl12); |
|---|
| 263 | SWAP_F(vsl13, vsl12); |
|---|
| 264 | s = 1; |
|---|
| 265 | } |
|---|
| 266 | |
|---|
| 267 | float tus = (ub - ua) / (xb - xa); |
|---|
| 268 | float tvs = (vb - va) / (xb - xa); |
|---|
| 269 | v = va; |
|---|
| 270 | u = ua; |
|---|
| 271 | |
|---|
| 272 | /* scanline */ |
|---|
| 273 | for (x = xa; x < xb; x++) |
|---|
| 274 | { |
|---|
| 275 | u += tus; |
|---|
| 276 | v += tvs; |
|---|
| 277 | /* FIXME: use caca_get_canvas_attrs / caca_get_canvas_chars */ |
|---|
| 278 | uint32_t attr = caca_get_attr(tex, u, v); |
|---|
| 279 | uint32_t c = caca_get_char(tex, u, v); |
|---|
| 280 | caca_set_attr(cv, attr); |
|---|
| 281 | caca_put_char(cv, x, y, c); |
|---|
| 282 | } |
|---|
| 283 | |
|---|
| 284 | xa += sl13; |
|---|
| 285 | xb += sl12; |
|---|
| 286 | |
|---|
| 287 | ua += usl13; |
|---|
| 288 | va += vsl13; |
|---|
| 289 | ub += usl12; |
|---|
| 290 | vb += vsl12; |
|---|
| 291 | } |
|---|
| 292 | |
|---|
| 293 | if (s) |
|---|
| 294 | { |
|---|
| 295 | SWAP_F(xb, xa); |
|---|
| 296 | SWAP_F(sl13, sl12); |
|---|
| 297 | SWAP_F(ua, ub); |
|---|
| 298 | SWAP_F(va, vb); |
|---|
| 299 | SWAP_F(usl13, usl12); |
|---|
| 300 | SWAP_F(vsl13, vsl12); |
|---|
| 301 | } |
|---|
| 302 | |
|---|
| 303 | |
|---|
| 304 | /* Bottom */ |
|---|
| 305 | xb = (float)x2; |
|---|
| 306 | |
|---|
| 307 | /* These variables are set by 'top' routine and are in an incorrect state |
|---|
| 308 | if we only draw the bottom part */ |
|---|
| 309 | if (y1 == y2) |
|---|
| 310 | { |
|---|
| 311 | ua = u1; |
|---|
| 312 | ub = u2; |
|---|
| 313 | va = v1; |
|---|
| 314 | vb = v2; |
|---|
| 315 | } |
|---|
| 316 | |
|---|
| 317 | for (y = y2; y < y3; y++) |
|---|
| 318 | { |
|---|
| 319 | if (xb <= xa) |
|---|
| 320 | { |
|---|
| 321 | SWAP_F(xb, xa); |
|---|
| 322 | SWAP_F(sl13, sl23); |
|---|
| 323 | SWAP_F(ua, ub); |
|---|
| 324 | SWAP_F(va, vb); |
|---|
| 325 | SWAP_F(usl13, usl23); |
|---|
| 326 | SWAP_F(vsl13, vsl23); |
|---|
| 327 | } |
|---|
| 328 | |
|---|
| 329 | float tus = (ub - ua) / ((float)xb - xa); |
|---|
| 330 | float tvs = (vb - va) / ((float)xb - xa); |
|---|
| 331 | u = ua; |
|---|
| 332 | v = va; |
|---|
| 333 | |
|---|
| 334 | /* scanline */ |
|---|
| 335 | for (x = xa; x < xb; x++) |
|---|
| 336 | { |
|---|
| 337 | u += tus; |
|---|
| 338 | v += tvs; |
|---|
| 339 | /* FIXME, can be heavily optimised */ |
|---|
| 340 | uint32_t attr = caca_get_attr(tex, u, v); |
|---|
| 341 | uint32_t c = caca_get_char(tex, u, v); |
|---|
| 342 | caca_set_attr(cv, attr); |
|---|
| 343 | caca_put_char(cv, x, y, c); |
|---|
| 344 | } |
|---|
| 345 | |
|---|
| 346 | xa += sl13; |
|---|
| 347 | xb += sl23; |
|---|
| 348 | |
|---|
| 349 | ua += usl13; |
|---|
| 350 | va += vsl13; |
|---|
| 351 | ub += usl23; |
|---|
| 352 | vb += vsl23; |
|---|
| 353 | } |
|---|
| 354 | |
|---|
| 355 | caca_set_attr(cv, savedattr); |
|---|
| 356 | |
|---|
| 357 | return 0; |
|---|
| 358 | } |
|---|
| 359 | |
|---|
| 360 | /** \brief Fill a triangle on the canvas using an arbitrary-sized texture. |
|---|
| 361 | * |
|---|
| 362 | * This function fails if one or both the canvas are missing |
|---|
| 363 | * |
|---|
| 364 | * \param cv The handle to the libcaca canvas. |
|---|
| 365 | * \param coords The coordinates of the triangle (3{x,y}) |
|---|
| 366 | * \param tex The handle of the canvas texture. |
|---|
| 367 | * \param uv The coordinates of the texture (3{u,v}) |
|---|
| 368 | * \return This function return 0 if ok, -1 if canvas or texture are missing. |
|---|
| 369 | */ |
|---|
| 370 | int caca_fill_triangle_textured(caca_canvas_t * cv, |
|---|
| 371 | int coords[6], |
|---|
| 372 | caca_canvas_t * tex, float uv[6]) |
|---|
| 373 | { |
|---|
| 374 | |
|---|
| 375 | return caca_fill_triangle_textured_l(cv, |
|---|
| 376 | coords[0], coords[1], |
|---|
| 377 | coords[2], coords[3], |
|---|
| 378 | coords[4], coords[5], |
|---|
| 379 | tex, |
|---|
| 380 | uv[0], uv[1], |
|---|
| 381 | uv[2], uv[3], uv[4], uv[5]); |
|---|
| 382 | } |
|---|
| 383 | |
|---|
| 384 | |
|---|
| 385 | |
|---|
| 386 | /* |
|---|
| 387 | * XXX: The following functions are aliases. |
|---|
| 388 | */ |
|---|
| 389 | |
|---|
| 390 | int cucul_draw_triangle(cucul_canvas_t *, int, int, int, int, int, |
|---|
| 391 | int, uint32_t) CACA_ALIAS(caca_draw_triangle); |
|---|
| 392 | int cucul_draw_thin_triangle(cucul_canvas_t *, int, int, int, int, |
|---|
| 393 | int, |
|---|
| 394 | int) CACA_ALIAS(caca_draw_thin_triangle); |
|---|
| 395 | int cucul_fill_triangle(cucul_canvas_t *, int, int, int, int, int, int, |
|---|
| 396 | uint32_t) CACA_ALIAS(caca_fill_triangle); |
|---|