f296be78d3ae11560e0f1fbbf43489ba39ae9ee3
[bertos.git] / gfx / text.c
1 /**
2  * \file
3  * <!--
4  * Copyright 2003, 2004, 2005 Develer S.r.l. (http://www.develer.com/)
5  * Copyright 1999 Bernardo Innocenti <bernie@develer.com>
6  * This file is part of DevLib - See README.devlib for information.
7  * -->
8  *
9  * \brief Text graphic routines
10  *
11  * \version $Id$
12  * \author Bernardo Innocenti <bernie@develer.com>
13  * \author Stefano Fedrigo <aleph@develer.com>
14  */
15
16 /*#*
17  *#* $Log$
18  *#* Revision 1.13  2006/07/19 12:56:26  bernie
19  *#* Convert to new Doxygen style.
20  *#*
21  *#* Revision 1.12  2006/05/25 23:35:22  bernie
22  *#* Implement correct and faster clipping for algo text.
23  *#*
24  *#* Revision 1.11  2006/05/15 07:21:06  bernie
25  *#* Doxygen fix.
26  *#*
27  *#* Revision 1.10  2006/04/27 05:39:23  bernie
28  *#* Enhance text rendering to arbitrary x,y coords.
29  *#*
30  *#* Revision 1.9  2006/04/11 00:08:24  bernie
31  *#* text_offset(): New function, but I'm not quite confident with the design.
32  *#*
33  *#* Revision 1.8  2006/03/22 09:50:37  bernie
34  *#* Use the same format for fonts and rasters.
35  *#*
36  *#* Revision 1.7  2006/03/20 17:51:55  bernie
37  *#* Cleanups.
38  *#*
39  *#* Revision 1.6  2006/03/13 02:05:54  bernie
40  *#* Mark slow paths as UNLIKELY.
41  *#*
42  *#* Revision 1.5  2006/03/07 22:18:04  bernie
43  *#* Correctly compute text width for prop fonts; Make styles a per-bitmap attribute.
44  *#*
45  *#* Revision 1.4  2006/02/15 09:10:15  bernie
46  *#* Implement prop fonts; Fix algo styles.
47  *#*
48  *#* Revision 1.3  2006/02/10 12:31:55  bernie
49  *#* Add multiple font support in bitmaps.
50  *#*
51  *#* Revision 1.2  2005/11/04 18:17:45  bernie
52  *#* Fix header guards and includes for new location of gfx module.
53  *#*
54  *#* Revision 1.1  2005/11/04 18:11:35  bernie
55  *#* Move graphics stuff from mware/ to gfx/.
56  *#*
57  *#* Revision 1.13  2005/11/04 16:20:02  bernie
58  *#* Fix reference to README.devlib in header.
59  *#*
60  *#* Revision 1.12  2005/04/11 19:10:28  bernie
61  *#* Include top-level headers from cfg/ subdir.
62  *#*
63  *#* Revision 1.11  2005/01/20 18:46:31  aleph
64  *#* Fix progmem includes.
65  *#*
66  *#* Revision 1.10  2005/01/08 09:20:12  bernie
67  *#* Really make it work on both architectures.
68  *#*
69  *#* Revision 1.9  2004/12/31 16:44:29  bernie
70  *#* Sanitize for non-Harvard processors.
71  *#*
72  *#* Revision 1.8  2004/11/16 21:16:28  bernie
73  *#* Update to new naming scheme in mware/gfx.c.
74  *#*
75  *#* Revision 1.7  2004/09/20 03:28:28  bernie
76  *#* Fix header.
77  *#*
78  *#* Revision 1.6  2004/09/14 20:57:15  bernie
79  *#* Use debug.h instead of kdebug.h.
80  *#*
81  *#* Revision 1.5  2004/09/06 21:51:26  bernie
82  *#* Extend interface to allow any algorithmic style.
83  *#*
84  *#* Revision 1.2  2004/06/03 11:27:09  bernie
85  *#* Add dual-license information.
86  *#*
87  *#* Revision 1.1  2004/05/23 15:43:16  bernie
88  *#* Import mware modules.
89  *#*
90  *#* Revision 1.17  2004/05/15 16:57:01  aleph
91  *#* Fixes for non-DEBUG build
92  *#*
93  *#* Revision 1.16  2004/04/03 20:42:49  aleph
94  *#* Add text_clear()
95  *#*
96  *#* Revision 1.15  2004/03/24 15:03:45  bernie
97  *#* Use explicit include paths; clean Doxygen comments
98  *#*
99  *#* Revision 1.14  2004/03/19 16:52:28  bernie
100  *#* Move printf() like functions from text.c to text_format.c and add PROGMEM versions.
101  *#*
102  *#* Revision 1.13  2004/03/17 18:23:32  bernie
103  *#* Oops.
104  *#*
105  *#* Revision 1.12  2004/03/17 18:03:22  bernie
106  *#* Make diagnostic message shorter
107  *#*
108  *#* Revision 1.11  2004/03/13 22:52:54  aleph
109  *#* documentation fixes
110  *#*/
111
112 #include <gfx/gfx.h>
113 #include <gfx/font.h>
114 #include <gfx/text.h>
115 #include <gfx/text.h>
116
117 #include <gfx/gfx_p.h> // FIXME: BM_DRAWPIXEL
118
119 #include <cfg/debug.h>
120
121
122 /**
123  * ANSI escape sequences flag: true for ESC state on.
124  *
125  * \todo Move to Bitmap.flags.
126  */
127 static bool ansi_mode = false;
128
129 /**
130  * Move (imaginary) cursor to coordinates specified.
131  */
132 void text_setCoord(struct Bitmap *bm, int x, int y)
133 {
134         bm->penX = x;
135         bm->penY = y;
136 }
137
138
139 /**
140  * Move (imaginary) cursor to column and row specified.
141  * Next text write will start a that row and col.
142  */
143 void text_moveTo(struct Bitmap *bm, int row, int col)
144 {
145         ASSERT(col >= 0);
146         ASSERT(col < bm->width / bm->font->width);
147         ASSERT(row >= 0);
148         ASSERT(row < bm->height / bm->font->height);
149
150         text_setCoord(bm, col * bm->font->width, row * bm->font->height);
151 }
152
153
154 /**
155  * Render char \a c on Bitmap \a bm.
156  */
157 static int text_putglyph(char c, struct Bitmap *bm)
158 {
159         const uint8_t * PROGMEM glyph;  /* font is in progmem */
160         uint8_t glyph_width, glyph_height, glyph_height_bytes;
161         unsigned char index = (unsigned char)c;
162
163         /* Check for out of range char and replace with '?' or first char in font. */
164         if (UNLIKELY(!FONT_HAS_GLYPH(bm->font, index)))
165         {
166                 kprintf("Illegal char '%c' (0x%02x)\n", index, index);
167                 if (FONT_HAS_GLYPH(bm->font, '?'))
168                         index = '?';
169                 else
170                         index = bm->font->first;
171         }
172
173         /* Make character relative to font start */
174         index -= bm->font->first;
175
176         glyph_height = bm->font->height;
177         // FIXME: for vertical fonts only
178         glyph_height_bytes = (glyph_height + 7) / 8;
179
180         if (bm->font->offset)
181         {
182                 /* Proportional font */
183                 glyph_width = bm->font->widths[index]; /* TODO: optimize away */
184                 glyph = bm->font->glyph + bm->font->offset[index];
185         }
186         else
187         {
188                 /*
189                  * Fixed-width font: compute the first column of pixels
190                  * of the selected glyph using the character code to index
191                  * the glyph array.
192                  */
193                 glyph_width = bm->font->width;
194
195                 //For horizontal fonts
196                 //glyph = bm->font->glyph + index * (((glyph_width + 7) / 8) * glyph_height);
197                 glyph = bm->font->glyph + index * glyph_height_bytes * glyph_width;
198         }
199
200         /* Slow path for styled glyphs */
201         if (UNLIKELY(bm->styles))
202         {
203                 uint8_t styles = bm->styles;
204                 uint8_t prev_dots = 0, italic_prev_dots = 0;
205                 uint8_t dots;
206                 uint8_t row, col, row_bit;
207
208                 /*
209                  * To avoid repeating clipping and other expensive computations,
210                  * we cluster calls to gfx_blitRaster() using a small buffer.
211                  */
212                 #define CONFIG_TEXT_RENDER_OPTIMIZE 1
213                 #if CONFIG_TEXT_RENDER_OPTIMIZE
214                         #define RENDER_BUF_WIDTH 12
215                         #define RENDER_BUF_HEIGHT 8
216                         uint8_t render_buf[RAST_SIZE(RENDER_BUF_WIDTH, RENDER_BUF_HEIGHT)];
217                         uint8_t render_xpos = 0;
218                 #endif
219
220                 /* This style alone could be handled by the fast path too */
221                 if (bm->styles & STYLEF_CONDENSED)
222                         --glyph_width;
223
224                 if (bm->styles & STYLEF_EXPANDED)
225                         glyph_width *= 2;
226
227                 for (row = 0, row_bit = 0; row < glyph_height_bytes; ++row, row_bit += 8)
228                 {
229                         /* For each dot column in the glyph... */
230                         for (col = 0; col < glyph_width; ++col)
231                         {
232                                 uint8_t src_col = col;
233
234                                 /* Expanded style: advances only once every two columns. */
235                                 if (styles & STYLEF_EXPANDED)
236                                         src_col /= 2;
237
238                                 /* Fetch a column of dots from glyph. */
239                                 dots = PGM_READ_CHAR(RAST_ADDR(glyph, src_col, row_bit, glyph_width));
240
241                                 /* Italic: get lower 4 dots from previous column */
242                                 if (styles & STYLEF_ITALIC)
243                                 {
244                                         uint8_t new_dots = dots;
245                                         dots = (dots & 0xF0) | italic_prev_dots;
246                                         italic_prev_dots = new_dots & 0x0F;
247                                 }
248
249                                 /* Bold: "or" pixels with the previous column */
250                                 if (styles & STYLEF_BOLD)
251                                 {
252                                         uint8_t new_dots = dots;
253                                         dots |= prev_dots;
254                                         prev_dots = new_dots;
255                                 }
256
257                                 /* Underlined: turn on base pixel */
258                                 if ((styles & STYLEF_UNDERLINE)
259                                         && (row == glyph_height_bytes - 1))
260                                         dots |= (1 << (glyph_height - row_bit - 1));
261
262                                 /* Inverted: invert pixels */
263                                 if (styles & STYLEF_INVERT)
264                                         dots = ~dots;
265
266                                 /* Output dots */
267                                 #if CONFIG_TEXT_RENDER_OPTIMIZE
268                                         render_buf[render_xpos++] = dots;
269                                         if (render_xpos == RENDER_BUF_WIDTH)
270                                         {
271                                                 gfx_blitRaster(bm, bm->penX + col - render_xpos + 1, bm->penY + row_bit,
272                                                         render_buf, render_xpos,
273                                                         MIN((uint8_t)RENDER_BUF_HEIGHT, (uint8_t)(glyph_height - row_bit)),
274                                                         RENDER_BUF_WIDTH);
275                                                 render_xpos = 0;
276                                         }
277                                 #else
278                                         gfx_blitRaster(bm, bm->penX + col, bm->penY + row_bit,
279                                                 &dots, 1, MIN((uint8_t)8, glyph_height - row_bit), 1);
280                                 #endif
281                         }
282
283                         #if CONFIG_TEXT_RENDER_OPTIMIZE
284                                 /* Flush out rest of render buffer */
285                                 if (render_xpos != 0)
286                                 {
287                                         gfx_blitRaster(bm, bm->penX + col - render_xpos, bm->penY + row_bit,
288                                                 render_buf, render_xpos,
289                                                 MIN((uint8_t)RENDER_BUF_HEIGHT, (uint8_t)(glyph_height - row_bit)),
290                                                 RENDER_BUF_WIDTH);
291                                         render_xpos = 0;
292                                 }
293                         #endif
294                 }
295         }
296         else
297         {
298                 /* No style: fast vanilla copy of glyph to bitmap */
299                 gfx_blitRaster(bm, bm->penX, bm->penY, glyph, glyph_width, glyph_height, glyph_width);
300         }
301
302         /* Update current pen position */
303         bm->penX += glyph_width;
304
305         return c;
306 }
307
308
309 /**
310  * Render char \c c, with (currently) limited ANSI escapes
311  * emulation support and '\n' for newline.
312  */
313 int text_putchar(char c, struct Bitmap *bm)
314 {
315         /* Handle ANSI escape sequences */
316         if (UNLIKELY(ansi_mode))
317         {
318                 switch (c)
319                 {
320                 case ANSI_ESC_CLEARSCREEN:
321                         gfx_bitmapClear(bm);
322                         bm->penX = 0;
323                         bm->penY = 0;
324                         text_style(bm, 0, STYLEF_MASK);
325                         break;
326                 DB(default:
327                         kprintf("Unknown ANSI esc code: %x\n", c);)
328                 }
329                 ansi_mode = false;
330         }
331         else if (c == '\033')  /* Enter ANSI ESC mode */
332         {
333                 ansi_mode = true;
334         }
335         else if (c == '\n')  /* Go one line down on a line-feed */
336         {
337                 if (bm->penY + bm->font->height < bm->height)
338                 {
339                         bm->penY += bm->font->height;
340                         bm->penX = 0;
341                 }
342         }
343         else
344         {
345                 text_putglyph(c, bm);
346         }
347         return c;
348 }
349
350
351 /**
352  * Clear the screen and reset cursor position
353  */
354 void text_clear(struct Bitmap *bmp)
355 {
356         text_putchar('\x1b', bmp);
357         text_putchar('c', bmp);
358 }
359
360
361 void text_clearLine(struct Bitmap *bm, int line)
362 {
363         gfx_rectClear(bm, 0, line * bm->font->height, bm->width, (line + 1) * bm->font->height);
364 }
365
366
367 /**
368  * Set/clear algorithmic font style bits.
369  *
370  * \param bm     Pointer to Bitmap to affect.
371  * \param flags  Style flags to set
372  * \param mask   Mask of flags to modify
373  * \return       Old style flags
374  *
375  * Examples:
376  * Turn on bold, leave other styles alone
377  *   \code text_style(bm, STYLEF_BOLD, STYLEF_BOLD); \endcode
378  *
379  * Turn off bold and turn on italic, leave others as they are
380  *   \code text_style(bm, STYLEF_ITALIC, STYLEF_BOLD | STYLEF_ITALIC); \endcode
381  *
382  * Query current style without chaning it
383  *   \code style = text_style(bm, 0, 0); \endcode
384  *
385  * Reset all styles (plain text)
386  *   \code text_style(bm, 0, STYLE_MASK); \endcode
387  */
388 uint8_t text_style(struct Bitmap *bm, uint8_t flags, uint8_t mask)
389 {
390         uint8_t old = bm->styles;
391         bm->styles = (bm->styles & ~mask) | flags;
392         return old;
393 }