2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright 2020 Toomas Soome
5 * Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
6 * Copyright 2020 RackTop Systems, Inc.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 #include <sys/cdefs.h>
33 #include <sys/param.h>
38 #include <sys/stdint.h>
39 #include <sys/endian.h>
41 #include <bootstrap.h>
50 /* VGA text mode does use bold font. */
51 #if !defined(VGA_8X16_FONT)
52 #define VGA_8X16_FONT "/boot/fonts/8x16b.fnt"
54 #if !defined(DEFAULT_8X16_FONT)
55 #define DEFAULT_8X16_FONT "/boot/fonts/8x16.fnt"
59 * Must be sorted by font size in descending order
61 font_list_t fonts = STAILQ_HEAD_INITIALIZER(fonts);
63 #define DEFAULT_FONT_DATA font_data_8x16
64 extern vt_font_bitmap_data_t font_data_8x16;
65 teken_gfx_t gfx_state = { 0 };
68 unsigned char r; /* Red percentage value. */
69 unsigned char g; /* Green percentage value. */
70 unsigned char b; /* Blue percentage value. */
71 } color_def[NCOLORS] = {
72 {0, 0, 0}, /* black */
73 {50, 0, 0}, /* dark red */
74 {0, 50, 0}, /* dark green */
75 {77, 63, 0}, /* dark yellow */
76 {20, 40, 64}, /* dark blue */
77 {50, 0, 50}, /* dark magenta */
78 {0, 50, 50}, /* dark cyan */
79 {75, 75, 75}, /* light gray */
81 {18, 20, 21}, /* dark gray */
82 {100, 0, 0}, /* light red */
83 {0, 100, 0}, /* light green */
84 {100, 100, 0}, /* light yellow */
85 {45, 62, 81}, /* light blue */
86 {100, 0, 100}, /* light magenta */
87 {0, 100, 100}, /* light cyan */
88 {100, 100, 100}, /* white */
93 * Between console's palette and VGA's one:
94 * - blue and red are swapped (1 <-> 4)
95 * - yellow and cyan are swapped (3 <-> 6)
97 const int cons_to_vga_colors[NCOLORS] = {
98 0, 4, 2, 6, 1, 5, 3, 7,
99 8, 12, 10, 14, 9, 13, 11, 15
102 static const int vga_to_cons_colors[NCOLORS] = {
103 0, 1, 2, 3, 4, 5, 6, 7,
104 8, 9, 10, 11, 12, 13, 14, 15
107 struct text_pixel *screen_buffer;
109 static EFI_GRAPHICS_OUTPUT_BLT_PIXEL *GlyphBuffer;
111 static struct paletteentry *GlyphBuffer;
113 static size_t GlyphBufferSize;
115 static bool insert_font(char *, FONT_FLAGS);
116 static int font_set(struct env_var *, int, const void *);
117 static void * allocate_glyphbuffer(uint32_t, uint32_t);
118 static void gfx_fb_cursor_draw(teken_gfx_t *, const teken_pos_t *, bool);
121 * Initialize gfx framework.
124 gfx_framework_init(void)
127 * Setup font list to have builtin font.
129 (void) insert_font(NULL, FONT_BUILTIN);
133 gfx_get_fb_address(void)
135 return (ptov((uint32_t)gfx_state.tg_fb.fb_addr));
139 * Utility function to parse gfx mode line strings.
142 gfx_parse_mode_str(char *str, int *x, int *y, int *depth)
148 *x = strtoul(p, &end, 0);
149 if (*x == 0 || errno != 0)
154 *y = strtoul(p, &end, 0);
155 if (*y == 0 || errno != 0)
158 *depth = -1; /* auto select */
161 *depth = strtoul(p, &end, 0);
162 if (*depth == 0 || errno != 0 || *end != '\0')
170 rgb_color_map(uint8_t index, uint32_t rmax, int roffset,
171 uint32_t gmax, int goffset, uint32_t bmax, int boffset)
173 uint32_t color, code, gray, level;
175 if (index < NCOLORS) {
176 #define CF(_f, _i) ((_f ## max * color_def[(_i)]._f / 100) << _f ## offset)
177 return (CF(r, index) | CF(g, index) | CF(b, index));
181 #define CF(_f, _c) ((_f ## max & _c) << _f ## offset)
182 /* 6x6x6 color cube */
183 if (index > 15 && index < 232) {
184 uint32_t red, green, blue;
186 for (red = 0; red < 6; red++) {
187 for (green = 0; green < 6; green++) {
188 for (blue = 0; blue < 6; blue++) {
189 code = 16 + (red * 36) +
193 red = red ? (red * 40 + 55) : 0;
194 green = green ? (green * 40 + 55) : 0;
195 blue = blue ? (blue * 40 + 55) : 0;
197 color |= CF(g, green);
198 color |= CF(b, blue);
205 /* colors 232-255 are a grayscale ramp */
206 for (gray = 0; gray < 24; gray++) {
207 level = (gray * 10) + 8;
212 return (CF(r, level) | CF(g, level) | CF(b, level));
217 * Support for color mapping.
218 * For 8, 24 and 32 bit depth, use mask size 8.
219 * 15/16 bit depth needs to use mask size from mode,
220 * or we will lose color information from 32-bit to 15/16 bit translation.
223 gfx_fb_color_map(uint8_t index)
225 int rmask, gmask, bmask;
226 int roff, goff, boff, bpp;
228 roff = ffs(gfx_state.tg_fb.fb_mask_red) - 1;
229 goff = ffs(gfx_state.tg_fb.fb_mask_green) - 1;
230 boff = ffs(gfx_state.tg_fb.fb_mask_blue) - 1;
231 bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3;
234 rmask = gfx_state.tg_fb.fb_mask_red >> roff;
239 gmask = gfx_state.tg_fb.fb_mask_green >> goff;
244 bmask = gfx_state.tg_fb.fb_mask_blue >> boff;
248 return (rgb_color_map(index, rmask, 16, gmask, 8, bmask, 0));
251 /* Get indexed color */
253 rgb_to_color_index(uint8_t r, uint8_t g, uint8_t b)
256 uint32_t color, best, dist, k;
260 best = NCMAP * NCMAP * NCMAP;
261 for (k = 0; k < NCMAP; k++) {
262 diff = r - pe8[k].Red;
264 diff = g - pe8[k].Green;
266 diff = b - pe8[k].Blue;
288 generate_cons_palette(uint32_t *palette, int format,
289 uint32_t rmax, int roffset, uint32_t gmax, int goffset,
290 uint32_t bmax, int boffset)
295 case COLOR_FORMAT_VGA:
296 for (i = 0; i < NCOLORS; i++)
297 palette[i] = cons_to_vga_colors[i];
298 for (; i < NCMAP; i++)
301 case COLOR_FORMAT_RGB:
302 for (i = 0; i < NCMAP; i++)
303 palette[i] = rgb_color_map(i, rmax, roffset,
304 gmax, goffset, bmax, boffset);
314 gfx_mem_wr1(uint8_t *base, size_t size, uint32_t o, uint8_t v)
319 *(uint8_t *)(base + o) = v;
323 gfx_mem_wr2(uint8_t *base, size_t size, uint32_t o, uint16_t v)
328 *(uint16_t *)(base + o) = v;
332 gfx_mem_wr4(uint8_t *base, size_t size, uint32_t o, uint32_t v)
337 *(uint32_t *)(base + o) = v;
340 /* Our GFX Block transfer toolkit. */
341 static int gfxfb_blt_fill(void *BltBuffer,
342 uint32_t DestinationX, uint32_t DestinationY,
343 uint32_t Width, uint32_t Height)
346 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
348 struct paletteentry *p;
350 uint32_t data, bpp, pitch, y, x;
351 int roff, goff, boff;
354 uint8_t *destination;
356 if (BltBuffer == NULL)
359 if (DestinationY + Height > gfx_state.tg_fb.fb_height)
362 if (DestinationX + Width > gfx_state.tg_fb.fb_width)
365 if (Width == 0 || Height == 0)
369 roff = ffs(gfx_state.tg_fb.fb_mask_red) - 1;
370 goff = ffs(gfx_state.tg_fb.fb_mask_green) - 1;
371 boff = ffs(gfx_state.tg_fb.fb_mask_blue) - 1;
373 if (gfx_state.tg_fb.fb_bpp == 8) {
374 data = rgb_to_color_index(p->Red, p->Green, p->Blue);
377 (gfx_state.tg_fb.fb_mask_red >> roff)) << roff;
379 (gfx_state.tg_fb.fb_mask_green >> goff)) << goff;
381 (gfx_state.tg_fb.fb_mask_blue >> boff)) << boff;
384 bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3;
385 pitch = gfx_state.tg_fb.fb_stride * bpp;
386 destination = gfx_get_fb_address();
387 size = gfx_state.tg_fb.fb_size;
389 for (y = DestinationY; y < Height + DestinationY; y++) {
390 off = y * pitch + DestinationX * bpp;
391 for (x = 0; x < Width; x++) {
394 gfx_mem_wr1(destination, size, off,
396 cons_to_vga_colors[data] : data);
399 gfx_mem_wr2(destination, size, off, data);
402 gfx_mem_wr1(destination, size, off,
403 (data >> 16) & 0xff);
404 gfx_mem_wr1(destination, size, off + 1,
406 gfx_mem_wr1(destination, size, off + 2,
410 gfx_mem_wr4(destination, size, off, data);
421 gfxfb_blt_video_to_buffer(void *BltBuffer, uint32_t SourceX, uint32_t SourceY,
422 uint32_t DestinationX, uint32_t DestinationY,
423 uint32_t Width, uint32_t Height, uint32_t Delta)
426 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
428 struct paletteentry *p;
431 uint32_t bpp, pitch, copybytes;
433 uint8_t *source, *destination, *buffer, *sb;
434 uint8_t rm, rp, gm, gp, bm, bp;
437 if (BltBuffer == NULL)
440 if (SourceY + Height >
441 gfx_state.tg_fb.fb_height)
444 if (SourceX + Width > gfx_state.tg_fb.fb_width)
447 if (Width == 0 || Height == 0)
451 Delta = Width * sizeof (*p);
453 bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3;
454 pitch = gfx_state.tg_fb.fb_stride * bpp;
456 copybytes = Width * bpp;
458 rp = ffs(gfx_state.tg_fb.fb_mask_red) - 1;
459 gp = ffs(gfx_state.tg_fb.fb_mask_green) - 1;
460 bp = ffs(gfx_state.tg_fb.fb_mask_blue) - 1;
461 rm = gfx_state.tg_fb.fb_mask_red >> rp;
462 gm = gfx_state.tg_fb.fb_mask_green >> gp;
463 bm = gfx_state.tg_fb.fb_mask_blue >> bp;
465 /* If FB pixel format is BGRA, we can use direct copy. */
467 ffs(rm) - 1 == 8 && rp == 16 &&
468 ffs(gm) - 1 == 8 && gp == 8 &&
469 ffs(bm) - 1 == 8 && bp == 0;
474 buffer = malloc(copybytes);
479 for (sy = SourceY, dy = DestinationY; dy < Height + DestinationY;
481 off = sy * pitch + SourceX * bpp;
482 source = gfx_get_fb_address() + off;
485 destination = (uint8_t *)BltBuffer + dy * Delta +
486 DestinationX * sizeof (*p);
488 destination = buffer;
491 bcopy(source, destination, copybytes);
494 for (x = 0; x < Width; x++) {
497 p = (void *)((uint8_t *)BltBuffer +
499 (DestinationX + x) * sizeof (*p));
500 sb = buffer + x * bpp;
509 c = sb[0] << 16 | sb[1] << 8 | sb[2];
517 *(uint32_t *)p = gfx_fb_color_map(
519 vga_to_cons_colors[c] : c);
521 p->Red = (c >> rp) & rm;
522 p->Green = (c >> gp) & gm;
523 p->Blue = (c >> bp) & bm;
535 gfxfb_blt_buffer_to_video(void *BltBuffer, uint32_t SourceX, uint32_t SourceY,
536 uint32_t DestinationX, uint32_t DestinationY,
537 uint32_t Width, uint32_t Height, uint32_t Delta)
540 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
542 struct paletteentry *p;
545 uint32_t bpp, pitch, copybytes;
547 uint8_t *source, *destination, *buffer;
548 uint8_t rm, rp, gm, gp, bm, bp;
551 if (BltBuffer == NULL)
554 if (DestinationY + Height >
555 gfx_state.tg_fb.fb_height)
558 if (DestinationX + Width > gfx_state.tg_fb.fb_width)
561 if (Width == 0 || Height == 0)
565 Delta = Width * sizeof (*p);
567 bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3;
568 pitch = gfx_state.tg_fb.fb_stride * bpp;
570 copybytes = Width * bpp;
572 rp = ffs(gfx_state.tg_fb.fb_mask_red) - 1;
573 gp = ffs(gfx_state.tg_fb.fb_mask_green) - 1;
574 bp = ffs(gfx_state.tg_fb.fb_mask_blue) - 1;
575 rm = gfx_state.tg_fb.fb_mask_red >> rp;
576 gm = gfx_state.tg_fb.fb_mask_green >> gp;
577 bm = gfx_state.tg_fb.fb_mask_blue >> bp;
579 /* If FB pixel format is BGRA, we can use direct copy. */
581 ffs(rm) - 1 == 8 && rp == 16 &&
582 ffs(gm) - 1 == 8 && gp == 8 &&
583 ffs(bm) - 1 == 8 && bp == 0;
588 buffer = malloc(copybytes);
592 for (sy = SourceY, dy = DestinationY; sy < Height + SourceY;
594 off = dy * pitch + DestinationX * bpp;
595 destination = gfx_get_fb_address() + off;
598 source = (uint8_t *)BltBuffer + sy * Delta +
599 SourceX * sizeof (*p);
601 for (x = 0; x < Width; x++) {
604 p = (void *)((uint8_t *)BltBuffer +
606 (SourceX + x) * sizeof (*p));
608 c = rgb_to_color_index(p->Red,
611 c = (p->Red & rm) << rp |
612 (p->Green & gm) << gp |
613 (p->Blue & bm) << bp;
618 gfx_mem_wr1(buffer, copybytes,
620 cons_to_vga_colors[c] : c);
623 gfx_mem_wr2(buffer, copybytes,
627 gfx_mem_wr1(buffer, copybytes,
628 off, (c >> 16) & 0xff);
629 gfx_mem_wr1(buffer, copybytes,
630 off + 1, (c >> 8) & 0xff);
631 gfx_mem_wr1(buffer, copybytes,
635 gfx_mem_wr4(buffer, copybytes,
643 bcopy(source, destination, copybytes);
651 gfxfb_blt_video_to_video(uint32_t SourceX, uint32_t SourceY,
652 uint32_t DestinationX, uint32_t DestinationY,
653 uint32_t Width, uint32_t Height)
655 uint32_t bpp, copybytes;
657 uint8_t *source, *destination;
660 if (SourceY + Height >
661 gfx_state.tg_fb.fb_height)
664 if (SourceX + Width > gfx_state.tg_fb.fb_width)
667 if (DestinationY + Height >
668 gfx_state.tg_fb.fb_height)
671 if (DestinationX + Width > gfx_state.tg_fb.fb_width)
674 if (Width == 0 || Height == 0)
677 bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3;
678 pitch = gfx_state.tg_fb.fb_stride * bpp;
680 copybytes = Width * bpp;
682 off = SourceY * pitch + SourceX * bpp;
683 source = gfx_get_fb_address() + off;
684 off = DestinationY * pitch + DestinationX * bpp;
685 destination = gfx_get_fb_address() + off;
687 if ((uintptr_t)destination > (uintptr_t)source) {
688 source += Height * pitch;
689 destination += Height * pitch;
693 while (Height-- > 0) {
694 bcopy(source, destination, copybytes);
696 destination += pitch;
703 gfxfb_blt(void *BltBuffer, GFXFB_BLT_OPERATION BltOperation,
704 uint32_t SourceX, uint32_t SourceY,
705 uint32_t DestinationX, uint32_t DestinationY,
706 uint32_t Width, uint32_t Height, uint32_t Delta)
711 EFI_GRAPHICS_OUTPUT *gop = gfx_state.tg_private;
713 if (gop != NULL && (gop->Mode->Info->PixelFormat == PixelBltOnly ||
714 gfx_state.tg_fb.fb_addr == 0)) {
715 switch (BltOperation) {
716 case GfxFbBltVideoFill:
717 status = gop->Blt(gop, BltBuffer, EfiBltVideoFill,
718 SourceX, SourceY, DestinationX, DestinationY,
719 Width, Height, Delta);
722 case GfxFbBltVideoToBltBuffer:
723 status = gop->Blt(gop, BltBuffer,
724 EfiBltVideoToBltBuffer,
725 SourceX, SourceY, DestinationX, DestinationY,
726 Width, Height, Delta);
729 case GfxFbBltBufferToVideo:
730 status = gop->Blt(gop, BltBuffer, EfiBltBufferToVideo,
731 SourceX, SourceY, DestinationX, DestinationY,
732 Width, Height, Delta);
735 case GfxFbBltVideoToVideo:
736 status = gop->Blt(gop, BltBuffer, EfiBltVideoToVideo,
737 SourceX, SourceY, DestinationX, DestinationY,
738 Width, Height, Delta);
742 status = EFI_INVALID_PARAMETER;
751 case EFI_INVALID_PARAMETER:
755 case EFI_DEVICE_ERROR:
765 switch (BltOperation) {
766 case GfxFbBltVideoFill:
767 rv = gfxfb_blt_fill(BltBuffer, DestinationX, DestinationY,
771 case GfxFbBltVideoToBltBuffer:
772 rv = gfxfb_blt_video_to_buffer(BltBuffer, SourceX, SourceY,
773 DestinationX, DestinationY, Width, Height, Delta);
776 case GfxFbBltBufferToVideo:
777 rv = gfxfb_blt_buffer_to_video(BltBuffer, SourceX, SourceY,
778 DestinationX, DestinationY, Width, Height, Delta);
781 case GfxFbBltVideoToVideo:
782 rv = gfxfb_blt_video_to_video(SourceX, SourceY,
783 DestinationX, DestinationY, Width, Height);
794 gfx_bitblt_bitmap(teken_gfx_t *state, const uint8_t *glyph,
795 const teken_attr_t *a, uint32_t alpha, bool cursor)
797 uint32_t width, height;
798 uint32_t fgc, bgc, bpl, cc, o;
802 bpp = 4; /* We only generate BGRA */
803 width = state->tg_font.vf_width;
804 height = state->tg_font.vf_height;
805 bpl = (width + 7) / 8; /* Bytes per source line. */
809 if (a->ta_format & TF_BOLD)
811 if (a->ta_format & TF_BLINK)
814 fgc = gfx_fb_color_map(fgc);
815 bgc = gfx_fb_color_map(bgc);
817 if (a->ta_format & TF_REVERSE)
833 for (uint32_t y = 0; y < height; y++) {
834 for (uint32_t x = 0; x < width; x++) {
835 byte = y * bpl + x / 8;
836 bit = 0x80 >> (x % 8);
837 o = y * width * bpp + x * bpp;
838 cc = glyph[byte] & bit ? fgc : bgc;
840 gfx_mem_wr4(state->tg_glyph,
841 state->tg_glyph_size, o, cc);
847 * Draw prepared glyph on terminal point p.
850 gfx_fb_printchar(teken_gfx_t *state, const teken_pos_t *p)
852 unsigned x, y, width, height;
854 width = state->tg_font.vf_width;
855 height = state->tg_font.vf_height;
856 x = state->tg_origin.tp_col + p->tp_col * width;
857 y = state->tg_origin.tp_row + p->tp_row * height;
859 gfx_fb_cons_display(x, y, width, height, state->tg_glyph);
863 * Store char with its attribute to buffer and put it on screen.
866 gfx_fb_putchar(void *arg, const teken_pos_t *p, teken_char_t c,
867 const teken_attr_t *a)
869 teken_gfx_t *state = arg;
870 const uint8_t *glyph;
873 idx = p->tp_col + p->tp_row * state->tg_tp.tp_col;
874 if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row)
877 /* remove the cursor */
878 if (state->tg_cursor_visible)
879 gfx_fb_cursor_draw(state, &state->tg_cursor, false);
881 screen_buffer[idx].c = c;
882 screen_buffer[idx].a = *a;
884 glyph = font_lookup(&state->tg_font, c, a);
885 gfx_bitblt_bitmap(state, glyph, a, 0xff, false);
886 gfx_fb_printchar(state, p);
888 /* display the cursor */
889 if (state->tg_cursor_visible) {
890 const teken_pos_t *c;
892 c = teken_get_cursor(&state->tg_teken);
893 gfx_fb_cursor_draw(state, c, true);
898 gfx_fb_fill(void *arg, const teken_rect_t *r, teken_char_t c,
899 const teken_attr_t *a)
901 teken_gfx_t *state = arg;
902 const uint8_t *glyph;
904 struct text_pixel *row;
906 /* remove the cursor */
907 if (state->tg_cursor_visible)
908 gfx_fb_cursor_draw(state, &state->tg_cursor, false);
910 glyph = font_lookup(&state->tg_font, c, a);
911 gfx_bitblt_bitmap(state, glyph, a, 0xff, false);
913 for (p.tp_row = r->tr_begin.tp_row; p.tp_row < r->tr_end.tp_row;
915 row = &screen_buffer[p.tp_row * state->tg_tp.tp_col];
916 for (p.tp_col = r->tr_begin.tp_col;
917 p.tp_col < r->tr_end.tp_col; p.tp_col++) {
919 row[p.tp_col].a = *a;
920 gfx_fb_printchar(state, &p);
924 /* display the cursor */
925 if (state->tg_cursor_visible) {
926 const teken_pos_t *c;
928 c = teken_get_cursor(&state->tg_teken);
929 gfx_fb_cursor_draw(state, c, true);
934 gfx_fb_cursor_draw(teken_gfx_t *state, const teken_pos_t *p, bool on)
936 const uint8_t *glyph;
939 idx = p->tp_col + p->tp_row * state->tg_tp.tp_col;
940 if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row)
943 glyph = font_lookup(&state->tg_font, screen_buffer[idx].c,
944 &screen_buffer[idx].a);
945 gfx_bitblt_bitmap(state, glyph, &screen_buffer[idx].a, 0xff, on);
946 gfx_fb_printchar(state, p);
947 state->tg_cursor = *p;
951 gfx_fb_cursor(void *arg, const teken_pos_t *p)
953 teken_gfx_t *state = arg;
957 tpl = BS->RaiseTPL(TPL_NOTIFY);
960 /* Switch cursor off in old location and back on in new. */
961 if (state->tg_cursor_visible) {
962 gfx_fb_cursor_draw(state, &state->tg_cursor, false);
963 gfx_fb_cursor_draw(state, p, true);
971 gfx_fb_param(void *arg, int cmd, unsigned int value)
973 teken_gfx_t *state = arg;
974 const teken_pos_t *c;
977 case TP_SETLOCALCURSOR:
979 * 0 means normal (usually block), 1 means hidden, and
980 * 2 means blinking (always block) for compatibility with
981 * syscons. We don't support any changes except hiding,
982 * so must map 2 to 0.
984 value = (value == 1) ? 0 : 1;
987 c = teken_get_cursor(&state->tg_teken);
988 gfx_fb_cursor_draw(state, c, true);
990 state->tg_cursor_visible = true;
992 state->tg_cursor_visible = false;
995 /* Not yet implemented */
1001 is_same_pixel(struct text_pixel *px1, struct text_pixel *px2)
1003 if (px1->c != px2->c)
1006 /* Is there image stored? */
1007 if ((px1->a.ta_format & TF_IMAGE) ||
1008 (px2->a.ta_format & TF_IMAGE))
1011 if (px1->a.ta_format != px2->a.ta_format)
1013 if (px1->a.ta_fgcolor != px2->a.ta_fgcolor)
1015 if (px1->a.ta_bgcolor != px2->a.ta_bgcolor)
1022 gfx_fb_copy_area(teken_gfx_t *state, const teken_rect_t *s,
1023 const teken_pos_t *d)
1025 uint32_t sx, sy, dx, dy, width, height;
1027 width = state->tg_font.vf_width;
1028 height = state->tg_font.vf_height;
1030 sx = state->tg_origin.tp_col + s->tr_begin.tp_col * width;
1031 sy = state->tg_origin.tp_row + s->tr_begin.tp_row * height;
1032 dx = state->tg_origin.tp_col + d->tp_col * width;
1033 dy = state->tg_origin.tp_row + d->tp_row * height;
1035 width *= (s->tr_end.tp_col - s->tr_begin.tp_col + 1);
1037 (void) gfxfb_blt(NULL, GfxFbBltVideoToVideo, sx, sy, dx, dy,
1042 gfx_fb_copy_line(teken_gfx_t *state, int ncol, teken_pos_t *s, teken_pos_t *d)
1046 unsigned soffset, doffset;
1050 soffset = s->tp_col + s->tp_row * state->tg_tp.tp_col;
1051 doffset = d->tp_col + d->tp_row * state->tg_tp.tp_col;
1053 for (x = 0; x < ncol; x++) {
1054 if (is_same_pixel(&screen_buffer[soffset + x],
1055 &screen_buffer[doffset + x])) {
1057 gfx_fb_copy_area(state, &sr, &dp);
1061 screen_buffer[doffset + x] = screen_buffer[soffset + x];
1063 /* update end point */
1064 sr.tr_end.tp_col = s->tp_col + x;;
1066 /* set up new rectangle */
1068 sr.tr_begin.tp_col = s->tp_col + x;
1069 sr.tr_begin.tp_row = s->tp_row;
1070 sr.tr_end.tp_col = s->tp_col + x;
1071 sr.tr_end.tp_row = s->tp_row;
1072 dp.tp_col = d->tp_col + x;
1073 dp.tp_row = d->tp_row;
1078 gfx_fb_copy_area(state, &sr, &dp);
1083 gfx_fb_copy(void *arg, const teken_rect_t *r, const teken_pos_t *p)
1085 teken_gfx_t *state = arg;
1086 unsigned doffset, soffset;
1088 int nrow, ncol, y; /* Has to be signed - >= 0 comparison */
1091 * Copying is a little tricky. We must make sure we do it in
1092 * correct order, to make sure we don't overwrite our own data.
1095 nrow = r->tr_end.tp_row - r->tr_begin.tp_row;
1096 ncol = r->tr_end.tp_col - r->tr_begin.tp_col;
1098 if (p->tp_row + nrow > state->tg_tp.tp_row ||
1099 p->tp_col + ncol > state->tg_tp.tp_col)
1102 soffset = r->tr_begin.tp_col + r->tr_begin.tp_row * state->tg_tp.tp_col;
1103 doffset = p->tp_col + p->tp_row * state->tg_tp.tp_col;
1105 /* remove the cursor */
1106 if (state->tg_cursor_visible)
1107 gfx_fb_cursor_draw(state, &state->tg_cursor, false);
1110 * Copy line by line.
1112 if (doffset <= soffset) {
1115 for (y = 0; y < nrow; y++) {
1116 s.tp_row = r->tr_begin.tp_row + y;
1117 d.tp_row = p->tp_row + y;
1119 gfx_fb_copy_line(state, ncol, &s, &d);
1122 for (y = nrow - 1; y >= 0; y--) {
1123 s.tp_row = r->tr_begin.tp_row + y;
1124 d.tp_row = p->tp_row + y;
1126 gfx_fb_copy_line(state, ncol, &s, &d);
1130 /* display the cursor */
1131 if (state->tg_cursor_visible) {
1132 const teken_pos_t *c;
1134 c = teken_get_cursor(&state->tg_teken);
1135 gfx_fb_cursor_draw(state, c, true);
1140 * Implements alpha blending for RGBA data, could use pixels for arguments,
1141 * but byte stream seems more generic.
1142 * The generic alpha blending is:
1143 * blend = alpha * fg + (1.0 - alpha) * bg.
1144 * Since our alpha is not from range [0..1], we scale appropriately.
1147 alpha_blend(uint8_t fg, uint8_t bg, uint8_t alpha)
1149 uint16_t blend, h, l;
1151 /* trivial corner cases */
1156 blend = (alpha * fg + (0xFF - alpha) * bg);
1157 /* Division by 0xFF */
1166 * Implements alpha blending for RGBA data, could use pixels for arguments,
1167 * but byte stream seems more generic.
1168 * The generic alpha blending is:
1169 * blend = alpha * fg + (1.0 - alpha) * bg.
1170 * Since our alpha is not from range [0..1], we scale appropriately.
1173 bitmap_cpy(void *dst, void *src, uint32_t size)
1176 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *ps, *pd;
1178 struct paletteentry *ps, *pd;
1187 * we only implement alpha blending for depth 32.
1189 for (i = 0; i < size; i ++) {
1191 pd[i].Red = alpha_blend(ps[i].Red, pd[i].Red, a);
1192 pd[i].Green = alpha_blend(ps[i].Green, pd[i].Green, a);
1193 pd[i].Blue = alpha_blend(ps[i].Blue, pd[i].Blue, a);
1199 allocate_glyphbuffer(uint32_t width, uint32_t height)
1203 size = sizeof (*GlyphBuffer) * width * height;
1204 if (size != GlyphBufferSize) {
1206 GlyphBuffer = malloc(size);
1207 if (GlyphBuffer == NULL)
1209 GlyphBufferSize = size;
1211 return (GlyphBuffer);
1215 gfx_fb_cons_display(uint32_t x, uint32_t y, uint32_t width, uint32_t height,
1219 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *buf;
1221 struct paletteentry *buf;
1225 size = width * height * sizeof(*buf);
1228 * Common data to display is glyph, use preallocated
1231 if (gfx_state.tg_glyph_size != GlyphBufferSize)
1232 (void) allocate_glyphbuffer(width, height);
1234 if (size == GlyphBufferSize)
1241 if (gfxfb_blt(buf, GfxFbBltVideoToBltBuffer, x, y, 0, 0,
1242 width, height, 0) == 0) {
1243 bitmap_cpy(buf, data, width * height);
1244 (void) gfxfb_blt(buf, GfxFbBltBufferToVideo, 0, 0, x, y,
1247 if (buf != GlyphBuffer)
1252 * Public graphics primitives.
1261 /* "bit" starts at the highest power of four <= the argument. */
1266 if (num >= res + bit) {
1268 res = (res >> 1) + bit;
1277 /* set pixel in framebuffer using gfx coordinates */
1279 gfx_fb_setpixel(uint32_t x, uint32_t y)
1282 const teken_attr_t *ap;
1284 if (gfx_state.tg_fb_type == FB_TEXT)
1287 ap = teken_get_curattr(&gfx_state.tg_teken);
1288 if (ap->ta_format & TF_REVERSE) {
1290 if (ap->ta_format & TF_BLINK)
1294 if (ap->ta_format & TF_BOLD)
1298 c = gfx_fb_color_map(c);
1300 if (x >= gfx_state.tg_fb.fb_width ||
1301 y >= gfx_state.tg_fb.fb_height)
1304 gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x, y, 1, 1, 0);
1308 * draw rectangle in framebuffer using gfx coordinates.
1309 * The function is borrowed from vt_fb.c
1312 gfx_fb_drawrect(uint32_t x1, uint32_t y1, uint32_t x2, uint32_t y2,
1317 if (gfx_state.tg_fb_type == FB_TEXT)
1320 for (y = y1; y <= y2; y++) {
1321 if (fill || (y == y1) || (y == y2)) {
1322 for (x = x1; x <= x2; x++)
1323 gfx_fb_setpixel(x, y);
1325 gfx_fb_setpixel(x1, y);
1326 gfx_fb_setpixel(x2, y);
1332 gfx_fb_line(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t wd)
1335 int err, e2, x2, y2, ed, width;
1337 if (gfx_state.tg_fb_type == FB_TEXT)
1341 sx = x0 < x1? 1 : -1;
1342 sy = y0 < y1? 1 : -1;
1343 dx = x1 > x0? x1 - x0 : x0 - x1;
1344 dy = y1 > y0? y1 - y0 : y0 - y1;
1346 ed = dx + dy == 0 ? 1: isqrt(dx * dx + dy * dy);
1349 gfx_fb_setpixel(x0, y0);
1352 if ((e2 << 1) >= -dx) { /* x step */
1355 while (e2 < ed * width &&
1356 (y1 != (uint32_t)y2 || dx > dy)) {
1358 gfx_fb_setpixel(x0, y2);
1367 if ((e2 << 1) <= dy) { /* y step */
1369 while (e2 < ed * width &&
1370 (x1 != (uint32_t)x2 || dx < dy)) {
1372 gfx_fb_setpixel(x2, y0);
1384 * quadratic Bézier curve limited to gradients without sign change.
1387 gfx_fb_bezier(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t x2,
1388 uint32_t y2, uint32_t wd)
1390 int sx, sy, xx, yy, xy, width;
1391 int dx, dy, err, curvature;
1394 if (gfx_state.tg_fb_type == FB_TEXT)
1402 curvature = xx*sy - yy*sx;
1404 if (sx*sx + sy*sy > xx*xx+yy*yy) {
1409 curvature = -curvature;
1411 if (curvature != 0) {
1413 sx = x0 < x2? 1 : -1;
1416 sy = y0 < y2? 1 : -1;
1421 if (curvature * sx * sy < 0) {
1425 curvature = -curvature;
1427 dx = 4 * sy * curvature * (x1 - x0) + xx - xy;
1428 dy = 4 * sx * curvature * (y0 - y1) + yy - xy;
1433 for (i = 0; i <= width; i++)
1434 gfx_fb_setpixel(x0 + i, y0);
1435 if (x0 == x2 && y0 == y2)
1436 return; /* last pixel -> curve finished */
1450 } while (dy < dx); /* gradient negates -> algorithm fails */
1452 gfx_fb_line(x0, y0, x2, y2, width);
1456 * draw rectangle using terminal coordinates and current foreground color.
1459 gfx_term_drawrect(uint32_t ux1, uint32_t uy1, uint32_t ux2, uint32_t uy2)
1464 uint32_t vf_width, vf_height;
1467 if (gfx_state.tg_fb_type == FB_TEXT)
1470 vf_width = gfx_state.tg_font.vf_width;
1471 vf_height = gfx_state.tg_font.vf_height;
1472 width = vf_width / 4; /* line width */
1473 xshift = (vf_width - width) / 2;
1474 yshift = (vf_height - width) / 2;
1476 /* Shift coordinates */
1484 /* mark area used in terminal */
1485 r.tr_begin.tp_col = ux1;
1486 r.tr_begin.tp_row = uy1;
1487 r.tr_end.tp_col = ux2 + 1;
1488 r.tr_end.tp_row = uy2 + 1;
1490 term_image_display(&gfx_state, &r);
1493 * Draw horizontal lines width points thick, shifted from outer edge.
1495 x1 = (ux1 + 1) * vf_width + gfx_state.tg_origin.tp_col;
1496 y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row + yshift;
1497 x2 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
1498 gfx_fb_drawrect(x1, y1, x2, y1 + width, 1);
1499 y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
1500 y2 += vf_height - yshift - width;
1501 gfx_fb_drawrect(x1, y2, x2, y2 + width, 1);
1504 * Draw vertical lines width points thick, shifted from outer edge.
1506 x1 = ux1 * vf_width + gfx_state.tg_origin.tp_col + xshift;
1507 y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row;
1509 y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
1510 gfx_fb_drawrect(x1, y1, x1 + width, y2, 1);
1511 x1 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
1512 x1 += vf_width - xshift - width;
1513 gfx_fb_drawrect(x1, y1, x1 + width, y2, 1);
1515 /* Draw upper left corner. */
1516 x1 = ux1 * vf_width + gfx_state.tg_origin.tp_col + xshift;
1517 y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row;
1520 x2 = ux1 * vf_width + gfx_state.tg_origin.tp_col;
1522 y2 = uy1 * vf_height + gfx_state.tg_origin.tp_row + yshift;
1523 for (i = 0; i <= width; i++)
1524 gfx_fb_bezier(x1 + i, y1, x1 + i, y2 + i, x2, y2 + i, width-i);
1526 /* Draw lower left corner. */
1527 x1 = ux1 * vf_width + gfx_state.tg_origin.tp_col;
1529 y1 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
1530 y1 += vf_height - yshift;
1531 x2 = ux1 * vf_width + gfx_state.tg_origin.tp_col + xshift;
1532 y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
1533 for (i = 0; i <= width; i++)
1534 gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i);
1536 /* Draw upper right corner. */
1537 x1 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
1538 y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row + yshift;
1539 x2 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
1540 x2 += vf_width - xshift - width;
1541 y2 = uy1 * vf_height + gfx_state.tg_origin.tp_row;
1543 for (i = 0; i <= width; i++)
1544 gfx_fb_bezier(x1, y1 + i, x2 + i, y1 + i, x2 + i, y2, width-i);
1546 /* Draw lower right corner. */
1547 x1 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
1548 y1 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
1549 y1 += vf_height - yshift;
1550 x2 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
1551 x2 += vf_width - xshift - width;
1552 y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
1553 for (i = 0; i <= width; i++)
1554 gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i);
1558 gfx_fb_putimage(png_t *png, uint32_t ux1, uint32_t uy1, uint32_t ux2,
1559 uint32_t uy2, uint32_t flags)
1562 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
1564 struct paletteentry *p;
1567 uint32_t i, j, x, y, fheight, fwidth;
1574 trace = (flags & FL_PUTIMAGE_DEBUG) != 0;
1576 if (gfx_state.tg_fb_type == FB_TEXT) {
1578 printf("Framebuffer not active.\n");
1582 if (png->color_type != PNG_TRUECOLOR_ALPHA) {
1584 printf("Not truecolor image.\n");
1588 if (ux1 > gfx_state.tg_fb.fb_width ||
1589 uy1 > gfx_state.tg_fb.fb_height) {
1591 printf("Top left coordinate off screen.\n");
1595 if (png->width > UINT16_MAX || png->height > UINT16_MAX) {
1597 printf("Image too large.\n");
1601 if (png->width < 1 || png->height < 1) {
1603 printf("Image too small.\n");
1608 * If 0 was passed for either ux2 or uy2, then calculate the missing
1609 * part of the bottom right coordinate.
1612 if (ux2 == 0 && uy2 == 0) {
1613 /* Both 0, use the native resolution of the image */
1614 ux2 = ux1 + png->width;
1615 uy2 = uy1 + png->height;
1617 } else if (ux2 == 0) {
1618 /* Set ux2 from uy2/uy1 to maintain aspect ratio */
1619 ux2 = ux1 + (png->width * (uy2 - uy1)) / png->height;
1620 } else if (uy2 == 0) {
1621 /* Set uy2 from ux2/ux1 to maintain aspect ratio */
1622 uy2 = uy1 + (png->height * (ux2 - ux1)) / png->width;
1625 if (ux2 > gfx_state.tg_fb.fb_width ||
1626 uy2 > gfx_state.tg_fb.fb_height) {
1628 printf("Bottom right coordinate off screen.\n");
1633 fheight = uy2 - uy1;
1636 * If the original image dimensions have been passed explicitly,
1639 if (fwidth == png->width && fheight == png->height)
1644 * No top left X co-ordinate (real coordinates start at 1),
1645 * place as far right as it will fit.
1647 ux2 = gfx_state.tg_fb.fb_width - gfx_state.tg_origin.tp_col;
1653 * No top left Y co-ordinate (real coordinates start at 1),
1654 * place as far down as it will fit.
1656 uy2 = gfx_state.tg_fb.fb_height - gfx_state.tg_origin.tp_row;
1657 uy1 = uy2 - fheight;
1660 if (ux1 >= ux2 || uy1 >= uy2) {
1662 printf("Image dimensions reversed.\n");
1666 if (fwidth < 2 || fheight < 2) {
1668 printf("Target area too small\n");
1673 printf("Image %ux%u -> %ux%u @%ux%u\n",
1674 png->width, png->height, fwidth, fheight, ux1, uy1);
1676 rect.tr_begin.tp_col = ux1 / gfx_state.tg_font.vf_width;
1677 rect.tr_begin.tp_row = uy1 / gfx_state.tg_font.vf_height;
1678 rect.tr_end.tp_col = (ux1 + fwidth) / gfx_state.tg_font.vf_width;
1679 rect.tr_end.tp_row = (uy1 + fheight) / gfx_state.tg_font.vf_height;
1682 * mark area used in terminal
1684 if (!(flags & FL_PUTIMAGE_NOSCROLL))
1685 term_image_display(&gfx_state, &rect);
1687 if ((flags & FL_PUTIMAGE_BORDER))
1688 gfx_fb_drawrect(ux1, uy1, ux2, uy2, 0);
1690 data = malloc(fwidth * fheight * sizeof(*p));
1694 printf("Out of memory.\n");
1699 * Build image for our framebuffer.
1702 /* Helper to calculate the pixel index from the source png */
1703 #define GETPIXEL(xx, yy) (((yy) * png->width + (xx)) * png->bpp)
1706 * For each of the x and y directions, calculate the number of pixels
1707 * in the source image that correspond to a single pixel in the target.
1708 * Use fixed-point arithmetic with 16-bits for each of the integer and
1711 const uint32_t wcstep = ((png->width - 1) << 16) / (fwidth - 1);
1712 const uint32_t hcstep = ((png->height - 1) << 16) / (fheight - 1);
1714 rs = 8 - (fls(gfx_state.tg_fb.fb_mask_red) -
1715 ffs(gfx_state.tg_fb.fb_mask_red) + 1);
1716 gs = 8 - (fls(gfx_state.tg_fb.fb_mask_green) -
1717 ffs(gfx_state.tg_fb.fb_mask_green) + 1);
1718 bs = 8 - (fls(gfx_state.tg_fb.fb_mask_blue) -
1719 ffs(gfx_state.tg_fb.fb_mask_blue) + 1);
1722 for (y = 0; y < fheight; y++) {
1723 uint32_t hc2 = (hc >> 9) & 0x7f;
1724 uint32_t hc1 = 0x80 - hc2;
1726 uint32_t offset_y = hc >> 16;
1727 uint32_t offset_y1 = offset_y + 1;
1730 for (x = 0; x < fwidth; x++) {
1731 uint32_t wc2 = (wc >> 9) & 0x7f;
1732 uint32_t wc1 = 0x80 - wc2;
1734 uint32_t offset_x = wc >> 16;
1735 uint32_t offset_x1 = offset_x + 1;
1737 /* Target pixel index */
1743 g = png->image[i + 1];
1744 b = png->image[i + 2];
1745 a = png->image[i + 3];
1749 uint32_t p00 = GETPIXEL(offset_x, offset_y);
1750 uint32_t p01 = GETPIXEL(offset_x, offset_y1);
1751 uint32_t p10 = GETPIXEL(offset_x1, offset_y);
1752 uint32_t p11 = GETPIXEL(offset_x1, offset_y1);
1755 * Given a 2x2 array of pixels in the source
1756 * image, combine them to produce a single
1757 * value for the pixel in the target image.
1758 * Each column of pixels is combined using
1759 * a weighted average where the top and bottom
1760 * pixels contribute hc1 and hc2 respectively.
1761 * The calculation for bottom pixel pB and
1763 * (pT * hc1 + pB * hc2) / (hc1 + hc2)
1764 * Once the values are determined for the two
1765 * columns of pixels, then the columns are
1766 * averaged together in the same way but using
1767 * wc1 and wc2 for the weightings.
1769 * Since hc1 and hc2 are chosen so that
1770 * hc1 + hc2 == 128 (and same for wc1 + wc2),
1771 * the >> 14 below is a quick way to divide by
1772 * (hc1 + hc2) * (wc1 + wc2)
1774 for (i = 0; i < 4; i++)
1776 (png->image[p00 + i] * hc1 +
1777 png->image[p01 + i] * hc2) * wc1 +
1778 (png->image[p10 + i] * hc1 +
1779 png->image[p11 + i] * hc2) * wc2)
1789 printf("r/g/b: %x/%x/%x\n", r, g, b);
1791 * Rough colorspace reduction for 15/16 bit colors.
1794 p[j].Green = g >> gs;
1795 p[j].Blue = b >> bs;
1803 gfx_fb_cons_display(ux1, uy1, fwidth, fheight, data);
1809 * Reset font flags to FONT_AUTO.
1812 reset_font_flags(void)
1814 struct fontlist *fl;
1816 STAILQ_FOREACH(fl, &fonts, font_next) {
1817 fl->font_flags = FONT_AUTO;
1821 static vt_font_bitmap_data_t *
1822 set_font(teken_unit_t *rows, teken_unit_t *cols, teken_unit_t h, teken_unit_t w)
1824 vt_font_bitmap_data_t *font = NULL;
1825 struct fontlist *fl;
1826 unsigned height = h;
1830 * First check for manually loaded font.
1832 STAILQ_FOREACH(fl, &fonts, font_next) {
1833 if (fl->font_flags == FONT_MANUAL) {
1834 font = fl->font_data;
1835 if (font->vfbd_font == NULL && fl->font_load != NULL &&
1836 fl->font_name != NULL) {
1837 font = fl->font_load(fl->font_name);
1839 if (font == NULL || font->vfbd_font == NULL)
1846 *rows = (height - BORDER_PIXELS) / font->vfbd_height;
1847 *cols = (width - BORDER_PIXELS) / font->vfbd_width;
1852 * Find best font for these dimensions, or use default
1854 * A 1 pixel border is the absolute minimum we could have
1855 * as a border around the text window (BORDER_PIXELS = 2),
1856 * however a slightly larger border not only looks better
1857 * but for the fonts currently statically built into the
1858 * emulator causes much better font selection for the
1859 * normal range of screen resolutions.
1861 STAILQ_FOREACH(fl, &fonts, font_next) {
1862 font = fl->font_data;
1863 if ((((*rows * font->vfbd_height) + BORDER_PIXELS) <= height) &&
1864 (((*cols * font->vfbd_width) + BORDER_PIXELS) <= width)) {
1865 if (font->vfbd_font == NULL ||
1866 fl->font_flags == FONT_RELOAD) {
1867 if (fl->font_load != NULL &&
1868 fl->font_name != NULL) {
1869 font = fl->font_load(fl->font_name);
1874 *rows = (height - BORDER_PIXELS) / font->vfbd_height;
1875 *cols = (width - BORDER_PIXELS) / font->vfbd_width;
1883 * We have fonts sorted smallest last, try it before
1884 * falling back to builtin.
1886 fl = STAILQ_LAST(&fonts, fontlist, font_next);
1887 if (fl != NULL && fl->font_load != NULL &&
1888 fl->font_name != NULL) {
1889 font = fl->font_load(fl->font_name);
1892 font = &DEFAULT_FONT_DATA;
1894 *rows = (height - BORDER_PIXELS) / font->vfbd_height;
1895 *cols = (width - BORDER_PIXELS) / font->vfbd_width;
1904 char clear[] = { '\033', 'c' };
1906 /* Reset terminal */
1907 teken_input(&gfx_state.tg_teken, clear, sizeof(clear));
1908 gfx_state.tg_functions->tf_param(&gfx_state, TP_SHOWCURSOR, 0);
1912 setup_font(teken_gfx_t *state, teken_unit_t height, teken_unit_t width)
1914 vt_font_bitmap_data_t *font_data;
1915 teken_pos_t *tp = &state->tg_tp;
1920 * set_font() will select a appropriate sized font for
1921 * the number of rows and columns selected. If we don't
1922 * have a font that will fit, then it will use the
1923 * default builtin font and adjust the rows and columns
1924 * to fit on the screen.
1926 font_data = set_font(&tp->tp_row, &tp->tp_col, height, width);
1928 if (font_data == NULL)
1929 panic("out of memory");
1931 for (i = 0; i < VFNT_MAPS; i++) {
1932 state->tg_font.vf_map[i] =
1933 font_data->vfbd_font->vf_map[i];
1934 state->tg_font.vf_map_count[i] =
1935 font_data->vfbd_font->vf_map_count[i];
1938 state->tg_font.vf_bytes = font_data->vfbd_font->vf_bytes;
1939 state->tg_font.vf_height = font_data->vfbd_font->vf_height;
1940 state->tg_font.vf_width = font_data->vfbd_font->vf_width;
1942 snprintf(env, sizeof (env), "%ux%u",
1943 state->tg_font.vf_width, state->tg_font.vf_height);
1944 env_setenv("screen.font", EV_VOLATILE | EV_NOHOOK,
1945 env, font_set, env_nounset);
1948 /* Binary search for the glyph. Return 0 if not found. */
1950 font_bisearch(const vfnt_map_t *map, uint32_t len, teken_char_t src)
1952 unsigned min, mid, max;
1957 /* Empty font map. */
1960 /* Character below minimal entry. */
1961 if (src < map[0].vfm_src)
1963 /* Optimization: ASCII characters occur very often. */
1964 if (src <= map[0].vfm_src + map[0].vfm_len)
1965 return (src - map[0].vfm_src + map[0].vfm_dst);
1966 /* Character above maximum entry. */
1967 if (src > map[max].vfm_src + map[max].vfm_len)
1970 /* Binary search. */
1971 while (max >= min) {
1972 mid = (min + max) / 2;
1973 if (src < map[mid].vfm_src)
1975 else if (src > map[mid].vfm_src + map[mid].vfm_len)
1978 return (src - map[mid].vfm_src + map[mid].vfm_dst);
1985 * Return glyph bitmap. If glyph is not found, we will return bitmap
1986 * for the first (offset 0) glyph.
1989 font_lookup(const struct vt_font *vf, teken_char_t c, const teken_attr_t *a)
1994 /* Substitute bold with normal if not found. */
1995 if (a->ta_format & TF_BOLD) {
1996 dst = font_bisearch(vf->vf_map[VFNT_MAP_BOLD],
1997 vf->vf_map_count[VFNT_MAP_BOLD], c);
2001 dst = font_bisearch(vf->vf_map[VFNT_MAP_NORMAL],
2002 vf->vf_map_count[VFNT_MAP_NORMAL], c);
2005 stride = howmany(vf->vf_width, 8) * vf->vf_height;
2006 return (&vf->vf_bytes[dst * stride]);
2010 load_mapping(int fd, struct vt_font *fp, int n)
2016 if (fp->vf_map_count[n] == 0)
2019 size = fp->vf_map_count[n] * sizeof(*mp);
2025 rv = read(fd, mp, size);
2026 if (rv < 0 || (size_t)rv != size) {
2027 free(fp->vf_map[n]);
2028 fp->vf_map[n] = NULL;
2032 for (i = 0; i < fp->vf_map_count[n]; i++) {
2033 mp[i].vfm_src = be32toh(mp[i].vfm_src);
2034 mp[i].vfm_dst = be16toh(mp[i].vfm_dst);
2035 mp[i].vfm_len = be16toh(mp[i].vfm_len);
2041 builtin_mapping(struct vt_font *fp, int n)
2044 struct vfnt_map *mp;
2049 if (fp->vf_map_count[n] == 0)
2052 size = fp->vf_map_count[n] * sizeof(*mp);
2058 memcpy(mp, DEFAULT_FONT_DATA.vfbd_font->vf_map[n], size);
2063 * Load font from builtin or from file.
2064 * We do need special case for builtin because the builtin font glyphs
2065 * are compressed and we do need to uncompress them.
2066 * Having single load_font() for both cases will help us to simplify
2067 * font switch handling.
2069 static vt_font_bitmap_data_t *
2070 load_font(char *path)
2074 struct font_header fh;
2075 struct fontlist *fl;
2076 vt_font_bitmap_data_t *bp;
2081 /* Get our entry from the font list. */
2082 STAILQ_FOREACH(fl, &fonts, font_next) {
2083 if (strcmp(fl->font_name, path) == 0)
2087 return (NULL); /* Should not happen. */
2090 if (bp->vfbd_font != NULL && fl->font_flags != FONT_RELOAD)
2095 * Special case for builtin font.
2096 * Builtin font is the very first font we load, we do not have
2097 * previous loads to be released.
2099 if (fl->font_flags == FONT_BUILTIN) {
2100 if ((fp = calloc(1, sizeof(struct vt_font))) == NULL)
2103 fp->vf_width = DEFAULT_FONT_DATA.vfbd_width;
2104 fp->vf_height = DEFAULT_FONT_DATA.vfbd_height;
2106 fp->vf_bytes = malloc(DEFAULT_FONT_DATA.vfbd_uncompressed_size);
2107 if (fp->vf_bytes == NULL) {
2112 bp->vfbd_uncompressed_size =
2113 DEFAULT_FONT_DATA.vfbd_uncompressed_size;
2114 bp->vfbd_compressed_size =
2115 DEFAULT_FONT_DATA.vfbd_compressed_size;
2117 if (lz4_decompress(DEFAULT_FONT_DATA.vfbd_compressed_data,
2119 DEFAULT_FONT_DATA.vfbd_compressed_size,
2120 DEFAULT_FONT_DATA.vfbd_uncompressed_size, 0) != 0) {
2126 for (i = 0; i < VFNT_MAPS; i++) {
2127 fp->vf_map_count[i] =
2128 DEFAULT_FONT_DATA.vfbd_font->vf_map_count[i];
2129 if (builtin_mapping(fp, i) != 0)
2137 fd = open(path, O_RDONLY);
2142 rv = read(fd, &fh, size);
2143 if (rv < 0 || (size_t)rv != size) {
2147 if (memcmp(fh.fh_magic, FONT_HEADER_MAGIC, sizeof(fh.fh_magic)) != 0) {
2151 if ((fp = calloc(1, sizeof(struct vt_font))) == NULL) {
2155 for (i = 0; i < VFNT_MAPS; i++)
2156 fp->vf_map_count[i] = be32toh(fh.fh_map_count[i]);
2158 glyphs = be32toh(fh.fh_glyph_count);
2159 fp->vf_width = fh.fh_width;
2160 fp->vf_height = fh.fh_height;
2162 size = howmany(fp->vf_width, 8) * fp->vf_height * glyphs;
2163 bp->vfbd_uncompressed_size = size;
2164 if ((fp->vf_bytes = malloc(size)) == NULL)
2167 rv = read(fd, fp->vf_bytes, size);
2168 if (rv < 0 || (size_t)rv != size)
2170 for (i = 0; i < VFNT_MAPS; i++) {
2171 if (load_mapping(fd, fp, i) != 0)
2176 * Reset builtin flag now as we have full font loaded.
2178 if (fl->font_flags == FONT_BUILTIN)
2179 fl->font_flags = FONT_AUTO;
2182 * Release previously loaded entries. We can do this now, as
2183 * the new font is loaded. Note, there can be no console
2184 * output till the new font is in place and teken is notified.
2185 * We do need to keep fl->font_data for glyph dimensions.
2187 STAILQ_FOREACH(fl, &fonts, font_next) {
2188 if (fl->font_data->vfbd_font == NULL)
2191 for (i = 0; i < VFNT_MAPS; i++)
2192 free(fl->font_data->vfbd_font->vf_map[i]);
2193 free(fl->font_data->vfbd_font->vf_bytes);
2194 free(fl->font_data->vfbd_font);
2195 fl->font_data->vfbd_font = NULL;
2199 bp->vfbd_compressed_size = 0;
2207 for (i = 0; i < VFNT_MAPS; i++)
2208 free(fp->vf_map[i]);
2217 SLIST_ENTRY(name_entry) n_entry;
2220 SLIST_HEAD(name_list, name_entry);
2222 /* Read font names from index file. */
2223 static struct name_list *
2224 read_list(char *fonts)
2226 struct name_list *nl;
2227 struct name_entry *np;
2232 dir = strdup(fonts);
2236 ptr = strrchr(dir, '/');
2239 fd = open(fonts, O_RDONLY);
2243 nl = malloc(sizeof(*nl));
2250 while ((len = fgetstr(buf, sizeof (buf), fd)) >= 0) {
2251 if (*buf == '#' || *buf == '\0')
2254 if (bcmp(buf, "MENU", 4) == 0)
2257 if (bcmp(buf, "FONT", 4) == 0)
2260 ptr = strchr(buf, ':');
2266 np = malloc(sizeof(*np));
2269 return (nl); /* return what we have */
2271 if (asprintf(&np->n_name, "%s/%s", dir, buf) < 0) {
2274 return (nl); /* return what we have */
2276 SLIST_INSERT_HEAD(nl, np, n_entry);
2283 * Read the font properties and insert new entry into the list.
2284 * The font list is built in descending order.
2287 insert_font(char *name, FONT_FLAGS flags)
2289 struct font_header fh;
2290 struct fontlist *fp, *previous, *entry, *next;
2297 if (flags == FONT_BUILTIN) {
2299 * We only install builtin font once, while setting up
2300 * initial console. Since this will happen very early,
2301 * we assume asprintf will not fail. Once we have access to
2302 * files, the builtin font will be replaced by font loaded
2305 if (!STAILQ_EMPTY(&fonts))
2308 fh.fh_width = DEFAULT_FONT_DATA.vfbd_width;
2309 fh.fh_height = DEFAULT_FONT_DATA.vfbd_height;
2311 (void) asprintf(&font_name, "%dx%d",
2312 DEFAULT_FONT_DATA.vfbd_width,
2313 DEFAULT_FONT_DATA.vfbd_height);
2315 fd = open(name, O_RDONLY);
2318 rv = read(fd, &fh, sizeof(fh));
2320 if (rv < 0 || (size_t)rv != sizeof(fh))
2323 if (memcmp(fh.fh_magic, FONT_HEADER_MAGIC,
2324 sizeof(fh.fh_magic)) != 0)
2326 font_name = strdup(name);
2329 if (font_name == NULL)
2333 * If we have an entry with the same glyph dimensions, replace
2334 * the file name and mark us. We only support unique dimensions.
2336 STAILQ_FOREACH(entry, &fonts, font_next) {
2337 if (fh.fh_width == entry->font_data->vfbd_width &&
2338 fh.fh_height == entry->font_data->vfbd_height) {
2339 free(entry->font_name);
2340 entry->font_name = font_name;
2341 entry->font_flags = FONT_RELOAD;
2346 fp = calloc(sizeof(*fp), 1);
2351 fp->font_data = calloc(sizeof(*fp->font_data), 1);
2352 if (fp->font_data == NULL) {
2357 fp->font_name = font_name;
2358 fp->font_flags = flags;
2359 fp->font_load = load_font;
2360 fp->font_data->vfbd_width = fh.fh_width;
2361 fp->font_data->vfbd_height = fh.fh_height;
2363 if (STAILQ_EMPTY(&fonts)) {
2364 STAILQ_INSERT_HEAD(&fonts, fp, font_next);
2369 size = fp->font_data->vfbd_width * fp->font_data->vfbd_height;
2371 STAILQ_FOREACH(entry, &fonts, font_next) {
2372 vt_font_bitmap_data_t *bd;
2374 bd = entry->font_data;
2375 /* Should fp be inserted before the entry? */
2376 if (size > bd->vfbd_width * bd->vfbd_height) {
2377 if (previous == NULL) {
2378 STAILQ_INSERT_HEAD(&fonts, fp, font_next);
2380 STAILQ_INSERT_AFTER(&fonts, previous, fp,
2385 next = STAILQ_NEXT(entry, font_next);
2387 size > next->font_data->vfbd_width *
2388 next->font_data->vfbd_height) {
2389 STAILQ_INSERT_AFTER(&fonts, entry, fp, font_next);
2398 font_set(struct env_var *ev __unused, int flags __unused, const void *value)
2400 struct fontlist *fl;
2402 unsigned long x = 0, y = 0;
2405 * Attempt to extract values from "XxY" string. In case of error,
2406 * we have unmaching glyph dimensions and will just output the
2409 if (value != NULL) {
2410 x = strtoul(value, &eptr, 10);
2412 y = strtoul(eptr + 1, &eptr, 10);
2414 STAILQ_FOREACH(fl, &fonts, font_next) {
2415 if (fl->font_data->vfbd_width == x &&
2416 fl->font_data->vfbd_height == y)
2420 /* Reset any FONT_MANUAL flag. */
2423 /* Mark this font manually loaded */
2424 fl->font_flags = FONT_MANUAL;
2425 cons_update_mode(gfx_state.tg_fb_type != FB_TEXT);
2429 printf("Available fonts:\n");
2430 STAILQ_FOREACH(fl, &fonts, font_next) {
2431 printf(" %dx%d\n", fl->font_data->vfbd_width,
2432 fl->font_data->vfbd_height);
2438 bios_text_font(bool use_vga_font)
2441 (void) insert_font(VGA_8X16_FONT, FONT_MANUAL);
2443 (void) insert_font(DEFAULT_8X16_FONT, FONT_MANUAL);
2447 autoload_font(bool bios)
2449 struct name_list *nl;
2450 struct name_entry *np;
2452 nl = read_list("/boot/fonts/INDEX.fonts");
2456 while (!SLIST_EMPTY(nl)) {
2457 np = SLIST_FIRST(nl);
2458 SLIST_REMOVE_HEAD(nl, n_entry);
2459 if (insert_font(np->n_name, FONT_AUTO) == false)
2460 printf("failed to add font: %s\n", np->n_name);
2466 * If vga text mode was requested, load vga.font (8x16 bold) font.
2469 bios_text_font(true);
2472 (void) cons_update_mode(gfx_state.tg_fb_type != FB_TEXT);
2475 COMMAND_SET(load_font, "loadfont", "load console font from file", command_font);
2478 command_font(int argc, char *argv[])
2481 struct fontlist *fl;
2482 vt_font_bitmap_data_t *bd;
2490 while ((c = getopt(argc, argv, "l")) != -1) {
2504 if (argc > 1 || (list && argc != 0)) {
2505 printf("Usage: loadfont [-l] | [file.fnt]\n");
2510 STAILQ_FOREACH(fl, &fonts, font_next) {
2511 printf("font %s: %dx%d%s\n", fl->font_name,
2512 fl->font_data->vfbd_width,
2513 fl->font_data->vfbd_height,
2514 fl->font_data->vfbd_font == NULL? "" : " loaded");
2523 char *name = argv[0];
2525 if (insert_font(name, FONT_MANUAL) == false) {
2526 printf("loadfont error: failed to load: %s\n", name);
2530 (void) cons_update_mode(gfx_state.tg_fb_type != FB_TEXT);
2536 * Walk entire font list, release any loaded font, and set
2537 * autoload flag. The font list does have at least the builtin
2540 STAILQ_FOREACH(fl, &fonts, font_next) {
2541 if (fl->font_data->vfbd_font != NULL) {
2545 * Note the setup_font() is releasing
2548 for (i = 0; i < VFNT_MAPS; i++)
2549 free(bd->vfbd_font->vf_map[i]);
2550 free(fl->font_data->vfbd_font);
2551 fl->font_data->vfbd_font = NULL;
2552 fl->font_data->vfbd_uncompressed_size = 0;
2553 fl->font_flags = FONT_AUTO;
2556 (void) cons_update_mode(gfx_state.tg_fb_type != FB_TEXT);
2562 gfx_get_edid_resolution(struct vesa_edid_info *edid, edid_res_list_t *res)
2564 struct resolution *rp, *p;
2567 * Walk detailed timings tables (4).
2569 if ((edid->display.supported_features
2570 & EDID_FEATURE_PREFERRED_TIMING_MODE) != 0) {
2571 /* Walk detailed timing descriptors (4) */
2572 for (int i = 0; i < DET_TIMINGS; i++) {
2574 * Reserved value 0 is not used for display decriptor.
2576 if (edid->detailed_timings[i].pixel_clock == 0)
2578 if ((rp = malloc(sizeof(*rp))) == NULL)
2580 rp->width = GET_EDID_INFO_WIDTH(edid, i);
2581 rp->height = GET_EDID_INFO_HEIGHT(edid, i);
2582 if (rp->width > 0 && rp->width <= EDID_MAX_PIXELS &&
2583 rp->height > 0 && rp->height <= EDID_MAX_LINES)
2584 TAILQ_INSERT_TAIL(res, rp, next);
2591 * Walk standard timings list (8).
2593 for (int i = 0; i < STD_TIMINGS; i++) {
2594 /* Is this field unused? */
2595 if (edid->standard_timings[i] == 0x0101)
2598 if ((rp = malloc(sizeof(*rp))) == NULL)
2601 rp->width = HSIZE(edid->standard_timings[i]);
2602 switch (RATIO(edid->standard_timings[i])) {
2604 rp->height = HSIZE(edid->standard_timings[i]);
2605 if (edid->header.version > 1 ||
2606 edid->header.revision > 2) {
2607 rp->height = rp->height * 10 / 16;
2611 rp->height = HSIZE(edid->standard_timings[i]) * 3 / 4;
2614 rp->height = HSIZE(edid->standard_timings[i]) * 4 / 5;
2617 rp->height = HSIZE(edid->standard_timings[i]) * 9 / 16;
2622 * Create resolution list in decreasing order, except keep
2623 * first entry (preferred timing mode).
2625 TAILQ_FOREACH(p, res, next) {
2626 if (p->width * p->height < rp->width * rp->height) {
2627 /* Keep preferred mode first */
2628 if (TAILQ_FIRST(res) == p)
2629 TAILQ_INSERT_AFTER(res, p, rp, next);
2631 TAILQ_INSERT_BEFORE(p, rp, next);
2634 if (TAILQ_NEXT(p, next) == NULL) {
2635 TAILQ_INSERT_TAIL(res, rp, next);
2640 return (!TAILQ_EMPTY(res));