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