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