Include top-level headers from cfg/ subdir.
[bertos.git] / mware / gfx.c
1 /*!
2  * \file
3  * <!--
4  * Copyright 2003, 2004 Develer S.r.l. (http://www.develer.com/)
5  * Copyright 1999 Bernardo Innocenti <bernie@develer.com>
6  * This file is part of DevLib - See devlib/README 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 General pourpose graphics routines
15  */
16
17 /*#*
18  *#* $Log$
19  *#* Revision 1.13  2005/04/11 19:10:28  bernie
20  *#* Include top-level headers from cfg/ subdir.
21  *#*
22  *#* Revision 1.12  2005/03/01 23:26:45  bernie
23  *#* Use new CPU-neutral program-memory API.
24  *#*
25  *#* Revision 1.11  2004/12/08 08:06:16  bernie
26  *#* Remove done todo.
27  *#*
28  *#* Revision 1.10  2004/11/01 15:14:07  bernie
29  *#* Update to current coding conventions.
30  *#*
31  *#* Revision 1.9  2004/10/21 10:58:33  bernie
32  *#* Add a TODO item.
33  *#*
34  *#* Revision 1.8  2004/09/20 03:29:22  bernie
35  *#* Relax assertion.
36  *#*
37  *#* Revision 1.7  2004/09/14 21:01:08  bernie
38  *#* Rename rectangle drawing functions; Unify filled/cleared implementations.
39  *#*
40  *#* Revision 1.4  2004/08/24 16:53:10  bernie
41  *#* Use new-style config macros.
42  *#*
43  *#* Revision 1.3  2004/08/04 03:16:59  bernie
44  *#* Switch to new DevLib CONFIG_ convention.
45  *#*
46  *#* Revision 1.2  2004/06/03 11:27:09  bernie
47  *#* Add dual-license information.
48  *#*/
49
50 #include "gfx.h"
51 #include <cfg/config.h>  /* CONFIG_GFX_CLIPPING */
52 #include <cfg/debug.h>
53 #include <cfg/cpu.h>     /* CPU_HARVARD */
54 #include <cfg/macros.h>  /* SWAP() */
55
56 #include <string.h>
57
58
59 /*!
60  * Plot a point in bitmap \a bm.
61  *
62  * \note bm is evaluated twice
63  */
64 #define BM_PLOT(bm, x, y) \
65         ( *((bm)->raster + ((y) / 8) * (bm)->width + (x)) |= 1 << ((y) % 8) )
66
67 /*!
68  * Clear a point in bitmap \a bm.
69  *
70  * \note bm is evaluated twice
71  */
72 #define BM_CLEAR(bm, x, y) \
73         ( *((bm)->raster + ((y) / 8) * (bm)->width + (x)) &= ~(1 << ((y) % 8)) )
74
75 /*!
76  * Set a point in bitmap \a bm to the specified color.
77  *
78  * \note bm is evaluated twice
79  * \note This macro is somewhat slower than BM_PLOT and BM_CLEAR.
80  * \see BM_PLOT BM_CLEAR
81  */
82 #define BM_DRAWPIXEL(bm, x, y, fg_pen) \
83         do { \
84                 uint8_t *p = (bm)->raster + ((y) / 8) * (bm)->width + (x); \
85                 uint8_t mask = 1 << ((y) % 8); \
86                 *p = (*p & ~mask) | ((fg_pen) ? mask : 0); \
87         } while (0)
88
89 /*!
90  * Initialize a Bitmap structure with the provided parameters.
91  *
92  * \note The pen position is reset to the origin.
93  */
94 void gfx_bitmapInit(Bitmap *bm, uint8_t *raster, coord_t w, coord_t h)
95 {
96         bm->raster = raster;
97         bm->width = w;
98         bm->height = h;
99         bm->penX = 0;
100         bm->penY = 0;
101 }
102
103
104 /*!
105  * Clear the whole bitmap surface to the background color.
106  *
107  * \note This function does \b not update the current pen position
108  */
109 void gfx_bitmapClear(Bitmap *bm)
110 {
111         memset(bm->raster, 0, (bm->width * bm->height) / 8);
112 }
113
114
115 #if CPU_HARVARD
116
117 #include <avr/pgmspace.h> /* FIXME: memcpy_P() */
118
119 /*!
120  * Copy a raster picture located in program memory in the bitmap.
121  * The size of the raster to copy *must* be the same of the raster bitmap.
122  *
123  * \note This function does \b not update the current pen position
124  */
125 void gfx_blit_P(Bitmap *bm, const pgm_uint8_t *raster)
126 {
127         memcpy_P(bm->raster, raster, (bm->height / 8) * bm->width);
128 }
129 #endif /* CPU_HARVARD */
130
131
132 /*!
133  * Draw a line on the bitmap \a bm using the specified start and end
134  * coordinates.
135  *
136  * \note This function does \b not update the current pen position.
137  *
138  * \todo Optimize for vertical and horiziontal lines.
139  */
140 void gfx_line(Bitmap *bm, coord_t x1, coord_t y1, coord_t x2, coord_t y2)
141 {
142         int x, y, e, len, adx, ady, signx, signy;
143
144
145 #if CONFIG_GFX_CLIPPING
146         /* FIXME: broken */
147
148         #define XMIN 0
149         #define YMIN 0
150         #define XMAX (bm->width - 1)
151         #define YMAX (bm->height - 1)
152
153         /* Clipping */
154         if (x1 < XMIN)
155         {
156                 y1 = y2 - ((x2 - XMIN) * (y2 - y1)) / (x2 - x1);
157                 x1 = XMIN;
158         }
159         if (y1 < YMIN)
160         {
161                 x1 = x2 - ((y2 - YMIN) * (x2 - x1)) / (y2 - y1);
162                 y1 = YMIN;
163         }
164         if (x2 < XMIN)
165         {
166                 y2 = y2 - ((XMIN - x1) * (y2 - y1)) / (x2 - x1);
167                 x2 = XMIN;
168         }
169         if (y2 < YMIN)
170         {
171                 x2 = x2 - ((YMIN - y1) * (x2 - x1)) / (y2 - y1);
172                 y2 = YMIN;
173         }
174
175         if (x1 > XMAX)
176         {
177                 y1 = ((x2 - XMAX) * (y2 - y1)) / (x2 - x1);
178                 x1 = XMAX;
179         }
180         if (y1 > YMAX)
181         {
182                 x1 = ((y2 - YMAX) * (x2 - x1)) / (y2 - y1);
183                 y1 = YMAX;
184         }
185         if (x2 > XMAX)
186         {
187                 y2 = ((XMAX - x1) * (y2 - y1)) / (x2 - x1);
188                 x2 = XMAX;
189         }
190         if (y2 > YMAX)
191         {
192                 x2 = ((YMAX - y1) * (x2 - x1)) / (y2 - y1);
193                 y2 = YMAX;
194         }
195
196         #undef XMIN
197         #undef YMIN
198         #undef XMAX
199         #undef YMAX
200
201 #endif /* CONFIG_GFX_CLIPPING */
202
203
204         if (x2 > x1)
205         {
206                 /* left to right */
207                 signx = +1;
208                 adx = x2 - x1;
209         }
210         else
211         {
212                 /* right to left */
213                 signx = -1;
214                 adx = x1 - x2;
215         }
216
217         if (y2 > y1)
218         {
219                 /* top to bottom */
220                 signy = +1;
221                 ady = y2 - y1;
222         }
223         else
224         {
225                 /* bottom to top */
226                 signy = -1;
227                 ady = y1 - y2;
228         }
229
230         x = x1;
231         y = y1;
232
233         if (adx > ady)
234         {
235                 /* X-major line (octants 1/4/5/8) */
236
237                 len = adx;
238                 e = -adx;
239                 while (len--)
240                 {
241                         /* Sanity check */
242                         if ((x >= 0) && (x < bm->width) && (y >= 0) && (y < bm->height))
243                                 BM_PLOT(bm, x, y);
244                         x += signx;
245                         e += ady;
246                         if (e >= 0)
247                         {
248                                 y += signy;
249                                 e -= adx;
250                         }
251                 }
252         }
253         else
254         {
255                 /* Y-major line (octants 2/3/6/7) */
256
257                 len = ady;
258                 e = -ady;
259                 while (len--)
260                 {
261                         /* Sanity check */
262                         if ((x >= 0) && (x < bm->width) && (y >= 0) && (y < bm->height))
263                                 BM_PLOT(bm, x, y);
264                         y += signy;
265                         e += adx;
266                         if (e >= 0)
267                         {
268                                 x += signx;
269                                 e -= ady;
270                         }
271                 }
272         }
273 }
274
275
276 /*!
277  * Move the current pen position to the specified coordinates.
278  */
279 void gfx_moveTo(Bitmap *bm, coord_t x, coord_t y)
280 {
281         bm->penX = x;
282         bm->penY = y;
283 }
284
285 /*!
286  * Draw a line from the current pen position to the new coordinates.
287  *
288  * \note This function moves the current pen position to the
289  *       new coordinates.
290  */
291 void gfx_lineTo(Bitmap *bm, coord_t x, coord_t y)
292 {
293         gfx_line(bm, bm->penX, bm->penY, x, y);
294         gfx_moveTo(bm, x, y);
295 }
296
297
298 /*!
299  * Draw an the outline of an hollow rectangle.
300  *
301  * \note The bottom-right border of the rectangle is not drawn.
302  * \note This function does \b not update the current pen position
303  */
304 void gfx_rectDraw(Bitmap *bm, coord_t x1, coord_t y1, coord_t x2, coord_t y2)
305 {
306         /* Sort coords (needed for correct bottom-right semantics) */
307         if (x1 > x2) SWAP(x1, x2);
308         if (y1 > y2) SWAP(y1, y2);
309
310         /* Draw rectangle */
311         gfx_line(bm, x1,   y1,   x2-1, y1);
312         gfx_line(bm, x2-1, y1,   x2-1, y2-1);
313         gfx_line(bm, x2-1, y2-1, x1,   y2-1);
314         gfx_line(bm, x1,   y2-1, x1,   y1);
315 }
316
317
318 /*!
319  * Fill a rectangular area with \a color.
320  *
321  * \note The bottom-right border of the rectangle is not drawn.
322  *
323  * \note This function does \b not update the current pen position
324  */
325 void gfx_rectFillC(Bitmap *bm, coord_t x1, coord_t y1, coord_t x2, coord_t y2, uint8_t color)
326 {
327         coord_t x, y;
328
329         /* Sort coords */
330         if (x1 > x2) SWAP(x1, x2);
331         if (y1 > y2) SWAP(y1, y2);
332
333         /* Clip rect to bitmap bounds */
334         if (x1 < 0)             x1 = 0;
335         if (x2 < 0)             x2 = 0;
336         if (x1 > bm->width)     x1 = bm->width;
337         if (x2 > bm->width)     x2 = bm->width;
338         if (y1 < 0)             y1 = 0;
339         if (y2 < 0)             y2 = 0;
340         if (y1 > bm->width)     y1 = bm->width;
341         if (y2 > bm->width)     y2 = bm->width;
342
343         /*
344          * Draw rectangle
345          * NOTE: Code paths are duplicated for efficiency
346          */
347         if (color) /* fill */
348         {
349                 for (x = x1; x < x2; x++)
350                         for (y = y1; y < y2; y++)
351                                 BM_PLOT(bm, x, y);
352         }
353         else /* clear */
354         {
355                 for (x = x1; x < x2; x++)
356                         for (y = y1; y < y2; y++)
357                                 BM_CLEAR(bm, x, y);
358         }
359 }
360
361
362 /*!
363  * Draw a filled rectangle.
364  *
365  * \note The bottom-right border of the rectangle is not drawn.
366  *
367  * \note This function does \b not update the current pen position
368  */
369 void gfx_rectFill(Bitmap *bm, coord_t x1, coord_t y1, coord_t x2, coord_t y2)
370 {
371         gfx_rectFillC(bm, x1, y1, x2, y2, 0xFF);
372 }
373
374
375 /*!
376  * Clear a rectangular area.
377  *
378  * \note The bottom-right border of the rectangle is not drawn.
379  *
380  * \note This function does \b not update the current pen position
381  */
382 void gfx_rectClear(Bitmap *bm, coord_t x1, coord_t y1, coord_t x2, coord_t y2)
383 {
384         gfx_rectFillC(bm, x1, y1, x2, y2, 0x00);
385 }
386
387
388 /*!
389  * Imposta un rettangolo di clipping per il disegno nella bitmap.
390  */
391 void gfx_setClipRect(Bitmap *bm, coord_t minx, coord_t miny, coord_t maxx, coord_t maxy)
392 {
393         ASSERT(minx < maxx);
394         ASSERT(miny < maxy);
395         ASSERT(miny >= 0);
396         ASSERT(minx >= 0);
397         ASSERT(maxx <= bm->width);
398         ASSERT(maxy <= bm->height);
399
400         bm->cr.xmin = minx;
401         bm->cr.ymin = miny;
402         bm->cr.xmax = maxx;
403         bm->cr.ymax = maxy;
404
405 /*      DB(kprintf("cr.xmin = %d, cr.ymin = %d, cr.xmax = %d, cr.ymax = %d\n",
406                 bm->cr.xMin, bm->cr.ymin, bm->cr.xmax, bm->cr.ymax);)
407 */
408 }
409
410
411 #if CONFIG_GFX_VCOORDS
412 /*!
413  * Imposta gli estremi del sistema di coordinate cartesiane rispetto
414  * al rettangolo di clipping della bitmap.
415  */
416 void gfx_setViewRect(Bitmap *bm, vcoord_t x1, vcoord_t y1, vcoord_t x2, vcoord_t y2)
417 {
418         ASSERT(x1 != x2);
419         ASSERT(y1 != y2);
420
421         bm->orgX    = x1;
422         bm->orgY    = y1;
423         bm->scaleX  = (vcoord_t)(bm->cr.xmax - bm->cr.xmin - 1) / (vcoord_t)(x2 - x1);
424         bm->scaleY  = (vcoord_t)(bm->cr.ymax - bm->cr.ymin - 1) / (vcoord_t)(y2 - y1);
425
426 /*      DB(kprintf("orgX = %f, orgY = %f, scaleX = %f, scaleY = %f\n",
427                 bm->orgX, bm->orgY, bm->scaleX, bm->scaleY);)
428 */
429 }
430
431
432 /*!
433  * Transform a coordinate from the current reference system to a
434  * pixel offset within the bitmap.
435  */
436 coord_t gfx_transformX(Bitmap *bm, vcoord_t x)
437 {
438         return bm->cr.xmin + (coord_t)((x - bm->orgX) * bm->scaleX);
439 }
440
441 /*!
442  * Transform a coordinate from the current reference system to a
443  * pixel offset within the bitmap.
444  */
445 coord_t gfx_transformY(Bitmap *bm, vcoord_t y)
446 {
447         return bm->cr.ymin + (coord_t)((y - bm->orgY) * bm->scaleY);
448 }
449
450
451 /*!
452  * Draw a line from (x1;y1) to (x2;y2).
453  */
454 void gfx_vline(Bitmap *bm, vcoord_t x1, vcoord_t y1, vcoord_t x2, vcoord_t y2)
455 {
456         gfx_line(bm,
457                 gfx_transformX(bm, x1), gfx_transformY(bm, y1),
458                 gfx_transformY(bm, x2), gfx_transformY(bm, y2));
459 }
460 #endif /* CONFIG_GFX_VCOORDS */