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