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