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