source: libcaca/trunk/caca/triangle.c @ 3925

Last change on this file since 3925 was 3925, checked in by Sam Hocevar, 10 years ago

Preserve default attribute across caca_fill_triangle_texture calls.

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