source: libcaca/trunk/caca/line.c @ 2821

Last change on this file since 2821 was 2821, checked in by Sam Hocevar, 11 years ago

Starting refactoring to get rid of libcucul. The initial reason for the
split is rendered moot by the plugin system: when enabled, binaries do
not link directly with libX11 or libGL. I hope this is a step towards
more consisteny and clarity.

  • Property svn:keywords set to Id
File size: 9.2 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: line.c 2821 2008-09-27 13:12:46Z 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 line and polyline drawing functions, with both thin
17 *  and thick styles.
18 */
19
20#include "config.h"
21
22#if !defined(__KERNEL__)
23#   include <stdlib.h>
24#endif
25
26#include "caca.h"
27#include "caca_internals.h"
28
29#if !defined(_DOXYGEN_SKIP_ME)
30struct line
31{
32    int x1, y1;
33    int x2, y2;
34    uint32_t ch;
35    void (*draw) (caca_canvas_t *, struct line*);
36};
37#endif
38
39static void clip_line(caca_canvas_t*, struct line*);
40static uint8_t clip_bits(caca_canvas_t*, int, int);
41static void draw_solid_line(caca_canvas_t*, struct line*);
42static void draw_thin_line(caca_canvas_t*, struct line*);
43
44/** \brief Draw a line on the canvas using the given character.
45 *
46 *  This function never fails.
47 *
48 *  \param cv The handle to the libcaca canvas.
49 *  \param x1 X coordinate of the first point.
50 *  \param y1 Y coordinate of the first point.
51 *  \param x2 X coordinate of the second point.
52 *  \param y2 Y coordinate of the second point.
53 *  \param ch UTF-32 character to be used to draw the line.
54 *  \return This function always returns 0.
55 */
56int caca_draw_line(caca_canvas_t *cv, int x1, int y1, int x2, int y2,
57                    uint32_t ch)
58{
59    struct line s;
60    s.x1 = x1;
61    s.y1 = y1;
62    s.x2 = x2;
63    s.y2 = y2;
64    s.ch = ch;
65    s.draw = draw_solid_line;
66    clip_line(cv, &s);
67
68    return 0;
69}
70
71/** \brief Draw a polyline.
72 *
73 *  Draw a polyline on the canvas using the given character and coordinate
74 *  arrays. The first and last points are not connected, hence in order to
75 *  draw a polygon you need to specify the starting point at the end of the
76 *  list as well.
77 *
78 *  This function never fails.
79 *
80 *  \param cv The handle to the libcaca canvas.
81 *  \param x Array of X coordinates. Must have \p n + 1 elements.
82 *  \param y Array of Y coordinates. Must have \p n + 1 elements.
83 *  \param n Number of lines to draw.
84 *  \param ch UTF-32 character to be used to draw the lines.
85 *  \return This function always returns 0.
86 */
87int caca_draw_polyline(caca_canvas_t *cv, int const x[], int const y[],
88                        int n, uint32_t ch)
89{
90    int i;
91    struct line s;
92    s.ch = ch;
93    s.draw = draw_solid_line;
94
95    for(i = 0; i < n; i++)
96    {
97        s.x1 = x[i];
98        s.y1 = y[i];
99        s.x2 = x[i+1];
100        s.y2 = y[i+1];
101        clip_line(cv, &s);
102    }
103
104    return 0;
105}
106
107/** \brief Draw a thin line on the canvas, using ASCII art.
108 *
109 *  This function never fails.
110 *
111 *  \param cv The handle to the libcaca canvas.
112 *  \param x1 X coordinate of the first point.
113 *  \param y1 Y coordinate of the first point.
114 *  \param x2 X coordinate of the second point.
115 *  \param y2 Y coordinate of the second point.
116 *  \return This function always returns 0.
117 */
118int caca_draw_thin_line(caca_canvas_t *cv, int x1, int y1, int x2, int y2)
119{
120    struct line s;
121    s.x1 = x1;
122    s.y1 = y1;
123    s.x2 = x2;
124    s.y2 = y2;
125    s.draw = draw_thin_line;
126    clip_line(cv, &s);
127
128    return 0;
129}
130
131/** \brief Draw an ASCII art thin polyline.
132 *
133 *  Draw a thin polyline on the canvas using the given coordinate arrays and
134 *  with ASCII art. The first and last points are not connected, so in order
135 *  to draw a polygon you need to specify the starting point at the end of
136 *  the list as well.
137 *
138 *  This function never fails.
139 *
140 *  \param cv The handle to the libcaca canvas.
141 *  \param x Array of X coordinates. Must have \p n + 1 elements.
142 *  \param y Array of Y coordinates. Must have \p n + 1 elements.
143 *  \param n Number of lines to draw.
144 *  \return This function always returns 0.
145 */
146int caca_draw_thin_polyline(caca_canvas_t *cv, int const x[], int const y[],
147                             int n)
148{
149    int i;
150    struct line s;
151    s.draw = draw_thin_line;
152
153    for(i = 0; i < n; i++)
154    {
155        s.x1 = x[i];
156        s.y1 = y[i];
157        s.x2 = x[i+1];
158        s.y2 = y[i+1];
159        clip_line(cv, &s);
160    }
161
162    return 0;
163}
164
165/*
166 * XXX: The following functions are local.
167 */
168
169/* Generic Cohen-Sutherland line clipping function. */
170static void clip_line(caca_canvas_t *cv, struct line* s)
171{
172    uint8_t bits1, bits2;
173
174    bits1 = clip_bits(cv, s->x1, s->y1);
175    bits2 = clip_bits(cv, s->x2, s->y2);
176
177    if(bits1 & bits2)
178        return;
179
180    if(bits1 == 0)
181    {
182        if(bits2 == 0)
183            s->draw(cv, s);
184        else
185        {
186            int tmp;
187            tmp = s->x1; s->x1 = s->x2; s->x2 = tmp;
188            tmp = s->y1; s->y1 = s->y2; s->y2 = tmp;
189            clip_line(cv, s);
190        }
191
192        return;
193    }
194
195    if(bits1 & (1<<0))
196    {
197        s->y1 = s->y2 - (s->x2 - 0) * (s->y2 - s->y1) / (s->x2 - s->x1);
198        s->x1 = 0;
199    }
200    else if(bits1 & (1<<1))
201    {
202        int xmax = cv->width - 1;
203        s->y1 = s->y2 - (s->x2 - xmax) * (s->y2 - s->y1) / (s->x2 - s->x1);
204        s->x1 = xmax;
205    }
206    else if(bits1 & (1<<2))
207    {
208        s->x1 = s->x2 - (s->y2 - 0) * (s->x2 - s->x1) / (s->y2 - s->y1);
209        s->y1 = 0;
210    }
211    else if(bits1 & (1<<3))
212    {
213        int ymax = cv->height - 1;
214        s->x1 = s->x2 - (s->y2 - ymax) * (s->x2 - s->x1) / (s->y2 - s->y1);
215        s->y1 = ymax;
216    }
217
218    clip_line(cv, s);
219}
220
221/* Helper function for clip_line(). */
222static uint8_t clip_bits(caca_canvas_t *cv, int x, int y)
223{
224    uint8_t b = 0;
225
226    if(x < 0)
227        b |= (1<<0);
228    else if(x >= (int)cv->width)
229        b |= (1<<1);
230
231    if(y < 0)
232        b |= (1<<2);
233    else if(y >= (int)cv->height)
234        b |= (1<<3);
235
236    return b;
237}
238
239/* Solid line drawing function, using Bresenham's mid-point line
240 * scan-conversion algorithm. */
241static void draw_solid_line(caca_canvas_t *cv, struct line* s)
242{
243    int x1, y1, x2, y2;
244    int dx, dy;
245    int xinc, yinc;
246
247    x1 = s->x1; y1 = s->y1; x2 = s->x2; y2 = s->y2;
248
249    dx = abs(x2 - x1);
250    dy = abs(y2 - y1);
251
252    xinc = (x1 > x2) ? -1 : 1;
253    yinc = (y1 > y2) ? -1 : 1;
254
255    if(dx >= dy)
256    {
257        int dpr = dy << 1;
258        int dpru = dpr - (dx << 1);
259        int delta = dpr - dx;
260
261        for(; dx>=0; dx--)
262        {
263            caca_put_char(cv, x1, y1, s->ch);
264            if(delta > 0)
265            {
266                x1 += xinc;
267                y1 += yinc;
268                delta += dpru;
269            }
270            else
271            {
272                x1 += xinc;
273                delta += dpr;
274            }
275        }
276    }
277    else
278    {
279        int dpr = dx << 1;
280        int dpru = dpr - (dy << 1);
281        int delta = dpr - dy;
282
283        for(; dy >= 0; dy--)
284        {
285            caca_put_char(cv, x1, y1, s->ch);
286            if(delta > 0)
287            {
288                x1 += xinc;
289                y1 += yinc;
290                delta += dpru;
291            }
292            else
293            {
294                y1 += yinc;
295                delta += dpr;
296            }
297        }
298    }
299}
300
301/* Thin line drawing function, using Bresenham's mid-point line
302 * scan-conversion algorithm and ASCII art graphics. */
303static void draw_thin_line(caca_canvas_t *cv, struct line* s)
304{
305    uint32_t charmapx[2], charmapy[2];
306    int x1, y1, x2, y2;
307    int dx, dy;
308    int yinc;
309
310    if(s->x2 >= s->x1)
311    {
312        charmapx[0] = (s->y1 > s->y2) ? ',' : '`';
313        charmapx[1] = (s->y1 > s->y2) ? '\'' : '.';
314        x1 = s->x1; y1 = s->y1; x2 = s->x2; y2 = s->y2;
315    }
316    else
317    {
318        charmapx[0] = (s->y1 > s->y2) ? '`' : '.';
319        charmapx[1] = (s->y1 > s->y2) ? ',' : '\'';
320        x2 = s->x1; y2 = s->y1; x1 = s->x2; y1 = s->y2;
321    }
322
323    dx = abs(x2 - x1);
324    dy = abs(y2 - y1);
325
326    if(y1 > y2)
327    {
328        charmapy[0] = ',';
329        charmapy[1] = '\'';
330        yinc = -1;
331    }
332    else
333    {
334        yinc = 1;
335        charmapy[0] = '`';
336        charmapy[1] = '.';
337    }
338
339    if(dx >= dy)
340    {
341        int dpr = dy << 1;
342        int dpru = dpr - (dx << 1);
343        int delta = dpr - dx;
344        int prev = 0;
345
346        for(; dx>=0; dx--)
347        {
348            if(delta > 0)
349            {
350                caca_put_char(cv, x1, y1, charmapy[1]);
351                x1++;
352                y1 += yinc;
353                delta += dpru;
354                prev = 1;
355            }
356            else
357            {
358                if(prev)
359                    caca_put_char(cv, x1, y1, charmapy[0]);
360                else
361                    caca_put_char(cv, x1, y1, '-');
362                x1++;
363                delta += dpr;
364                prev = 0;
365            }
366        }
367    }
368    else
369    {
370        int dpr = dx << 1;
371        int dpru = dpr - (dy << 1);
372        int delta = dpr - dy;
373
374        for(; dy >= 0; dy--)
375        {
376            if(delta > 0)
377            {
378                caca_put_char(cv, x1, y1, charmapx[0]);
379                caca_put_char(cv, x1 + 1, y1, charmapx[1]);
380                x1++;
381                y1 += yinc;
382                delta += dpru;
383            }
384            else
385            {
386                caca_put_char(cv, x1, y1, '|');
387                y1 += yinc;
388                delta += dpr;
389            }
390        }
391    }
392}
393
Note: See TracBrowser for help on using the repository browser.