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