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