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