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