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