1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (c) 2016 Google, Inc
11 #include <video_console.h>
13 /* Functions needed by stb_truetype.h */
14 static int tt_floor(double val)
17 return (int)(val - 0.999);
22 static int tt_ceil(double val)
27 return (int)(val + 0.999);
30 static double frac(double val)
32 return val - tt_floor(val);
35 static double tt_fabs(double x)
37 return x < 0 ? -x : x;
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.
48 static double tt_sqrt(double value)
53 while (hi - lo > 0.00001) {
54 double mid = lo + (hi - lo) / 2;
56 if (mid * mid - value > 0.00001)
65 static double tt_fmod(double x, double y)
71 rem = x - (x / y) * y;
76 /* dummy implementation */
77 static double tt_pow(double x, double y)
82 /* dummy implementation */
83 static double tt_cos(double val)
88 /* dummy implementation */
89 static double tt_acos(double val)
94 #define STBTT_ifloor tt_floor
95 #define STBTT_iceil tt_ceil
96 #define STBTT_fabs tt_fabs
97 #define STBTT_sqrt tt_sqrt
98 #define STBTT_pow tt_pow
99 #define STBTT_fmod tt_fmod
100 #define STBTT_cos tt_cos
101 #define STBTT_acos tt_acos
102 #define STBTT_malloc(size, u) ((void)(u), malloc(size))
103 #define STBTT_free(size, u) ((void)(u), free(size))
104 #define STBTT_assert(x)
105 #define STBTT_strlen(x) strlen(x)
106 #define STBTT_memcpy memcpy
107 #define STBTT_memset memset
109 #define STB_TRUETYPE_IMPLEMENTATION
110 #include "stb_truetype.h"
113 * struct pos_info - Records a cursor position
115 * @xpos_frac: Fractional X position in pixels (multiplied by VID_FRAC_DIV)
116 * @ypos: Y position (pixels from the top)
124 * Allow one for each character on the command line plus one for each newline.
125 * This is just an estimate, but it should not be exceeded.
127 #define POS_HISTORY_SIZE (CONFIG_SYS_CBSIZE * 11 / 10)
130 * struct console_tt_metrics - Information about a font / size combination
132 * This caches various font metrics which are expensive to regenerate each time
133 * the font size changes. There is one of these for each font / size combination
136 * @font_name: Name of the font
137 * @font_size: Vertical font size in pixels
138 * @font_data: Pointer to TrueType font file contents
139 * @font: TrueType font information for the current font
140 * @baseline: Pixel offset of the font's baseline from the cursor position.
141 * This is the 'ascent' of the font, scaled to pixel coordinates.
142 * It measures the distance from the baseline to the top of the
144 * @scale: Scale of the font. This is calculated from the pixel height
145 * of the font. It is used by the STB library to generate images
146 * of the correct size.
148 struct console_tt_metrics {
149 const char *font_name;
158 * struct console_tt_priv - Private data for this driver
160 * @cur_met: Current metrics being used
161 * @metrics: List metrics that can be used
162 * @num_metrics: Number of available metrics
163 * @pos: List of cursor positions for each character written. This is
164 * used to handle backspace. We clear the frame buffer between
165 * the last position and the current position, thus erasing the
166 * last character. We record enough characters to go back to the
167 * start of the current command line.
168 * @pos_ptr: Current position in the position history
170 struct console_tt_priv {
171 struct console_tt_metrics *cur_met;
172 struct console_tt_metrics metrics[CONFIG_CONSOLE_TRUETYPE_MAX_METRICS];
174 struct pos_info pos[POS_HISTORY_SIZE];
178 static int console_truetype_set_row(struct udevice *dev, uint row, int clr)
180 struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
181 struct console_tt_priv *priv = dev_get_priv(dev);
182 struct console_tt_metrics *met = priv->cur_met;
186 line = vid_priv->fb + row * met->font_size * vid_priv->line_length;
187 end = line + met->font_size * vid_priv->line_length;
189 switch (vid_priv->bpix) {
193 if (IS_ENABLED(CONFIG_VIDEO_BPP8)) {
194 for (dst = line; dst < (u8 *)end; ++dst)
202 if (IS_ENABLED(CONFIG_VIDEO_BPP16)) {
203 for (dst = line; dst < (u16 *)end; ++dst)
211 if (IS_ENABLED(CONFIG_VIDEO_BPP32)) {
212 for (dst = line; dst < (u32 *)end; ++dst)
220 ret = vidconsole_sync_copy(dev, line, end);
227 static int console_truetype_move_rows(struct udevice *dev, uint rowdst,
228 uint rowsrc, uint count)
230 struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
231 struct console_tt_priv *priv = dev_get_priv(dev);
232 struct console_tt_metrics *met = priv->cur_met;
237 dst = vid_priv->fb + rowdst * met->font_size * vid_priv->line_length;
238 src = vid_priv->fb + rowsrc * met->font_size * vid_priv->line_length;
239 ret = vidconsole_memmove(dev, dst, src, met->font_size *
240 vid_priv->line_length * count);
244 /* Scroll up our position history */
245 diff = (rowsrc - rowdst) * met->font_size;
246 for (i = 0; i < priv->pos_ptr; i++)
247 priv->pos[i].ypos -= diff;
252 static int console_truetype_putc_xy(struct udevice *dev, uint x, uint y,
255 struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
256 struct udevice *vid = dev->parent;
257 struct video_priv *vid_priv = dev_get_uclass_priv(vid);
258 struct console_tt_priv *priv = dev_get_priv(dev);
259 struct console_tt_metrics *met = priv->cur_met;
260 stbtt_fontinfo *font = &met->font;
261 int width, height, xoff, yoff;
262 double xpos, x_shift;
264 int width_frac, linenum;
265 struct pos_info *pos;
268 void *start, *end, *line;
271 /* First get some basic metrics about this character */
272 stbtt_GetCodepointHMetrics(font, ch, &advance, &lsb);
275 * First out our current X position in fractional pixels. If we wrote
276 * a character previously, using kerning to fine-tune the position of
278 xpos = frac(VID_TO_PIXEL((double)x));
279 if (vc_priv->last_ch) {
280 xpos += met->scale * stbtt_GetCodepointKernAdvance(font,
281 vc_priv->last_ch, ch);
285 * Figure out where the cursor will move to after this character, and
286 * abort if we are out of space on this line. Also calculate the
287 * effective width of this character, which will be our return value:
288 * it dictates how much the cursor will move forward on the line.
290 x_shift = xpos - (double)tt_floor(xpos);
291 xpos += advance * met->scale;
292 width_frac = (int)VID_TO_POS(advance * met->scale);
293 if (x + width_frac >= vc_priv->xsize_frac)
296 /* Write the current cursor position into history */
297 if (priv->pos_ptr < POS_HISTORY_SIZE) {
298 pos = &priv->pos[priv->pos_ptr];
299 pos->xpos_frac = vc_priv->xcur_frac;
300 pos->ypos = vc_priv->ycur;
305 * Figure out how much past the start of a pixel we are, and pass this
306 * information into the render, which will return a 8-bit-per-pixel
307 * image of the character. For empty characters, like ' ', data will
310 data = stbtt_GetCodepointBitmapSubpixel(font, met->scale, met->scale,
311 x_shift, 0, ch, &width, &height,
316 /* Figure out where to write the character in the frame buffer */
318 start = vid_priv->fb + y * vid_priv->line_length +
319 VID_TO_PIXEL(x) * VNBYTES(vid_priv->bpix);
320 linenum = met->baseline + yoff;
322 start += linenum * vid_priv->line_length;
326 * Write a row at a time, converting the 8bpp image into the colour
327 * depth of the display. We only expect white-on-black or the reverse
328 * so the code only handles this simple case.
330 for (row = 0; row < height; row++) {
331 switch (vid_priv->bpix) {
333 if (IS_ENABLED(CONFIG_VIDEO_BPP8)) {
334 u8 *dst = line + xoff;
337 for (i = 0; i < width; i++) {
341 if (vid_priv->colour_bg)
344 if (vid_priv->colour_fg)
354 uint16_t *dst = (uint16_t *)line + xoff;
357 if (IS_ENABLED(CONFIG_VIDEO_BPP16)) {
358 for (i = 0; i < width; i++) {
362 if (vid_priv->colour_bg)
367 if (vid_priv->colour_fg)
378 u32 *dst = (u32 *)line + xoff;
381 if (IS_ENABLED(CONFIG_VIDEO_BPP32)) {
382 for (i = 0; i < width; i++) {
386 if (vid_priv->colour_bg)
388 out = val | val << 8 | val << 16;
389 if (vid_priv->colour_fg)
404 line += vid_priv->line_length;
406 ret = vidconsole_sync_copy(dev, start, line);
415 * console_truetype_backspace() - Handle a backspace operation
417 * This clears the previous character so that the console looks as if it had
420 * @dev: Device to update
421 * Return: 0 if OK, -ENOSYS if not supported
423 static int console_truetype_backspace(struct udevice *dev)
425 struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
426 struct console_tt_priv *priv = dev_get_priv(dev);
427 struct udevice *vid_dev = dev->parent;
428 struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
429 struct pos_info *pos;
433 * This indicates a very strange error higher in the stack. The caller
434 * has sent out n character and n + 1 backspaces.
439 /* Pop the last cursor position off the stack */
440 pos = &priv->pos[--priv->pos_ptr];
443 * Figure out the end position for clearing. Normally it is the current
444 * cursor position, but if we are clearing a character on the previous
445 * line, we clear from the end of the line.
447 if (pos->ypos == vc_priv->ycur)
448 xend = VID_TO_PIXEL(vc_priv->xcur_frac);
450 xend = vid_priv->xsize;
452 video_fill_part(vid_dev, VID_TO_PIXEL(pos->xpos_frac), pos->ypos,
453 xend, pos->ypos + vc_priv->y_charsize,
454 vid_priv->colour_bg);
456 /* Move the cursor back to where it was when we pushed this record */
457 vc_priv->xcur_frac = pos->xpos_frac;
458 vc_priv->ycur = pos->ypos;
463 static int console_truetype_entry_start(struct udevice *dev)
465 struct console_tt_priv *priv = dev_get_priv(dev);
467 /* A new input line has start, so clear our history */
474 * Provides a list of fonts which can be obtained at run-time in U-Boot. These
475 * are compiled in by the Makefile.
477 * At present there is no mechanism to select a particular font - the first
478 * one found is the one that is used. But the build system and the code here
479 * supports multiple fonts, which may be useful for certain firmware screens.
487 #define FONT_DECL(_name) \
488 extern u8 __ttf_ ## _name ## _begin[]; \
489 extern u8 __ttf_ ## _name ## _end[];
491 #define FONT_ENTRY(_name) { \
493 .begin = __ttf_ ## _name ## _begin, \
494 .end = __ttf_ ## _name ## _end, \
497 FONT_DECL(nimbus_sans_l_regular);
498 FONT_DECL(ankacoder_c75_r);
499 FONT_DECL(rufscript010);
500 FONT_DECL(cantoraone_regular);
502 static struct font_info font_table[] = {
503 #ifdef CONFIG_CONSOLE_TRUETYPE_NIMBUS
504 FONT_ENTRY(nimbus_sans_l_regular),
506 #ifdef CONFIG_CONSOLE_TRUETYPE_ANKACODER
507 FONT_ENTRY(ankacoder_c75_r),
509 #ifdef CONFIG_CONSOLE_TRUETYPE_RUFSCRIPT
510 FONT_ENTRY(rufscript010),
512 #ifdef CONFIG_CONSOLE_TRUETYPE_CANTORAONE
513 FONT_ENTRY(cantoraone_regular),
519 * font_valid() - Check if a font-table entry is valid
521 * Depending on available files in the build system, fonts may end up being
524 * @return true if the entry is valid
526 static inline bool font_valid(struct font_info *tab)
528 return abs(tab->begin - tab->end) > 4;
532 * console_truetype_find_font() - Find a suitable font
534 * This searches for the first available font.
536 * Return: pointer to the font-table entry, or NULL if none is found
538 static struct font_info *console_truetype_find_font(void)
540 struct font_info *tab;
542 for (tab = font_table; tab->begin; tab++) {
543 if (font_valid(tab)) {
544 debug("%s: Font '%s', at %p, size %lx\n", __func__,
545 tab->name, tab->begin,
546 (ulong)(tab->end - tab->begin));
554 int console_truetype_get_font(struct udevice *dev, int seq,
555 struct vidfont_info *info)
557 struct font_info *tab;
560 for (i = 0, tab = font_table; tab->begin; tab++, i++) {
561 if (i == seq && font_valid(tab)) {
562 info->name = tab->name;
571 * truetype_add_metrics() - Add a new font/size combination
573 * @dev: Video console device to update
574 * @font_name: Name of font
575 * @font_size: Size of the font (norminal pixel height)
576 * @font_data: Pointer to the font data
577 * @return 0 if OK, -EPERM if stbtt failed, -E2BIG if the the metrics table is
580 static int truetype_add_metrics(struct udevice *dev, const char *font_name,
581 uint font_size, const void *font_data)
583 struct console_tt_priv *priv = dev_get_priv(dev);
584 struct console_tt_metrics *met;
585 stbtt_fontinfo *font;
588 if (priv->num_metrics == CONFIG_CONSOLE_TRUETYPE_MAX_METRICS)
589 return log_msg_ret("num", -E2BIG);
591 met = &priv->metrics[priv->num_metrics];
592 met->font_name = font_name;
593 met->font_size = font_size;
594 met->font_data = font_data;
597 if (!stbtt_InitFont(font, font_data, 0)) {
598 debug("%s: Font init failed\n", __func__);
602 /* Pre-calculate some things we will need regularly */
603 met->scale = stbtt_ScaleForPixelHeight(font, font_size);
604 stbtt_GetFontVMetrics(font, &ascent, 0, 0);
605 met->baseline = (int)(ascent * met->scale);
607 return priv->num_metrics++;
611 * find_metrics() - Find the metrics for a given font and size
613 * @dev: Video console device to update
614 * @name: Name of font
615 * @size: Size of the font (norminal pixel height)
616 * @return metrics, if found, else NULL
618 static struct console_tt_metrics *find_metrics(struct udevice *dev,
619 const char *name, uint size)
621 struct console_tt_priv *priv = dev_get_priv(dev);
624 for (i = 0; i < priv->num_metrics; i++) {
625 struct console_tt_metrics *met = &priv->metrics[i];
627 if (!strcmp(name, met->font_name) && met->font_size == size)
634 static void select_metrics(struct udevice *dev, struct console_tt_metrics *met)
636 struct vidconsole_priv *vc_priv = dev_get_uclass_priv(dev);
637 struct console_tt_priv *priv = dev_get_priv(dev);
638 struct udevice *vid_dev = dev_get_parent(dev);
639 struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
642 vc_priv->x_charsize = met->font_size;
643 vc_priv->y_charsize = met->font_size;
644 vc_priv->xstart_frac = VID_TO_POS(2);
645 vc_priv->cols = vid_priv->xsize / met->font_size;
646 vc_priv->rows = vid_priv->ysize / met->font_size;
647 vc_priv->tab_width_frac = VID_TO_POS(met->font_size) * 8 / 2;
650 static int get_metrics(struct udevice *dev, const char *name, uint size,
651 struct console_tt_metrics **metp)
653 struct console_tt_priv *priv = dev_get_priv(dev);
654 struct console_tt_metrics *met;
655 struct font_info *tab;
659 size = CONFIG_CONSOLE_TRUETYPE_SIZE;
661 name = font_table->name;
663 met = find_metrics(dev, name, size);
665 for (tab = font_table; tab->begin; tab++) {
666 if (font_valid(tab) &&
667 !strcmp(name, tab->name)) {
670 ret = truetype_add_metrics(dev,
675 return log_msg_ret("add", ret);
677 met = &priv->metrics[ret];
683 return log_msg_ret("find", -ENOENT);
685 /* Use the default font */
694 static int truetype_select_font(struct udevice *dev, const char *name,
697 struct console_tt_metrics *met;
700 ret = get_metrics(dev, name, size, &met);
702 return log_msg_ret("sel", ret);
704 select_metrics(dev, met);
709 int truetype_measure(struct udevice *dev, const char *name, uint size,
710 const char *text, struct vidconsole_bbox *bbox)
712 struct console_tt_metrics *met;
713 stbtt_fontinfo *font;
720 ret = get_metrics(dev, name, size, &met);
722 return log_msg_ret("sel", ret);
730 for (last = 0, s = text; *s; s++) {
733 /* Used kerning to fine-tune the position of this character */
735 width += stbtt_GetCodepointKernAdvance(font, last, ch);
737 /* First get some basic metrics about this character */
738 stbtt_GetCodepointHMetrics(font, ch, &advance, &lsb);
747 bbox->x1 = tt_ceil((double)width * met->scale);
748 bbox->y1 = met->font_size;
753 const char *console_truetype_get_font_size(struct udevice *dev, uint *sizep)
755 struct console_tt_priv *priv = dev_get_priv(dev);
756 struct console_tt_metrics *met = priv->cur_met;
758 *sizep = met->font_size;
760 return met->font_name;
763 static int console_truetype_probe(struct udevice *dev)
765 struct console_tt_priv *priv = dev_get_priv(dev);
766 struct udevice *vid_dev = dev->parent;
767 struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
768 struct font_info *tab;
772 debug("%s: start\n", __func__);
773 if (vid_priv->font_size)
774 font_size = vid_priv->font_size;
776 font_size = CONFIG_CONSOLE_TRUETYPE_SIZE;
777 tab = console_truetype_find_font();
779 debug("%s: Could not find any fonts\n", __func__);
783 ret = truetype_add_metrics(dev, tab->name, font_size, tab->begin);
785 return log_msg_ret("add", ret);
786 priv->cur_met = &priv->metrics[ret];
788 select_metrics(dev, &priv->metrics[ret]);
790 debug("%s: ready\n", __func__);
795 struct vidconsole_ops console_truetype_ops = {
796 .putc_xy = console_truetype_putc_xy,
797 .move_rows = console_truetype_move_rows,
798 .set_row = console_truetype_set_row,
799 .backspace = console_truetype_backspace,
800 .entry_start = console_truetype_entry_start,
801 .get_font = console_truetype_get_font,
802 .get_font_size = console_truetype_get_font_size,
803 .select_font = truetype_select_font,
804 .measure = truetype_measure,
807 U_BOOT_DRIVER(vidconsole_truetype) = {
808 .name = "vidconsole_tt",
809 .id = UCLASS_VIDEO_CONSOLE,
810 .ops = &console_truetype_ops,
811 .probe = console_truetype_probe,
812 .priv_auto = sizeof(struct console_tt_priv),