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