uboot/drivers/video/console_truetype.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2016 Google, Inc
   3 *
   4 * SPDX-License-Identifier:     GPL-2.0+
   5 */
   6
   7#include <common.h>
   8#include <dm.h>
   9#include <video.h>
  10#include <video_console.h>
  11
  12/* Functions needed by stb_truetype.h */
  13static int tt_floor(double val)
  14{
  15        if (val < 0)
  16                return (int)(val - 0.999);
  17
  18        return (int)val;
  19}
  20
  21static int tt_ceil(double val)
  22{
  23        if (val < 0)
  24                return (int)val;
  25
  26        return (int)(val + 0.999);
  27}
  28
  29static double frac(double val)
  30{
  31        return val - tt_floor(val);
  32}
  33
  34static double tt_fabs(double x)
  35{
  36        return x < 0 ? -x : x;
  37}
  38
  39 /*
  40  * Simple square root algorithm. This is from:
  41  * http://stackoverflow.com/questions/1623375/writing-your-own-square-root-function
  42  * Written by Chihung Yu
  43  * Creative Commons license
  44  * http://creativecommons.org/licenses/by-sa/3.0/legalcode
  45  * It has been modified to compile correctly, and for U-Boot style.
  46  */
  47static double tt_sqrt(double value)
  48{
  49        double lo = 1.0;
  50        double hi = value;
  51
  52        while (hi - lo > 0.00001) {
  53                double mid = lo + (hi - lo) / 2;
  54
  55                if (mid * mid - value > 0.00001)
  56                        hi = mid;
  57                else
  58                        lo = mid;
  59        }
  60
  61        return lo;
  62}
  63
  64#define STBTT_ifloor            tt_floor
  65#define STBTT_iceil             tt_ceil
  66#define STBTT_fabs              tt_fabs
  67#define STBTT_sqrt              tt_sqrt
  68#define STBTT_malloc(size, u)   ((void)(u), malloc(size))
  69#define STBTT_free(size, u)     ((void)(u), free(size))
  70#define STBTT_assert(x)
  71#define STBTT_strlen(x)         strlen(x)
  72#define STBTT_memcpy            memcpy
  73#define STBTT_memset            memset
  74
  75#define STB_TRUETYPE_IMPLEMENTATION
  76#include "stb_truetype.h"
  77
  78/**
  79 * struct pos_info - Records a cursor position
  80 *
  81 * @xpos_frac:  Fractional X position in pixels (multiplied by VID_FRAC_DIV)
  82 * @ypos:       Y position (pixels from the top)
  83 */
  84struct pos_info {
  85        int xpos_frac;
  86        int ypos;
  87};
  88
  89/*
  90 * Allow one for each character on the command line plus one for each newline.
  91 * This is just an estimate, but it should not be exceeded.
  92 */
  93#define POS_HISTORY_SIZE        (CONFIG_SYS_CBSIZE * 11 / 10)
  94
  95/**
  96 * struct console_tt_priv - Private data for this driver
  97 *
  98 * @font_size:  Vertical font size in pixels
  99 * @font_data:  Pointer to TrueType font file contents
 100 * @font:       TrueType font information for the current font
 101 * @pos:        List of cursor positions for each character written. This is
 102 *              used to handle backspace. We clear the frame buffer between
 103 *              the last position and the current position, thus erasing the
 104 *              last character. We record enough characters to go back to the
 105 *              start of the current command line.
 106 * @pos_ptr:    Current position in the position history
 107 * @baseline:   Pixel offset of the font's baseline from the cursor position.
 108 *              This is the 'ascent' of the font, scaled to pixel coordinates.
 109 *              It measures the distance from the baseline to the top of the
 110 *              font.
 111 * @scale:      Scale of the font. This is calculated from the pixel height
 112 *              of the font. It is used by the STB library to generate images
 113 *              of the correct size.
 114 */
 115struct console_tt_priv {
 116        int font_size;
 117        u8 *font_data;
 118        stbtt_fontinfo font;
 119        struct pos_info pos[POS_HISTORY_SIZE];
 120        int pos_ptr;
 121        int baseline;
 122        double scale;
 123};
 124
 125static int console_truetype_set_row(struct udevice *dev, uint row, int clr)
 126{
 127        struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
 128        struct console_tt_priv *priv = dev_get_priv(dev);
 129        void *line;
 130        int pixels = priv->font_size * vid_priv->line_length;
 131        int i;
 132
 133        line = vid_priv->fb + row * priv->font_size * vid_priv->line_length;
 134        switch (vid_priv->bpix) {
 135#ifdef CONFIG_VIDEO_BPP8
 136        case VIDEO_BPP8: {
 137                uint8_t *dst = line;
 138
 139                for (i = 0; i < pixels; i++)
 140                        *dst++ = clr;
 141                break;
 142        }
 143#endif
 144#ifdef CONFIG_VIDEO_BPP16
 145        case VIDEO_BPP16: {
 146                uint16_t *dst = line;
 147
 148                for (i = 0; i < pixels; i++)
 149                        *dst++ = clr;
 150                break;
 151        }
 152#endif
 153#ifdef CONFIG_VIDEO_BPP32
 154        case VIDEO_BPP32: {
 155                uint32_t *dst = line;
 156
 157                for (i = 0; i < pixels; i++)
 158                        *dst++ = clr;
 159                break;
 160        }
 161#endif
 162        default:
 163                return -ENOSYS;
 164        }
 165
 166        return 0;
 167}
 168
 169static int console_truetype_move_rows(struct udevice *dev, uint rowdst,
 170                                     uint rowsrc, uint count)
 171{
 172        struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
 173        struct console_tt_priv *priv = dev_get_priv(dev);
 174        void *dst;
 175        void *src;
 176        int i, diff;
 177
 178        dst = vid_priv->fb + rowdst * priv->font_size * vid_priv->line_length;
 179        src = vid_priv->fb + rowsrc * priv->font_size * vid_priv->line_length;
 180        memmove(dst, src, priv->font_size * vid_priv->line_length * count);
 181
 182        /* Scroll up our position history */
 183        diff = (rowsrc - rowdst) * priv->font_size;
 184        for (i = 0; i < priv->pos_ptr; i++)
 185                priv->pos[i].ypos -= diff;
 186
 187        return 0;
 188}
 189
 190static int console_truetype_putc_xy(struct udevice *dev, uint x, uint y,
 191                                    char ch)
 192{
 193        struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
 194        struct udevice *vid = dev->parent;
 195        struct video_priv *vid_priv = dev_get_uclass_priv(vid);
 196        struct console_tt_priv *priv = dev_get_priv(dev);
 197        stbtt_fontinfo *font = &priv->font;
 198        int width, height, xoff, yoff;
 199        double xpos, x_shift;
 200        int lsb;
 201        int width_frac, linenum;
 202        struct pos_info *pos;
 203        u8 *bits, *data;
 204        int advance;
 205        void *line;
 206        int row;
 207
 208        /* First get some basic metrics about this character */
 209        stbtt_GetCodepointHMetrics(font, ch, &advance, &lsb);
 210
 211        /*
 212         * First out our current X position in fractional pixels. If we wrote
 213         * a character previously, using kerning to fine-tune the position of
 214         * this character */
 215        xpos = frac(VID_TO_PIXEL((double)x));
 216        if (vc_priv->last_ch) {
 217                xpos += priv->scale * stbtt_GetCodepointKernAdvance(font,
 218                                                        vc_priv->last_ch, ch);
 219        }
 220
 221        /*
 222         * Figure out where the cursor will move to after this character, and
 223         * abort if we are out of space on this line. Also calculate the
 224         * effective width of this character, which will be our return value:
 225         * it dictates how much the cursor will move forward on the line.
 226         */
 227        x_shift = xpos - (double)tt_floor(xpos);
 228        xpos += advance * priv->scale;
 229        width_frac = (int)VID_TO_POS(xpos);
 230        if (x + width_frac >= vc_priv->xsize_frac)
 231                return -EAGAIN;
 232
 233        /* Write the current cursor position into history */
 234        if (priv->pos_ptr < POS_HISTORY_SIZE) {
 235                pos = &priv->pos[priv->pos_ptr];
 236                pos->xpos_frac = vc_priv->xcur_frac;
 237                pos->ypos = vc_priv->ycur;
 238                priv->pos_ptr++;
 239        }
 240
 241        /*
 242         * Figure out how much past the start of a pixel we are, and pass this
 243         * information into the render, which will return a 8-bit-per-pixel
 244         * image of the character. For empty characters, like ' ', data will
 245         * return NULL;
 246         */
 247        data = stbtt_GetCodepointBitmapSubpixel(font, priv->scale, priv->scale,
 248                                                x_shift, 0, ch, &width, &height,
 249                                                &xoff, &yoff);
 250        if (!data)
 251                return width_frac;
 252
 253        /* Figure out where to write the character in the frame buffer */
 254        bits = data;
 255        line = vid_priv->fb + y * vid_priv->line_length +
 256                VID_TO_PIXEL(x) * VNBYTES(vid_priv->bpix);
 257        linenum = priv->baseline + yoff;
 258        if (linenum > 0)
 259                line += linenum * vid_priv->line_length;
 260
 261        /*
 262         * Write a row at a time, converting the 8bpp image into the colour
 263         * depth of the display. We only expect white-on-black or the reverse
 264         * so the code only handles this simple case.
 265         */
 266        for (row = 0; row < height; row++) {
 267                switch (vid_priv->bpix) {
 268#ifdef CONFIG_VIDEO_BPP16
 269                case VIDEO_BPP16: {
 270                        uint16_t *dst = (uint16_t *)line + xoff;
 271                        int i;
 272
 273                        for (i = 0; i < width; i++) {
 274                                int val = *bits;
 275                                int out;
 276
 277                                if (vid_priv->colour_bg)
 278                                        val = 255 - val;
 279                                out = val >> 3 |
 280                                        (val >> 2) << 5 |
 281                                        (val >> 3) << 11;
 282                                if (vid_priv->colour_fg)
 283                                        *dst++ |= out;
 284                                else
 285                                        *dst++ &= out;
 286                                bits++;
 287                        }
 288                        break;
 289                }
 290#endif
 291                default:
 292                        free(data);
 293                        return -ENOSYS;
 294                }
 295
 296                line += vid_priv->line_length;
 297        }
 298        free(data);
 299
 300        return width_frac;
 301}
 302
 303/**
 304 * console_truetype_erase() - Erase a character
 305 *
 306 * This is used for backspace. We erase a square of the display within the
 307 * given bounds.
 308 *
 309 * @dev:        Device to update
 310 * @xstart:     X start position in pixels from the left
 311 * @ystart:     Y start position in pixels from the top
 312 * @xend:       X end position in pixels from the left
 313 * @yend:       Y end position  in pixels from the top
 314 * @clr:        Value to write
 315 * @return 0 if OK, -ENOSYS if the display depth is not supported
 316 */
 317static int console_truetype_erase(struct udevice *dev, int xstart, int ystart,
 318                                  int xend, int yend, int clr)
 319{
 320        struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
 321        void *line;
 322        int pixels = xend - xstart;
 323        int row, i;
 324
 325        line = vid_priv->fb + ystart * vid_priv->line_length;
 326        line += xstart * VNBYTES(vid_priv->bpix);
 327        for (row = ystart; row < yend; row++) {
 328                switch (vid_priv->bpix) {
 329#ifdef CONFIG_VIDEO_BPP8
 330                case VIDEO_BPP8: {
 331                        uint8_t *dst = line;
 332
 333                        for (i = 0; i < pixels; i++)
 334                                *dst++ = clr;
 335                        break;
 336                }
 337#endif
 338#ifdef CONFIG_VIDEO_BPP16
 339                case VIDEO_BPP16: {
 340                        uint16_t *dst = line;
 341
 342                        for (i = 0; i < pixels; i++)
 343                                *dst++ = clr;
 344                        break;
 345                }
 346#endif
 347#ifdef CONFIG_VIDEO_BPP32
 348                case VIDEO_BPP32: {
 349                        uint32_t *dst = line;
 350
 351                        for (i = 0; i < pixels; i++)
 352                                *dst++ = clr;
 353                        break;
 354                }
 355#endif
 356                default:
 357                        return -ENOSYS;
 358                }
 359                line += vid_priv->line_length;
 360        }
 361
 362        return 0;
 363}
 364
 365/**
 366 * console_truetype_backspace() - Handle a backspace operation
 367 *
 368 * This clears the previous character so that the console looks as if it had
 369 * not been entered.
 370 *
 371 * @dev:        Device to update
 372 * @return 0 if OK, -ENOSYS if not supported
 373 */
 374static int console_truetype_backspace(struct udevice *dev)
 375{
 376        struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
 377        struct console_tt_priv *priv = dev_get_priv(dev);
 378        struct udevice *vid_dev = dev->parent;
 379        struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
 380        struct pos_info *pos;
 381        int xend;
 382
 383        /*
 384         * This indicates a very strange error higher in the stack. The caller
 385         * has sent out n character and n + 1 backspaces.
 386         */
 387        if (!priv->pos_ptr)
 388                return -ENOSYS;
 389
 390        /* Pop the last cursor position off the stack */
 391        pos = &priv->pos[--priv->pos_ptr];
 392
 393        /*
 394         * Figure out the end position for clearing. Normlly it is the current
 395         * cursor position, but if we are clearing a character on the previous
 396         * line, we clear from the end of the line.
 397         */
 398        if (pos->ypos == vc_priv->ycur)
 399                xend = VID_TO_PIXEL(vc_priv->xcur_frac);
 400        else
 401                xend = vid_priv->xsize;
 402
 403        console_truetype_erase(dev, VID_TO_PIXEL(pos->xpos_frac), pos->ypos,
 404                               xend, pos->ypos + vc_priv->y_charsize,
 405                               vid_priv->colour_bg);
 406
 407        /* Move the cursor back to where it was when we pushed this record */
 408        vc_priv->xcur_frac = pos->xpos_frac;
 409        vc_priv->ycur = pos->ypos;
 410
 411        return 0;
 412}
 413
 414static int console_truetype_entry_start(struct udevice *dev)
 415{
 416        struct console_tt_priv *priv = dev_get_priv(dev);
 417
 418        /* A new input line has start, so clear our history */
 419        priv->pos_ptr = 0;
 420
 421        return 0;
 422}
 423
 424/*
 425 * Provides a list of fonts which can be obtained at run-time in U-Boot. These
 426 * are compiled in by the Makefile.
 427 *
 428 * At present there is no mechanism to select a particular font - the first
 429 * one found is the one that is used. But the build system and the code here
 430 * supports multiple fonts, which may be useful for certain firmware screens.
 431 */
 432struct font_info {
 433        char *name;
 434        u8 *begin;
 435        u8 *end;
 436};
 437
 438#define FONT_DECL(_name) \
 439        extern u8 __ttf_ ## _name ## _begin[]; \
 440        extern u8 __ttf_ ## _name ## _end[];
 441
 442#define FONT_ENTRY(_name)               { \
 443        .name = #_name, \
 444        .begin = __ttf_ ## _name ## _begin, \
 445        .end = __ttf_ ## _name ## _end, \
 446        }
 447
 448FONT_DECL(nimbus_sans_l_regular);
 449FONT_DECL(ankacoder_c75_r);
 450FONT_DECL(rufscript010);
 451FONT_DECL(cantoraone_regular);
 452
 453static struct font_info font_table[] = {
 454#ifdef CONFIG_CONSOLE_TRUETYPE_NIMBUS
 455        FONT_ENTRY(nimbus_sans_l_regular),
 456#endif
 457#ifdef CONFIG_CONSOLE_TRUETYPE_ANKACODER
 458        FONT_ENTRY(ankacoder_c75_r),
 459#endif
 460#ifdef CONFIG_CONSOLE_TRUETYPE_RUFSCRIPT
 461        FONT_ENTRY(rufscript010),
 462#endif
 463#ifdef CONFIG_CONSOLE_TRUETYPE_CANTORAONE
 464        FONT_ENTRY(cantoraone_regular),
 465#endif
 466        {} /* sentinel */
 467};
 468
 469#define FONT_BEGIN(name)        __ttf_ ## name ## _begin
 470#define FONT_END(name)          __ttf_ ## name ## _end
 471#define FONT_IS_VALID(name)     (abs(FONT_END(name) - FONT_BEGIN) > 4)
 472
 473/**
 474 * console_truetype_find_font() - Find a suitable font
 475 *
 476 * This searched for the first available font.
 477 *
 478 * @return pointer to the font, or NULL if none is found
 479 */
 480static u8 *console_truetype_find_font(void)
 481{
 482        struct font_info *tab;
 483
 484        for (tab = font_table; tab->begin; tab++) {
 485                if (abs(tab->begin - tab->end) > 4) {
 486                        debug("%s: Font '%s', at %p, size %lx\n", __func__,
 487                              tab->name, tab->begin,
 488                              (ulong)(tab->end - tab->begin));
 489                        return tab->begin;
 490                }
 491        }
 492
 493        return NULL;
 494}
 495
 496static int console_truetype_probe(struct udevice *dev)
 497{
 498        struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
 499        struct console_tt_priv *priv = dev_get_priv(dev);
 500        struct udevice *vid_dev = dev->parent;
 501        struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
 502        stbtt_fontinfo *font = &priv->font;
 503        int ascent;
 504
 505        debug("%s: start\n", __func__);
 506        if (vid_priv->font_size)
 507                priv->font_size = vid_priv->font_size;
 508        else
 509                priv->font_size = CONFIG_CONSOLE_TRUETYPE_SIZE;
 510        priv->font_data = console_truetype_find_font();
 511        if (!priv->font_data) {
 512                debug("%s: Could not find any fonts\n", __func__);
 513                return -EBFONT;
 514        }
 515
 516        vc_priv->x_charsize = priv->font_size;
 517        vc_priv->y_charsize = priv->font_size;
 518        vc_priv->xstart_frac = VID_TO_POS(2);
 519        vc_priv->cols = vid_priv->xsize / priv->font_size;
 520        vc_priv->rows = vid_priv->ysize / priv->font_size;
 521        vc_priv->tab_width_frac = VID_TO_POS(priv->font_size) * 8 / 2;
 522
 523        if (!stbtt_InitFont(font, priv->font_data, 0)) {
 524                debug("%s: Font init failed\n", __func__);
 525                return -EPERM;
 526        }
 527
 528        /* Pre-calculate some things we will need regularly */
 529        priv->scale = stbtt_ScaleForPixelHeight(font, priv->font_size);
 530        stbtt_GetFontVMetrics(font, &ascent, 0, 0);
 531        priv->baseline = (int)(ascent * priv->scale);
 532        debug("%s: ready\n", __func__);
 533
 534        return 0;
 535}
 536
 537struct vidconsole_ops console_truetype_ops = {
 538        .putc_xy        = console_truetype_putc_xy,
 539        .move_rows      = console_truetype_move_rows,
 540        .set_row        = console_truetype_set_row,
 541        .backspace      = console_truetype_backspace,
 542        .entry_start    = console_truetype_entry_start,
 543};
 544
 545U_BOOT_DRIVER(vidconsole_truetype) = {
 546        .name   = "vidconsole_tt",
 547        .id     = UCLASS_VIDEO_CONSOLE,
 548        .ops    = &console_truetype_ops,
 549        .probe  = console_truetype_probe,
 550        .priv_auto_alloc_size   = sizeof(struct console_tt_priv),
 551};
 552