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