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