BeRTOS
text.c
Go to the documentation of this file.
00001 
00040 #include <gfx/gfx.h>
00041 #include <gfx/font.h>
00042 #include <gfx/text.h>
00043 
00044 #include <gfx/gfx_p.h> // FIXME: BM_DRAWPIXEL
00045 
00046 #include <cfg/debug.h>
00047 
00048 
00054 static bool ansi_mode = false;
00055 
00059 void text_setCoord(struct Bitmap *bm, int x, int y)
00060 {
00061     bm->penX = x;
00062     bm->penY = y;
00063 }
00064 
00065 
00070 void text_moveTo(struct Bitmap *bm, int row, int col)
00071 {
00072     ASSERT(col >= 0);
00073     ASSERT(col < bm->width / bm->font->width);
00074     ASSERT(row >= 0);
00075     ASSERT(row < bm->height / bm->font->height);
00076 
00077     text_setCoord(bm, col * bm->font->width, row * bm->font->height);
00078 }
00079 
00080 
00084 static int text_putglyph(char c, struct Bitmap *bm)
00085 {
00086     const uint8_t * PROGMEM glyph;  /* font is in progmem */
00087     uint8_t glyph_width, glyph_height, glyph_height_bytes;
00088     unsigned char index = (unsigned char)c;
00089 
00090     /* Check for out of range char and replace with '?' or first char in font. */
00091     if (UNLIKELY(!FONT_HAS_GLYPH(bm->font, index)))
00092     {
00093         kprintf("Illegal char '%c' (0x%02x)\n", index, index);
00094         if (FONT_HAS_GLYPH(bm->font, '?'))
00095             index = '?';
00096         else
00097             index = bm->font->first;
00098     }
00099 
00100     /* Make character relative to font start */
00101     index -= bm->font->first;
00102 
00103     glyph_height = bm->font->height;
00104     // FIXME: for vertical fonts only
00105     glyph_height_bytes = (glyph_height + 7) / 8;
00106 
00107     if (bm->font->offset)
00108     {
00109         /* Proportional font */
00110         glyph_width = bm->font->widths[index]; /* TODO: optimize away */
00111         glyph = bm->font->glyph + bm->font->offset[index];
00112     }
00113     else
00114     {
00115         /*
00116          * Fixed-width font: compute the first column of pixels
00117          * of the selected glyph using the character code to index
00118          * the glyph array.
00119          */
00120         glyph_width = bm->font->width;
00121 
00122         //For horizontal fonts
00123         //glyph = bm->font->glyph + index * (((glyph_width + 7) / 8) * glyph_height);
00124         glyph = bm->font->glyph + index * glyph_height_bytes * glyph_width;
00125     }
00126 
00127     /* Slow path for styled glyphs */
00128     if (UNLIKELY(bm->styles))
00129     {
00130         uint8_t styles = bm->styles;
00131         uint8_t prev_dots = 0, italic_prev_dots = 0;
00132         uint8_t dots;
00133         uint8_t row, col, row_bit;
00134 
00135         /*
00136          * To avoid repeating clipping and other expensive computations,
00137          * we cluster calls to gfx_blitRaster() using a small buffer.
00138          */
00139         #define CONFIG_TEXT_RENDER_OPTIMIZE 1
00140         #if CONFIG_TEXT_RENDER_OPTIMIZE
00141             #define RENDER_BUF_WIDTH 12
00142             #define RENDER_BUF_HEIGHT 8
00143             uint8_t render_buf[RAST_SIZE(RENDER_BUF_WIDTH, RENDER_BUF_HEIGHT)];
00144             uint8_t render_xpos = 0;
00145         #endif
00146 
00147         /* This style alone could be handled by the fast path too */
00148         if (bm->styles & STYLEF_CONDENSED)
00149             --glyph_width;
00150 
00151         if (bm->styles & STYLEF_EXPANDED)
00152             glyph_width *= 2;
00153 
00154         for (row = 0, row_bit = 0; row < glyph_height_bytes; ++row, row_bit += 8)
00155         {
00156             /* For each dot column in the glyph... */
00157             for (col = 0; col < glyph_width; ++col)
00158             {
00159                 uint8_t src_col = col;
00160 
00161                 /* Expanded style: advances only once every two columns. */
00162                 if (styles & STYLEF_EXPANDED)
00163                     src_col /= 2;
00164 
00165                 /* Fetch a column of dots from glyph. */
00166                 dots = PGM_READ_CHAR(RAST_ADDR(glyph, src_col, row_bit, glyph_width));
00167 
00168                 /* Italic: get lower 4 dots from previous column */
00169                 if (styles & STYLEF_ITALIC)
00170                 {
00171                     uint8_t new_dots = dots;
00172                     dots = (dots & 0xF0) | italic_prev_dots;
00173                     italic_prev_dots = new_dots & 0x0F;
00174                 }
00175 
00176                 /* Bold: "or" pixels with the previous column */
00177                 if (styles & STYLEF_BOLD)
00178                 {
00179                     uint8_t new_dots = dots;
00180                     dots |= prev_dots;
00181                     prev_dots = new_dots;
00182                 }
00183 
00184                 /* Underlined: turn on base pixel */
00185                 if ((styles & STYLEF_UNDERLINE)
00186                     && (row == glyph_height_bytes - 1))
00187                     dots |= (1 << (glyph_height - row_bit - 1));
00188 
00189                 /* Inverted: invert pixels */
00190                 if (styles & STYLEF_INVERT)
00191                     dots = ~dots;
00192 
00193                 /* Output dots */
00194                 #if CONFIG_TEXT_RENDER_OPTIMIZE
00195                     render_buf[render_xpos++] = dots;
00196                     if (render_xpos == RENDER_BUF_WIDTH)
00197                     {
00198                         gfx_blitRaster(bm, bm->penX + col - render_xpos + 1, bm->penY + row_bit,
00199                             render_buf, render_xpos,
00200                             MIN((uint8_t)RENDER_BUF_HEIGHT, (uint8_t)(glyph_height - row_bit)),
00201                             RENDER_BUF_WIDTH);
00202                         render_xpos = 0;
00203                     }
00204                 #else
00205                     gfx_blitRaster(bm, bm->penX + col, bm->penY + row_bit,
00206                         &dots, 1, MIN((uint8_t)8, glyph_height - row_bit), 1);
00207                 #endif
00208             }
00209 
00210             #if CONFIG_TEXT_RENDER_OPTIMIZE
00211                 /* Flush out rest of render buffer */
00212                 if (render_xpos != 0)
00213                 {
00214                     gfx_blitRaster(bm, bm->penX + col - render_xpos, bm->penY + row_bit,
00215                         render_buf, render_xpos,
00216                         MIN((uint8_t)RENDER_BUF_HEIGHT, (uint8_t)(glyph_height - row_bit)),
00217                         RENDER_BUF_WIDTH);
00218                     render_xpos = 0;
00219                 }
00220             #endif
00221         }
00222     }
00223     else
00224     {
00225         /* No style: fast vanilla copy of glyph to bitmap */
00226         gfx_blitRaster(bm, bm->penX, bm->penY, glyph, glyph_width, glyph_height, glyph_width);
00227     }
00228 
00229     /* Update current pen position */
00230     bm->penX += glyph_width;
00231 
00232     return c;
00233 }
00234 
00235 
00240 int text_putchar(char c, struct Bitmap *bm)
00241 {
00242     /* Handle ANSI escape sequences */
00243     if (UNLIKELY(ansi_mode))
00244     {
00245         switch (c)
00246         {
00247         case ANSI_ESC_CLEARSCREEN:
00248             gfx_bitmapClear(bm);
00249             bm->penX = 0;
00250             bm->penY = 0;
00251             text_style(bm, 0, STYLEF_MASK);
00252             break;
00253         DB(default:
00254             kprintf("Unknown ANSI esc code: %x\n", c);)
00255         }
00256         ansi_mode = false;
00257     }
00258     else if (c == '\033')  /* Enter ANSI ESC mode */
00259     {
00260         ansi_mode = true;
00261     }
00262     else if (c == '\n')  /* Go one line down on a line-feed */
00263     {
00264         if (bm->penY + bm->font->height < bm->height)
00265         {
00266             bm->penY += bm->font->height;
00267             bm->penX = 0;
00268         }
00269     }
00270     else
00271     {
00272         text_putglyph(c, bm);
00273     }
00274     return c;
00275 }
00276 
00277 
00281 void text_clear(struct Bitmap *bmp)
00282 {
00283     text_putchar('\x1b', bmp);
00284     text_putchar('c', bmp);
00285 }
00286 
00287 
00288 void text_clearLine(struct Bitmap *bm, int line)
00289 {
00290     gfx_rectClear(bm, 0, line * bm->font->height, bm->width, (line + 1) * bm->font->height);
00291 }
00292 
00293 
00315 uint8_t text_style(struct Bitmap *bm, uint8_t flags, uint8_t mask)
00316 {
00317     uint8_t old = bm->styles;
00318     bm->styles = (bm->styles & ~mask) | flags;
00319     return old;
00320 }