Split out gfx.c into bitmap.c and line.c.
[bertos.git] / gfx / line.c
1 /*!
2  * \file
3  * <!--
4  * Copyright 2003, 2004, 2005 Develer S.r.l. (http://www.develer.com/)
5  * Copyright 1999 Bernardo Innocenti <bernie@develer.com>
6  * This file is part of DevLib - See README.devlib for information.
7  * -->
8  *
9  * \version $Id$
10  *
11  * \author Bernardo Innocenti <bernie@develer.com>
12  * \author Stefano Fedrigo <aleph@develer.com>
13  *
14  * \brief Line drawing graphics routines
15  */
16
17 /*#*
18  *#* $Log$
19  *#* Revision 1.1  2006/01/24 02:17:49  bernie
20  *#* Split out gfx.c into bitmap.c and line.c.
21  *#*
22  *#*/
23
24 #include "gfx.h"
25 #include "gfx_p.h"
26
27 #include <cfg/debug.h>   /* ASSERT() */
28 #include <cfg/cpu.h>     /* CPU_HARVARD */
29 #include <cfg/macros.h>  /* SWAP() */
30 #include <appconfig.h>   /* CONFIG_GFX_CLIPPING */
31
32 /*!
33  * Draw a sloped line without performing clipping.
34  *
35  * Parameters are the same of gfx_line().
36  * This routine is based on the Bresenham Line-Drawing Algorithm.
37  *
38  * \note Passing coordinates outside the bitmap boundaries will
39  *       result in memory trashing.
40  *
41  * \todo Optimize for vertical and horiziontal lines.
42  *
43  * \sa gfx_line()
44  */
45 static void gfx_lineUnclipped(Bitmap *bm, coord_t x1, coord_t y1, coord_t x2, coord_t y2)
46 {
47         int x, y, e, len, adx, ady, signx, signy;
48
49         if (x2 > x1)
50         {
51                 /* left to right */
52                 signx = +1;
53                 adx = x2 - x1;
54         }
55         else
56         {
57                 /* right to left */
58                 signx = -1;
59                 adx = x1 - x2;
60         }
61
62         if (y2 > y1)
63         {
64                 /* top to bottom */
65                 signy = +1;
66                 ady = y2 - y1;
67         }
68         else
69         {
70                 /* bottom to top */
71                 signy = -1;
72                 ady = y1 - y2;
73         }
74
75         x = x1;
76         y = y1;
77
78         if (adx > ady)
79         {
80                 /* X-major line (octants 1/4/5/8) */
81
82                 len = adx;
83                 e = -adx;
84                 while (len--)
85                 {
86                         /* Sanity check */
87                         ASSERT((x >= 0) && (x < bm->width) && (y >= 0) && (y < bm->height));
88                         BM_PLOT(bm, x, y);
89                         x += signx;
90                         e += ady;
91                         if (e >= 0)
92                         {
93                                 y += signy;
94                                 e -= adx;
95                         }
96                 }
97         }
98         else
99         {
100                 /* Y-major line (octants 2/3/6/7) */
101
102                 len = ady;
103                 e = -ady;
104                 while (len--)
105                 {
106                         /* Sanity check */
107                         ASSERT ((x >= 0) && (x < bm->width) && (y >= 0) && (y < bm->height));
108                         BM_PLOT(bm, x, y);
109                         y += signy;
110                         e += adx;
111                         if (e >= 0)
112                         {
113                                 x += signx;
114                                 e -= ady;
115                         }
116                 }
117         }
118 }
119
120 //! Helper routine for gfx_line().
121 static int gfx_findRegion(int x, int y, Rect *cr)
122 {
123         int code = 0;
124
125         if (y >= cr->ymax)
126                 code |= 1; /* below */
127         else if (y < cr->ymin)
128                 code |= 2; /* above */
129
130         if (x >= cr->xmax)
131                 code |= 4; /* right */
132         else if (x < cr->xmin)
133                 code |= 8; /* left */
134
135         return code;
136 }
137
138 /**
139  * Draw a sloped line segment.
140  *
141  * Draw a sloped line segment identified by the provided
142  * start and end coordinates on the bitmap \a bm.
143  *
144  * The line endpoints are clipped inside the current bitmap
145  * clipping rectangle using the Cohen-Sutherland algorithm,
146  * which is very fast.
147  *
148  * \note The point at coordinates \a x2 \a y2 is not drawn.
149  *
150  * \note This function does \b not update the current pen position.
151  *
152  * \todo Compute updated Bresenham error term.
153  */
154 void gfx_line(Bitmap *bm, coord_t x1, coord_t y1, coord_t x2, coord_t y2)
155 {
156 #if CONFIG_GFX_CLIPPING
157         int clip1 = gfx_findRegion(x1, y1, &bm->cr);
158         int clip2 = gfx_findRegion(x2, y2, &bm->cr);
159
160         /* Loop while there is at least one point outside */
161         while (clip1 | clip2)
162         {
163                 /* Check for line totally outside */
164                 if (clip1 & clip2)
165                         return;
166
167                 int c = clip1 ? clip1 : clip2;
168                 int x, y;
169
170                 if (c & 1) /* Below */
171                 {
172                         x = x1 + (x2 - x1) * (bm->cr.ymax - y1) / (y2 - y1);
173                         y = bm->cr.ymax - 1;
174                 }
175                 else if (c & 2) /* Above */
176                 {
177                         x = x1 + (x2 - x1) * (bm->cr.ymin - y1) / (y2 - y1);
178                         y = bm->cr.ymin;
179                 }
180                 else if (c & 4) /* Right */
181                 {
182                         y = y1 + (y2 - y1) * (bm->cr.xmax - x1) / (x2 - x1);
183                         x = bm->cr.xmax - 1;
184                 }
185                 else /* Left */
186                 {
187                         y = y1 + (y2 - y1) * (bm->cr.xmin - x1) / (x2 - x1);
188                         x = bm->cr.xmin;
189                 }
190
191                 if (c == clip1) /* First endpoint was clipped */
192                 {
193                         // TODO: adjust Bresenham error term
194                         //coord_t clipdx = ABS(x - x1);
195                         //coord_t clipdy = ABS(y - y1);
196                         //e += (clipdy * e2) + ((clipdx - clipdy) * e1);
197
198                         x1 = x;
199                         y1 = y;
200                         clip1 = gfx_findRegion(x1, y1, &bm->cr);
201                 }
202                 else /* Second endpoint was clipped */
203                 {
204                         x2 = x;
205                         y2 = y;
206                         clip2 = gfx_findRegion(x2, y2, &bm->cr);
207                 }
208         }
209 #endif /* CONFIG_GFX_CLIPPING */
210
211         gfx_lineUnclipped(bm, x1, y1, x2, y2);
212 }
213
214 /*!
215  * Move the current pen position to the specified coordinates.
216  *
217  * The pen position is used for drawing operations such as
218  * gfx_lineTo(), which can be used to draw polygons.
219  */
220 void gfx_moveTo(Bitmap *bm, coord_t x, coord_t y)
221 {
222         bm->penX = x;
223         bm->penY = y;
224 }
225
226 /*!
227  * Draw a line from the current pen position to the new coordinates.
228  *
229  * \note This function moves the current pen position to the
230  *       new coordinates.
231  *
232  * \sa gfx_line()
233  */
234 void gfx_lineTo(Bitmap *bm, coord_t x, coord_t y)
235 {
236         gfx_line(bm, bm->penX, bm->penY, x, y);
237         gfx_moveTo(bm, x, y);
238 }
239
240
241 /*!
242  * Draw the perimeter of an hollow rectangle.
243  *
244  * \note The bottom-right corner of the rectangle is drawn at (x2-1;y2-1).
245  * \note This function does \b not update the current pen position.
246  */
247 void gfx_rectDraw(Bitmap *bm, coord_t x1, coord_t y1, coord_t x2, coord_t y2)
248 {
249         /* Sort coords (needed for correct bottom-right semantics) */
250         if (x1 > x2) SWAP(x1, x2);
251         if (y1 > y2) SWAP(y1, y2);
252
253         /* Draw rectangle */
254         gfx_line(bm, x1,   y1,   x2-1, y1);
255         gfx_line(bm, x2-1, y1,   x2-1, y2-1);
256         gfx_line(bm, x2-1, y2-1, x1,   y2-1);
257         gfx_line(bm, x1,   y2-1, x1,   y1);
258 }
259
260
261 /*!
262  * Fill a rectangular area with \a color.
263  *
264  * \note The bottom-right border of the rectangle is not drawn.
265  *
266  * \note This function does \b not update the current pen position.
267  */
268 void gfx_rectFillC(Bitmap *bm, coord_t x1, coord_t y1, coord_t x2, coord_t y2, uint8_t color)
269 {
270         coord_t x, y;
271
272         /* Sort coords */
273         if (x1 > x2) SWAP(x1, x2);
274         if (y1 > y2) SWAP(y1, y2);
275
276 #if CONFIG_GFX_CLIPPING
277         /* Clip rect to bitmap clip region */
278         if (x1 < bm->cr.xmin)   x1 = bm->cr.xmin;
279         if (x2 < bm->cr.xmin)   x2 = bm->cr.xmin;
280         if (x1 > bm->cr.xmax)   x1 = bm->cr.xmax;
281         if (x2 > bm->cr.xmax)   x2 = bm->cr.xmax;
282         if (y1 < bm->cr.ymin)   y1 = bm->cr.ymin;
283         if (y2 < bm->cr.ymin)   y2 = bm->cr.ymin;
284         if (y1 > bm->cr.ymax)   y1 = bm->cr.ymax;
285         if (y2 > bm->cr.ymax)   y2 = bm->cr.ymax;
286 #endif
287
288         /* NOTE: Code paths are duplicated for efficiency */
289         if (color) /* fill */
290         {
291                 for (x = x1; x < x2; x++)
292                         for (y = y1; y < y2; y++)
293                                 BM_PLOT(bm, x, y);
294         }
295         else /* clear */
296         {
297                 for (x = x1; x < x2; x++)
298                         for (y = y1; y < y2; y++)
299                                 BM_CLEAR(bm, x, y);
300         }
301 }
302
303
304 /*!
305  * Draw a filled rectangle.
306  *
307  * \note The bottom-right border of the rectangle is not drawn.
308  *
309  * \note This function does \b not update the current pen position.
310  */
311 void gfx_rectFill(Bitmap *bm, coord_t x1, coord_t y1, coord_t x2, coord_t y2)
312 {
313         gfx_rectFillC(bm, x1, y1, x2, y2, 0xFF);
314 }
315
316
317 /*!
318  * Clear a rectangular area.
319  *
320  * \note The bottom-right border of the rectangle is not cleared.
321  *
322  * \note This function does \b not update the current pen position.
323  */
324 void gfx_rectClear(Bitmap *bm, coord_t x1, coord_t y1, coord_t x2, coord_t y2)
325 {
326         gfx_rectFillC(bm, x1, y1, x2, y2, 0x00);
327 }
328
329
330 #if CONFIG_GFX_VCOORDS
331 /*!
332  * Imposta gli estremi del sistema di coordinate cartesiane rispetto
333  * al rettangolo di clipping della bitmap.
334  */
335 void gfx_setViewRect(Bitmap *bm, vcoord_t x1, vcoord_t y1, vcoord_t x2, vcoord_t y2)
336 {
337         ASSERT(x1 != x2);
338         ASSERT(y1 != y2);
339
340         bm->orgX    = x1;
341         bm->orgY    = y1;
342         bm->scaleX  = (vcoord_t)(bm->cr.xmax - bm->cr.xmin - 1) / (vcoord_t)(x2 - x1);
343         bm->scaleY  = (vcoord_t)(bm->cr.ymax - bm->cr.ymin - 1) / (vcoord_t)(y2 - y1);
344
345 /*      DB(kprintf("orgX = %f, orgY = %f, scaleX = %f, scaleY = %f\n",
346                 bm->orgX, bm->orgY, bm->scaleX, bm->scaleY);)
347 */
348 }
349
350
351 /*!
352  * Transform a coordinate from the current reference system to a
353  * pixel offset within the bitmap.
354  */
355 coord_t gfx_transformX(Bitmap *bm, vcoord_t x)
356 {
357         return bm->cr.xmin + (coord_t)((x - bm->orgX) * bm->scaleX);
358 }
359
360 /*!
361  * Transform a coordinate from the current reference system to a
362  * pixel offset within the bitmap.
363  */
364 coord_t gfx_transformY(Bitmap *bm, vcoord_t y)
365 {
366         return bm->cr.ymin + (coord_t)((y - bm->orgY) * bm->scaleY);
367 }
368
369
370 /*!
371  * Draw a line from (x1;y1) to (x2;y2).
372  */
373 void gfx_vline(Bitmap *bm, vcoord_t x1, vcoord_t y1, vcoord_t x2, vcoord_t y2)
374 {
375         gfx_line(bm,
376                 gfx_transformX(bm, x1), gfx_transformY(bm, y1),
377                 gfx_transformY(bm, x2), gfx_transformY(bm, y2));
378 }
379 #endif /* CONFIG_GFX_VCOORDS */