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
31 * The workhorse here is gfxfb_blt(). It is implemented to mimic UEFI
32 * GOP Blt, and allows us to fill the rectangle on screen, copy
33 * rectangle from video to buffer and buffer to video and video to video.
34 * Such implementation does allow us to have almost identical implementation
35 * for both BIOS VBE and UEFI.
37 * ALL pixel data is assumed to be 32-bit BGRA (byte order Blue, Green, Red,
38 * Alpha) format, this allows us to only handle RGB data and not to worry
39 * about mixing RGB with indexed colors.
40 * Data exchange between memory buffer and video will translate BGRA
41 * and native format as following:
43 * 32-bit to/from 32-bit is trivial case.
44 * 32-bit to/from 24-bit is also simple - we just drop the alpha channel.
45 * 32-bit to/from 16-bit is more complicated, because we nee to handle
46 * data loss from 32-bit to 16-bit. While reading/writing from/to video, we
47 * need to apply masks of 16-bit color components. This will preserve
48 * colors for terminal text. For 32-bit truecolor PMG images, we need to
49 * translate 32-bit colors to 15/16 bit colors and this means data loss.
50 * There are different algorithms how to perform such color space reduction,
51 * we are currently using bitwise right shift to reduce color space and so far
52 * this technique seems to be sufficient (see also gfx_fb_putimage(), the
54 * 32-bit to/from 8-bit is the most troublesome because 8-bit colors are
55 * indexed. From video, we do get color indexes, and we do translate
56 * color index values to RGB. To write to video, we again need to translate
57 * RGB to color index. Additionally, we need to translate between VGA and
60 * Our internal color data is represented using BGRA format. But the hardware
61 * used indexed colors for 8-bit colors (0-255) and for this mode we do
62 * need to perform translation to/from BGRA and index values.
64 * - paletteentry RGB <-> index -
65 * BGRA BUFFER <----/ \ - VIDEO
69 * To perform index to RGB translation, we use palette table generated
70 * from when we set up 8-bit mode video. We cannot read palette data from
71 * the hardware, because not all hardware supports reading it.
73 * BGRA to index is implemented in rgb_to_color_index() by searching
74 * palette array for closest match of RBG values.
76 * Note: In 8-bit mode, We do store first 16 colors to palette registers
77 * in VGA color order, this serves two purposes; firstly,
78 * if palette update is not supported, we still have correct 16 colors.
79 * Secondly, the kernel does get correct 16 colors when some other boot
80 * loader is used. However, the palette map for 8-bit colors is using
81 * console color ordering - this does allow us to skip translation
82 * from VGA colors to console colors, while we are reading RGB data.
85 #include <sys/param.h>
90 #include <sys/splash.h>
91 #include <sys/linker.h>
92 #include <sys/module.h>
93 #include <sys/stdint.h>
94 #include <sys/endian.h>
96 #include <bootstrap.h>
107 /* VGA text mode does use bold font. */
108 #if !defined(VGA_8X16_FONT)
109 #define VGA_8X16_FONT "/boot/fonts/8x16b.fnt"
111 #if !defined(DEFAULT_8X16_FONT)
112 #define DEFAULT_8X16_FONT "/boot/fonts/8x16.fnt"
116 * Must be sorted by font size in descending order
118 font_list_t fonts
= STAILQ_HEAD_INITIALIZER(fonts
);
120 #define DEFAULT_FONT_DATA font_data_8x16
121 extern vt_font_bitmap_data_t font_data_8x16
;
122 teken_gfx_t gfx_state
= { 0 };
125 unsigned char r
; /* Red percentage value. */
126 unsigned char g
; /* Green percentage value. */
127 unsigned char b
; /* Blue percentage value. */
128 } color_def
[NCOLORS
] = {
129 {0, 0, 0}, /* black */
130 {50, 0, 0}, /* dark red */
131 {0, 50, 0}, /* dark green */
132 {77, 63, 0}, /* dark yellow */
133 {20, 40, 64}, /* dark blue */
134 {50, 0, 50}, /* dark magenta */
135 {0, 50, 50}, /* dark cyan */
136 {75, 75, 75}, /* light gray */
138 {18, 20, 21}, /* dark gray */
139 {100, 0, 0}, /* light red */
140 {0, 100, 0}, /* light green */
141 {100, 100, 0}, /* light yellow */
142 {45, 62, 81}, /* light blue */
143 {100, 0, 100}, /* light magenta */
144 {0, 100, 100}, /* light cyan */
145 {100, 100, 100}, /* white */
147 uint32_t cmap
[NCMAP
];
150 * Between console's palette and VGA's one:
151 * - blue and red are swapped (1 <-> 4)
152 * - yellow and cyan are swapped (3 <-> 6)
154 const int cons_to_vga_colors
[NCOLORS
] = {
155 0, 4, 2, 6, 1, 5, 3, 7,
156 8, 12, 10, 14, 9, 13, 11, 15
159 static const int vga_to_cons_colors
[NCOLORS
] = {
160 0, 1, 2, 3, 4, 5, 6, 7,
161 8, 9, 10, 11, 12, 13, 14, 15
164 struct text_pixel
*screen_buffer
;
166 static EFI_GRAPHICS_OUTPUT_BLT_PIXEL
*GlyphBuffer
;
168 static struct paletteentry
*GlyphBuffer
;
170 static size_t GlyphBufferSize
;
172 static bool insert_font(char *, FONT_FLAGS
);
173 static int font_set(struct env_var
*, int, const void *);
174 static void * allocate_glyphbuffer(uint32_t, uint32_t);
175 static void gfx_fb_cursor_draw(teken_gfx_t
*, const teken_pos_t
*, bool);
178 * Initialize gfx framework.
181 gfx_framework_init(void)
184 * Setup font list to have builtin font.
186 (void) insert_font(NULL
, FONT_BUILTIN
);
187 gfx_interp_ref(); /* Draw in the gfx interpreter for this thing */
191 gfx_get_fb_address(void)
193 return (ptov((uint32_t)gfx_state
.tg_fb
.fb_addr
));
197 * Utility function to parse gfx mode line strings.
200 gfx_parse_mode_str(char *str
, int *x
, int *y
, int *depth
)
206 *x
= strtoul(p
, &end
, 0);
207 if (*x
== 0 || errno
!= 0)
212 *y
= strtoul(p
, &end
, 0);
213 if (*y
== 0 || errno
!= 0)
216 *depth
= -1; /* auto select */
219 *depth
= strtoul(p
, &end
, 0);
220 if (*depth
== 0 || errno
!= 0 || *end
!= '\0')
228 rgb_color_map(uint8_t index
, uint32_t rmax
, int roffset
,
229 uint32_t gmax
, int goffset
, uint32_t bmax
, int boffset
)
231 uint32_t color
, code
, gray
, level
;
233 if (index
< NCOLORS
) {
234 #define CF(_f, _i) ((_f ## max * color_def[(_i)]._f / 100) << _f ## offset)
235 return (CF(r
, index
) | CF(g
, index
) | CF(b
, index
));
239 #define CF(_f, _c) ((_f ## max & _c) << _f ## offset)
240 /* 6x6x6 color cube */
241 if (index
> 15 && index
< 232) {
242 uint32_t red
, green
, blue
;
244 for (red
= 0; red
< 6; red
++) {
245 for (green
= 0; green
< 6; green
++) {
246 for (blue
= 0; blue
< 6; blue
++) {
247 code
= 16 + (red
* 36) +
251 red
= red
? (red
* 40 + 55) : 0;
252 green
= green
? (green
* 40 + 55) : 0;
253 blue
= blue
? (blue
* 40 + 55) : 0;
255 color
|= CF(g
, green
);
256 color
|= CF(b
, blue
);
263 /* colors 232-255 are a grayscale ramp */
264 for (gray
= 0; gray
< 24; gray
++) {
265 level
= (gray
* 10) + 8;
270 return (CF(r
, level
) | CF(g
, level
) | CF(b
, level
));
275 * Support for color mapping.
276 * For 8, 24 and 32 bit depth, use mask size 8.
277 * 15/16 bit depth needs to use mask size from mode,
278 * or we will lose color information from 32-bit to 15/16 bit translation.
281 gfx_fb_color_map(uint8_t index
)
283 int rmask
, gmask
, bmask
;
284 int roff
, goff
, boff
, bpp
;
286 roff
= ffs(gfx_state
.tg_fb
.fb_mask_red
) - 1;
287 goff
= ffs(gfx_state
.tg_fb
.fb_mask_green
) - 1;
288 boff
= ffs(gfx_state
.tg_fb
.fb_mask_blue
) - 1;
289 bpp
= roundup2(gfx_state
.tg_fb
.fb_bpp
, 8) >> 3;
292 rmask
= gfx_state
.tg_fb
.fb_mask_red
>> roff
;
297 gmask
= gfx_state
.tg_fb
.fb_mask_green
>> goff
;
302 bmask
= gfx_state
.tg_fb
.fb_mask_blue
>> boff
;
306 return (rgb_color_map(index
, rmask
, 16, gmask
, 8, bmask
, 0));
310 * Get indexed color from RGB. This function is used to write data to video
311 * memory when the adapter is set to use indexed colors.
312 * Since UEFI does only support 32-bit colors, we do not implement it for
313 * UEFI because there is no need for it and we do not have palette array
317 rgb_to_color_index(uint8_t r
, uint8_t g
, uint8_t b
)
320 uint32_t color
, best
, dist
, k
;
324 best
= 255 * 255 * 255;
325 for (k
= 0; k
< NCMAP
; k
++) {
326 diff
= r
- pe8
[k
].Red
;
328 diff
= g
- pe8
[k
].Green
;
330 diff
= b
- pe8
[k
].Blue
;
333 /* Exact match, exit the loop */
354 generate_cons_palette(uint32_t *palette
, int format
,
355 uint32_t rmax
, int roffset
, uint32_t gmax
, int goffset
,
356 uint32_t bmax
, int boffset
)
361 case COLOR_FORMAT_VGA
:
362 for (i
= 0; i
< NCOLORS
; i
++)
363 palette
[i
] = cons_to_vga_colors
[i
];
364 for (; i
< NCMAP
; i
++)
367 case COLOR_FORMAT_RGB
:
368 for (i
= 0; i
< NCMAP
; i
++)
369 palette
[i
] = rgb_color_map(i
, rmax
, roffset
,
370 gmax
, goffset
, bmax
, boffset
);
380 gfx_mem_wr1(uint8_t *base
, size_t size
, uint32_t o
, uint8_t v
)
385 *(uint8_t *)(base
+ o
) = v
;
389 gfx_mem_wr2(uint8_t *base
, size_t size
, uint32_t o
, uint16_t v
)
394 *(uint16_t *)(base
+ o
) = v
;
398 gfx_mem_wr4(uint8_t *base
, size_t size
, uint32_t o
, uint32_t v
)
403 *(uint32_t *)(base
+ o
) = v
;
406 static int gfxfb_blt_fill(void *BltBuffer
,
407 uint32_t DestinationX
, uint32_t DestinationY
,
408 uint32_t Width
, uint32_t Height
)
411 EFI_GRAPHICS_OUTPUT_BLT_PIXEL
*p
;
413 struct paletteentry
*p
;
415 uint32_t data
, bpp
, pitch
, y
, x
;
416 int roff
, goff
, boff
;
419 uint8_t *destination
;
421 if (BltBuffer
== NULL
)
424 if (DestinationY
+ Height
> gfx_state
.tg_fb
.fb_height
)
427 if (DestinationX
+ Width
> gfx_state
.tg_fb
.fb_width
)
430 if (Width
== 0 || Height
== 0)
434 roff
= ffs(gfx_state
.tg_fb
.fb_mask_red
) - 1;
435 goff
= ffs(gfx_state
.tg_fb
.fb_mask_green
) - 1;
436 boff
= ffs(gfx_state
.tg_fb
.fb_mask_blue
) - 1;
438 if (gfx_state
.tg_fb
.fb_bpp
== 8) {
439 data
= rgb_to_color_index(p
->Red
, p
->Green
, p
->Blue
);
442 (gfx_state
.tg_fb
.fb_mask_red
>> roff
)) << roff
;
444 (gfx_state
.tg_fb
.fb_mask_green
>> goff
)) << goff
;
446 (gfx_state
.tg_fb
.fb_mask_blue
>> boff
)) << boff
;
449 bpp
= roundup2(gfx_state
.tg_fb
.fb_bpp
, 8) >> 3;
450 pitch
= gfx_state
.tg_fb
.fb_stride
* bpp
;
451 destination
= gfx_get_fb_address();
452 size
= gfx_state
.tg_fb
.fb_size
;
454 for (y
= DestinationY
; y
< Height
+ DestinationY
; y
++) {
455 off
= y
* pitch
+ DestinationX
* bpp
;
456 for (x
= 0; x
< Width
; x
++) {
459 gfx_mem_wr1(destination
, size
, off
,
461 cons_to_vga_colors
[data
] : data
);
464 gfx_mem_wr2(destination
, size
, off
, data
);
467 gfx_mem_wr1(destination
, size
, off
,
468 (data
>> 16) & 0xff);
469 gfx_mem_wr1(destination
, size
, off
+ 1,
471 gfx_mem_wr1(destination
, size
, off
+ 2,
475 gfx_mem_wr4(destination
, size
, off
, data
);
488 gfxfb_blt_video_to_buffer(void *BltBuffer
, uint32_t SourceX
, uint32_t SourceY
,
489 uint32_t DestinationX
, uint32_t DestinationY
,
490 uint32_t Width
, uint32_t Height
, uint32_t Delta
)
493 EFI_GRAPHICS_OUTPUT_BLT_PIXEL
*p
;
495 struct paletteentry
*p
;
498 uint32_t bpp
, pitch
, copybytes
;
500 uint8_t *source
, *destination
, *sb
;
501 uint8_t rm
, rp
, gm
, gp
, bm
, bp
;
504 if (BltBuffer
== NULL
)
507 if (SourceY
+ Height
>
508 gfx_state
.tg_fb
.fb_height
)
511 if (SourceX
+ Width
> gfx_state
.tg_fb
.fb_width
)
514 if (Width
== 0 || Height
== 0)
518 Delta
= Width
* sizeof (*p
);
520 bpp
= roundup2(gfx_state
.tg_fb
.fb_bpp
, 8) >> 3;
521 pitch
= gfx_state
.tg_fb
.fb_stride
* bpp
;
523 copybytes
= Width
* bpp
;
525 rp
= ffs(gfx_state
.tg_fb
.fb_mask_red
) - 1;
526 gp
= ffs(gfx_state
.tg_fb
.fb_mask_green
) - 1;
527 bp
= ffs(gfx_state
.tg_fb
.fb_mask_blue
) - 1;
528 rm
= gfx_state
.tg_fb
.fb_mask_red
>> rp
;
529 gm
= gfx_state
.tg_fb
.fb_mask_green
>> gp
;
530 bm
= gfx_state
.tg_fb
.fb_mask_blue
>> bp
;
532 /* If FB pixel format is BGRA, we can use direct copy. */
534 ffs(rm
) - 1 == 8 && rp
== 16 &&
535 ffs(gm
) - 1 == 8 && gp
== 8 &&
536 ffs(bm
) - 1 == 8 && bp
== 0;
538 for (sy
= SourceY
, dy
= DestinationY
; dy
< Height
+ DestinationY
;
540 off
= sy
* pitch
+ SourceX
* bpp
;
541 source
= gfx_get_fb_address() + off
;
542 destination
= (uint8_t *)BltBuffer
+ dy
* Delta
+
543 DestinationX
* sizeof (*p
);
546 bcopy(source
, destination
, copybytes
);
548 for (x
= 0; x
< Width
; x
++) {
551 p
= (void *)(destination
+ x
* sizeof (*p
));
552 sb
= source
+ x
* bpp
;
561 c
= sb
[0] << 16 | sb
[1] << 8 | sb
[2];
571 *(uint32_t *)p
= gfx_fb_color_map(
573 vga_to_cons_colors
[c
] : c
);
575 p
->Red
= (c
>> rp
) & rm
;
576 p
->Green
= (c
>> gp
) & gm
;
577 p
->Blue
= (c
>> bp
) & bm
;
588 gfxfb_blt_buffer_to_video(void *BltBuffer
, uint32_t SourceX
, uint32_t SourceY
,
589 uint32_t DestinationX
, uint32_t DestinationY
,
590 uint32_t Width
, uint32_t Height
, uint32_t Delta
)
593 EFI_GRAPHICS_OUTPUT_BLT_PIXEL
*p
;
595 struct paletteentry
*p
;
598 uint32_t bpp
, pitch
, copybytes
;
600 uint8_t *source
, *destination
;
601 uint8_t rm
, rp
, gm
, gp
, bm
, bp
;
604 if (BltBuffer
== NULL
)
607 if (DestinationY
+ Height
>
608 gfx_state
.tg_fb
.fb_height
)
611 if (DestinationX
+ Width
> gfx_state
.tg_fb
.fb_width
)
614 if (Width
== 0 || Height
== 0)
618 Delta
= Width
* sizeof (*p
);
620 bpp
= roundup2(gfx_state
.tg_fb
.fb_bpp
, 8) >> 3;
621 pitch
= gfx_state
.tg_fb
.fb_stride
* bpp
;
623 copybytes
= Width
* bpp
;
625 rp
= ffs(gfx_state
.tg_fb
.fb_mask_red
) - 1;
626 gp
= ffs(gfx_state
.tg_fb
.fb_mask_green
) - 1;
627 bp
= ffs(gfx_state
.tg_fb
.fb_mask_blue
) - 1;
628 rm
= gfx_state
.tg_fb
.fb_mask_red
>> rp
;
629 gm
= gfx_state
.tg_fb
.fb_mask_green
>> gp
;
630 bm
= gfx_state
.tg_fb
.fb_mask_blue
>> bp
;
632 /* If FB pixel format is BGRA, we can use direct copy. */
634 ffs(rm
) - 1 == 8 && rp
== 16 &&
635 ffs(gm
) - 1 == 8 && gp
== 8 &&
636 ffs(bm
) - 1 == 8 && bp
== 0;
638 for (sy
= SourceY
, dy
= DestinationY
; sy
< Height
+ SourceY
;
640 off
= dy
* pitch
+ DestinationX
* bpp
;
641 destination
= gfx_get_fb_address() + off
;
644 source
= (uint8_t *)BltBuffer
+ sy
* Delta
+
645 SourceX
* sizeof (*p
);
646 bcopy(source
, destination
, copybytes
);
648 for (x
= 0; x
< Width
; x
++) {
651 p
= (void *)((uint8_t *)BltBuffer
+
653 (SourceX
+ x
) * sizeof (*p
));
655 c
= rgb_to_color_index(p
->Red
,
658 c
= (p
->Red
& rm
) << rp
|
659 (p
->Green
& gm
) << gp
|
660 (p
->Blue
& bm
) << bp
;
665 gfx_mem_wr1(destination
, copybytes
,
667 cons_to_vga_colors
[c
] : c
);
670 gfx_mem_wr2(destination
, copybytes
,
674 gfx_mem_wr1(destination
, copybytes
,
675 off
, (c
>> 16) & 0xff);
676 gfx_mem_wr1(destination
, copybytes
,
677 off
+ 1, (c
>> 8) & 0xff);
678 gfx_mem_wr1(destination
, copybytes
,
682 gfx_mem_wr4(destination
, copybytes
,
696 gfxfb_blt_video_to_video(uint32_t SourceX
, uint32_t SourceY
,
697 uint32_t DestinationX
, uint32_t DestinationY
,
698 uint32_t Width
, uint32_t Height
)
700 uint32_t bpp
, copybytes
;
702 uint8_t *source
, *destination
;
705 if (SourceY
+ Height
>
706 gfx_state
.tg_fb
.fb_height
)
709 if (SourceX
+ Width
> gfx_state
.tg_fb
.fb_width
)
712 if (DestinationY
+ Height
>
713 gfx_state
.tg_fb
.fb_height
)
716 if (DestinationX
+ Width
> gfx_state
.tg_fb
.fb_width
)
719 if (Width
== 0 || Height
== 0)
722 bpp
= roundup2(gfx_state
.tg_fb
.fb_bpp
, 8) >> 3;
723 pitch
= gfx_state
.tg_fb
.fb_stride
* bpp
;
725 copybytes
= Width
* bpp
;
727 off
= SourceY
* pitch
+ SourceX
* bpp
;
728 source
= gfx_get_fb_address() + off
;
729 off
= DestinationY
* pitch
+ DestinationX
* bpp
;
730 destination
= gfx_get_fb_address() + off
;
732 if ((uintptr_t)destination
> (uintptr_t)source
) {
733 source
+= Height
* pitch
;
734 destination
+= Height
* pitch
;
738 while (Height
-- > 0) {
739 bcopy(source
, destination
, copybytes
);
741 destination
+= pitch
;
748 gfxfb_shadow_fill(uint32_t *BltBuffer
,
749 uint32_t DestinationX
, uint32_t DestinationY
,
750 uint32_t Width
, uint32_t Height
)
754 if (gfx_state
.tg_shadow_fb
== NULL
)
757 fbX
= gfx_state
.tg_fb
.fb_width
;
758 fbY
= gfx_state
.tg_fb
.fb_height
;
760 if (BltBuffer
== NULL
)
763 if (DestinationX
+ Width
> fbX
)
764 Width
= fbX
- DestinationX
;
766 if (DestinationY
+ Height
> fbY
)
767 Height
= fbY
- DestinationY
;
769 uint32_t y2
= Height
+ DestinationY
;
770 for (uint32_t y1
= DestinationY
; y1
< y2
; y1
++) {
771 uint32_t off
= y1
* fbX
+ DestinationX
;
773 for (uint32_t x
= 0; x
< Width
; x
++) {
774 gfx_state
.tg_shadow_fb
[off
+ x
] = *BltBuffer
;
780 gfxfb_blt(void *BltBuffer
, GFXFB_BLT_OPERATION BltOperation
,
781 uint32_t SourceX
, uint32_t SourceY
,
782 uint32_t DestinationX
, uint32_t DestinationY
,
783 uint32_t Width
, uint32_t Height
, uint32_t Delta
)
788 EFI_GRAPHICS_OUTPUT
*gop
= gfx_state
.tg_private
;
792 * We assume Blt() does work, if not, we will need to build exception
793 * list case by case. We only have boot services during part of our
794 * exectution. Once terminate boot services, these operations cannot be
795 * done as they are provided by protocols that disappear when exit
798 if (gop
!= NULL
&& boot_services_active
) {
799 tpl
= BS
->RaiseTPL(TPL_NOTIFY
);
800 switch (BltOperation
) {
801 case GfxFbBltVideoFill
:
802 gfxfb_shadow_fill(BltBuffer
, DestinationX
,
803 DestinationY
, Width
, Height
);
804 status
= gop
->Blt(gop
, BltBuffer
, EfiBltVideoFill
,
805 SourceX
, SourceY
, DestinationX
, DestinationY
,
806 Width
, Height
, Delta
);
809 case GfxFbBltVideoToBltBuffer
:
810 status
= gop
->Blt(gop
, BltBuffer
,
811 EfiBltVideoToBltBuffer
,
812 SourceX
, SourceY
, DestinationX
, DestinationY
,
813 Width
, Height
, Delta
);
816 case GfxFbBltBufferToVideo
:
817 status
= gop
->Blt(gop
, BltBuffer
, EfiBltBufferToVideo
,
818 SourceX
, SourceY
, DestinationX
, DestinationY
,
819 Width
, Height
, Delta
);
822 case GfxFbBltVideoToVideo
:
823 status
= gop
->Blt(gop
, BltBuffer
, EfiBltVideoToVideo
,
824 SourceX
, SourceY
, DestinationX
, DestinationY
,
825 Width
, Height
, Delta
);
829 status
= EFI_INVALID_PARAMETER
;
838 case EFI_INVALID_PARAMETER
:
842 case EFI_DEVICE_ERROR
:
853 switch (BltOperation
) {
854 case GfxFbBltVideoFill
:
855 gfxfb_shadow_fill(BltBuffer
, DestinationX
, DestinationY
,
857 rv
= gfxfb_blt_fill(BltBuffer
, DestinationX
, DestinationY
,
861 case GfxFbBltVideoToBltBuffer
:
862 rv
= gfxfb_blt_video_to_buffer(BltBuffer
, SourceX
, SourceY
,
863 DestinationX
, DestinationY
, Width
, Height
, Delta
);
866 case GfxFbBltBufferToVideo
:
867 rv
= gfxfb_blt_buffer_to_video(BltBuffer
, SourceX
, SourceY
,
868 DestinationX
, DestinationY
, Width
, Height
, Delta
);
871 case GfxFbBltVideoToVideo
:
872 rv
= gfxfb_blt_video_to_video(SourceX
, SourceY
,
873 DestinationX
, DestinationY
, Width
, Height
);
884 gfx_bitblt_bitmap(teken_gfx_t
*state
, const uint8_t *glyph
,
885 const teken_attr_t
*a
, uint32_t alpha
, bool cursor
)
887 uint32_t width
, height
;
888 uint32_t fgc
, bgc
, bpl
, cc
, o
;
892 bpp
= 4; /* We only generate BGRA */
893 width
= state
->tg_font
.vf_width
;
894 height
= state
->tg_font
.vf_height
;
895 bpl
= (width
+ 7) / 8; /* Bytes per source line. */
899 if (a
->ta_format
& TF_BOLD
)
901 if (a
->ta_format
& TF_BLINK
)
904 fgc
= gfx_fb_color_map(fgc
);
905 bgc
= gfx_fb_color_map(bgc
);
907 if (a
->ta_format
& TF_REVERSE
)
923 for (uint32_t y
= 0; y
< height
; y
++) {
924 for (uint32_t x
= 0; x
< width
; x
++) {
925 byte
= y
* bpl
+ x
/ 8;
926 bit
= 0x80 >> (x
% 8);
927 o
= y
* width
* bpp
+ x
* bpp
;
928 cc
= glyph
[byte
] & bit
? fgc
: bgc
;
930 gfx_mem_wr4(state
->tg_glyph
,
931 state
->tg_glyph_size
, o
, cc
);
937 * Draw prepared glyph on terminal point p.
940 gfx_fb_printchar(teken_gfx_t
*state
, const teken_pos_t
*p
)
942 unsigned x
, y
, width
, height
;
944 width
= state
->tg_font
.vf_width
;
945 height
= state
->tg_font
.vf_height
;
946 x
= state
->tg_origin
.tp_col
+ p
->tp_col
* width
;
947 y
= state
->tg_origin
.tp_row
+ p
->tp_row
* height
;
949 gfx_fb_cons_display(x
, y
, width
, height
, state
->tg_glyph
);
953 * Store char with its attribute to buffer and put it on screen.
956 gfx_fb_putchar(void *arg
, const teken_pos_t
*p
, teken_char_t c
,
957 const teken_attr_t
*a
)
959 teken_gfx_t
*state
= arg
;
960 const uint8_t *glyph
;
963 idx
= p
->tp_col
+ p
->tp_row
* state
->tg_tp
.tp_col
;
964 if (idx
>= state
->tg_tp
.tp_col
* state
->tg_tp
.tp_row
)
967 /* remove the cursor */
968 if (state
->tg_cursor_visible
)
969 gfx_fb_cursor_draw(state
, &state
->tg_cursor
, false);
971 screen_buffer
[idx
].c
= c
;
972 screen_buffer
[idx
].a
= *a
;
974 glyph
= font_lookup(&state
->tg_font
, c
, a
);
975 gfx_bitblt_bitmap(state
, glyph
, a
, 0xff, false);
976 gfx_fb_printchar(state
, p
);
978 /* display the cursor */
979 if (state
->tg_cursor_visible
) {
980 const teken_pos_t
*c
;
982 c
= teken_get_cursor(&state
->tg_teken
);
983 gfx_fb_cursor_draw(state
, c
, true);
988 gfx_fb_fill(void *arg
, const teken_rect_t
*r
, teken_char_t c
,
989 const teken_attr_t
*a
)
991 teken_gfx_t
*state
= arg
;
992 const uint8_t *glyph
;
994 struct text_pixel
*row
;
996 /* remove the cursor */
997 if (state
->tg_cursor_visible
)
998 gfx_fb_cursor_draw(state
, &state
->tg_cursor
, false);
1000 glyph
= font_lookup(&state
->tg_font
, c
, a
);
1001 gfx_bitblt_bitmap(state
, glyph
, a
, 0xff, false);
1003 for (p
.tp_row
= r
->tr_begin
.tp_row
; p
.tp_row
< r
->tr_end
.tp_row
;
1005 row
= &screen_buffer
[p
.tp_row
* state
->tg_tp
.tp_col
];
1006 for (p
.tp_col
= r
->tr_begin
.tp_col
;
1007 p
.tp_col
< r
->tr_end
.tp_col
; p
.tp_col
++) {
1008 row
[p
.tp_col
].c
= c
;
1009 row
[p
.tp_col
].a
= *a
;
1010 gfx_fb_printchar(state
, &p
);
1014 /* display the cursor */
1015 if (state
->tg_cursor_visible
) {
1016 const teken_pos_t
*c
;
1018 c
= teken_get_cursor(&state
->tg_teken
);
1019 gfx_fb_cursor_draw(state
, c
, true);
1024 gfx_fb_cursor_draw(teken_gfx_t
*state
, const teken_pos_t
*pos
, bool on
)
1026 const uint8_t *glyph
;
1031 if (p
.tp_col
>= state
->tg_tp
.tp_col
)
1032 p
.tp_col
= state
->tg_tp
.tp_col
- 1;
1033 if (p
.tp_row
>= state
->tg_tp
.tp_row
)
1034 p
.tp_row
= state
->tg_tp
.tp_row
- 1;
1035 idx
= p
.tp_col
+ p
.tp_row
* state
->tg_tp
.tp_col
;
1036 if (idx
>= state
->tg_tp
.tp_col
* state
->tg_tp
.tp_row
)
1039 glyph
= font_lookup(&state
->tg_font
, screen_buffer
[idx
].c
,
1040 &screen_buffer
[idx
].a
);
1041 gfx_bitblt_bitmap(state
, glyph
, &screen_buffer
[idx
].a
, 0xff, on
);
1042 gfx_fb_printchar(state
, &p
);
1044 state
->tg_cursor
= p
;
1048 gfx_fb_cursor(void *arg
, const teken_pos_t
*p
)
1050 teken_gfx_t
*state
= arg
;
1052 /* Switch cursor off in old location and back on in new. */
1053 if (state
->tg_cursor_visible
) {
1054 gfx_fb_cursor_draw(state
, &state
->tg_cursor
, false);
1055 gfx_fb_cursor_draw(state
, p
, true);
1060 gfx_fb_param(void *arg
, int cmd
, unsigned int value
)
1062 teken_gfx_t
*state
= arg
;
1063 const teken_pos_t
*c
;
1066 case TP_SETLOCALCURSOR
:
1068 * 0 means normal (usually block), 1 means hidden, and
1069 * 2 means blinking (always block) for compatibility with
1070 * syscons. We don't support any changes except hiding,
1071 * so must map 2 to 0.
1073 value
= (value
== 1) ? 0 : 1;
1076 c
= teken_get_cursor(&state
->tg_teken
);
1077 gfx_fb_cursor_draw(state
, c
, true);
1079 state
->tg_cursor_visible
= true;
1081 state
->tg_cursor_visible
= false;
1084 /* Not yet implemented */
1090 is_same_pixel(struct text_pixel
*px1
, struct text_pixel
*px2
)
1092 if (px1
->c
!= px2
->c
)
1095 /* Is there image stored? */
1096 if ((px1
->a
.ta_format
& TF_IMAGE
) ||
1097 (px2
->a
.ta_format
& TF_IMAGE
))
1100 if (px1
->a
.ta_format
!= px2
->a
.ta_format
)
1102 if (px1
->a
.ta_fgcolor
!= px2
->a
.ta_fgcolor
)
1104 if (px1
->a
.ta_bgcolor
!= px2
->a
.ta_bgcolor
)
1111 gfx_fb_copy_area(teken_gfx_t
*state
, const teken_rect_t
*s
,
1112 const teken_pos_t
*d
)
1114 uint32_t sx
, sy
, dx
, dy
, width
, height
;
1115 uint32_t pitch
, bytes
;
1118 width
= state
->tg_font
.vf_width
;
1119 height
= state
->tg_font
.vf_height
;
1121 sx
= s
->tr_begin
.tp_col
* width
;
1122 sy
= s
->tr_begin
.tp_row
* height
;
1123 dx
= d
->tp_col
* width
;
1124 dy
= d
->tp_row
* height
;
1126 width
*= (s
->tr_end
.tp_col
- s
->tr_begin
.tp_col
+ 1);
1129 * With no shadow fb, use video to video copy.
1131 if (state
->tg_shadow_fb
== NULL
) {
1132 (void) gfxfb_blt(NULL
, GfxFbBltVideoToVideo
,
1133 sx
+ state
->tg_origin
.tp_col
,
1134 sy
+ state
->tg_origin
.tp_row
,
1135 dx
+ state
->tg_origin
.tp_col
,
1136 dy
+ state
->tg_origin
.tp_row
,
1142 * With shadow fb, we need to copy data on both shadow and video,
1143 * to preserve the consistency. We only read data from shadow fb.
1147 pitch
= state
->tg_fb
.fb_width
;
1148 bytes
= width
* sizeof (*state
->tg_shadow_fb
);
1151 * To handle overlapping areas, set up reverse copy here.
1153 if (dy
* pitch
+ dx
> sy
* pitch
+ sx
) {
1159 while (height
-- > 0) {
1160 uint32_t *source
= &state
->tg_shadow_fb
[sy
* pitch
+ sx
];
1161 uint32_t *destination
= &state
->tg_shadow_fb
[dy
* pitch
+ dx
];
1163 bcopy(source
, destination
, bytes
);
1164 (void) gfxfb_blt(destination
, GfxFbBltBufferToVideo
,
1165 0, 0, dx
+ state
->tg_origin
.tp_col
,
1166 dy
+ state
->tg_origin
.tp_row
, width
, 1, 0);
1174 gfx_fb_copy_line(teken_gfx_t
*state
, int ncol
, teken_pos_t
*s
, teken_pos_t
*d
)
1178 unsigned soffset
, doffset
;
1182 soffset
= s
->tp_col
+ s
->tp_row
* state
->tg_tp
.tp_col
;
1183 doffset
= d
->tp_col
+ d
->tp_row
* state
->tg_tp
.tp_col
;
1185 for (x
= 0; x
< ncol
; x
++) {
1186 if (is_same_pixel(&screen_buffer
[soffset
+ x
],
1187 &screen_buffer
[doffset
+ x
])) {
1189 gfx_fb_copy_area(state
, &sr
, &dp
);
1193 screen_buffer
[doffset
+ x
] = screen_buffer
[soffset
+ x
];
1195 /* update end point */
1196 sr
.tr_end
.tp_col
= s
->tp_col
+ x
;
1198 /* set up new rectangle */
1200 sr
.tr_begin
.tp_col
= s
->tp_col
+ x
;
1201 sr
.tr_begin
.tp_row
= s
->tp_row
;
1202 sr
.tr_end
.tp_col
= s
->tp_col
+ x
;
1203 sr
.tr_end
.tp_row
= s
->tp_row
;
1204 dp
.tp_col
= d
->tp_col
+ x
;
1205 dp
.tp_row
= d
->tp_row
;
1210 gfx_fb_copy_area(state
, &sr
, &dp
);
1215 gfx_fb_copy(void *arg
, const teken_rect_t
*r
, const teken_pos_t
*p
)
1217 teken_gfx_t
*state
= arg
;
1218 unsigned doffset
, soffset
;
1220 int nrow
, ncol
, y
; /* Has to be signed - >= 0 comparison */
1223 * Copying is a little tricky. We must make sure we do it in
1224 * correct order, to make sure we don't overwrite our own data.
1227 nrow
= r
->tr_end
.tp_row
- r
->tr_begin
.tp_row
;
1228 ncol
= r
->tr_end
.tp_col
- r
->tr_begin
.tp_col
;
1230 if (p
->tp_row
+ nrow
> state
->tg_tp
.tp_row
||
1231 p
->tp_col
+ ncol
> state
->tg_tp
.tp_col
)
1234 soffset
= r
->tr_begin
.tp_col
+ r
->tr_begin
.tp_row
* state
->tg_tp
.tp_col
;
1235 doffset
= p
->tp_col
+ p
->tp_row
* state
->tg_tp
.tp_col
;
1237 /* remove the cursor */
1238 if (state
->tg_cursor_visible
)
1239 gfx_fb_cursor_draw(state
, &state
->tg_cursor
, false);
1242 * Copy line by line.
1244 if (doffset
<= soffset
) {
1247 for (y
= 0; y
< nrow
; y
++) {
1248 s
.tp_row
= r
->tr_begin
.tp_row
+ y
;
1249 d
.tp_row
= p
->tp_row
+ y
;
1251 gfx_fb_copy_line(state
, ncol
, &s
, &d
);
1254 for (y
= nrow
- 1; y
>= 0; y
--) {
1255 s
.tp_row
= r
->tr_begin
.tp_row
+ y
;
1256 d
.tp_row
= p
->tp_row
+ y
;
1258 gfx_fb_copy_line(state
, ncol
, &s
, &d
);
1262 /* display the cursor */
1263 if (state
->tg_cursor_visible
) {
1264 const teken_pos_t
*c
;
1266 c
= teken_get_cursor(&state
->tg_teken
);
1267 gfx_fb_cursor_draw(state
, c
, true);
1272 * Implements alpha blending for RGBA data, could use pixels for arguments,
1273 * but byte stream seems more generic.
1274 * The generic alpha blending is:
1275 * blend = alpha * fg + (1.0 - alpha) * bg.
1276 * Since our alpha is not from range [0..1], we scale appropriately.
1279 alpha_blend(uint8_t fg
, uint8_t bg
, uint8_t alpha
)
1281 uint16_t blend
, h
, l
;
1283 /* trivial corner cases */
1288 blend
= (alpha
* fg
+ (0xFF - alpha
) * bg
);
1289 /* Division by 0xFF */
1298 * Implements alpha blending for RGBA data, could use pixels for arguments,
1299 * but byte stream seems more generic.
1300 * The generic alpha blending is:
1301 * blend = alpha * fg + (1.0 - alpha) * bg.
1302 * Since our alpha is not from range [0..1], we scale appropriately.
1305 bitmap_cpy(void *dst
, void *src
, uint32_t size
)
1308 EFI_GRAPHICS_OUTPUT_BLT_PIXEL
*ps
, *pd
;
1310 struct paletteentry
*ps
, *pd
;
1319 * we only implement alpha blending for depth 32.
1321 for (i
= 0; i
< size
; i
++) {
1323 pd
[i
].Red
= alpha_blend(ps
[i
].Red
, pd
[i
].Red
, a
);
1324 pd
[i
].Green
= alpha_blend(ps
[i
].Green
, pd
[i
].Green
, a
);
1325 pd
[i
].Blue
= alpha_blend(ps
[i
].Blue
, pd
[i
].Blue
, a
);
1331 allocate_glyphbuffer(uint32_t width
, uint32_t height
)
1335 size
= sizeof (*GlyphBuffer
) * width
* height
;
1336 if (size
!= GlyphBufferSize
) {
1338 GlyphBuffer
= malloc(size
);
1339 if (GlyphBuffer
== NULL
)
1341 GlyphBufferSize
= size
;
1343 return (GlyphBuffer
);
1347 gfx_fb_cons_display(uint32_t x
, uint32_t y
, uint32_t width
, uint32_t height
,
1351 EFI_GRAPHICS_OUTPUT_BLT_PIXEL
*buf
, *p
;
1353 struct paletteentry
*buf
, *p
;
1358 * If we do have shadow fb, we will use shadow to render data,
1359 * and copy shadow to video.
1361 if (gfx_state
.tg_shadow_fb
!= NULL
) {
1362 uint32_t pitch
= gfx_state
.tg_fb
.fb_width
;
1364 /* Copy rectangle line by line. */
1366 for (uint32_t sy
= 0; sy
< height
; sy
++) {
1367 buf
= (void *)(gfx_state
.tg_shadow_fb
+
1368 (y
- gfx_state
.tg_origin
.tp_row
) * pitch
+
1369 x
- gfx_state
.tg_origin
.tp_col
);
1370 bitmap_cpy(buf
, &p
[sy
* width
], width
);
1371 (void) gfxfb_blt(buf
, GfxFbBltBufferToVideo
,
1372 0, 0, x
, y
, width
, 1, 0);
1379 * Common data to display is glyph, use preallocated
1382 if (gfx_state
.tg_glyph_size
!= GlyphBufferSize
)
1383 (void) allocate_glyphbuffer(width
, height
);
1385 size
= width
* height
* sizeof(*buf
);
1386 if (size
== GlyphBufferSize
)
1393 if (gfxfb_blt(buf
, GfxFbBltVideoToBltBuffer
, x
, y
, 0, 0,
1394 width
, height
, 0) == 0) {
1395 bitmap_cpy(buf
, data
, width
* height
);
1396 (void) gfxfb_blt(buf
, GfxFbBltBufferToVideo
, 0, 0, x
, y
,
1399 if (buf
!= GlyphBuffer
)
1404 * Public graphics primitives.
1413 /* "bit" starts at the highest power of four <= the argument. */
1418 if (num
>= res
+ bit
) {
1420 res
= (res
>> 1) + bit
;
1430 gfx_fb_getcolor(void)
1433 const teken_attr_t
*ap
;
1435 ap
= teken_get_curattr(&gfx_state
.tg_teken
);
1436 if (ap
->ta_format
& TF_REVERSE
) {
1438 if (ap
->ta_format
& TF_BLINK
)
1442 if (ap
->ta_format
& TF_BOLD
)
1446 return (gfx_fb_color_map(c
));
1449 /* set pixel in framebuffer using gfx coordinates */
1451 gfx_fb_setpixel(uint32_t x
, uint32_t y
)
1455 if (gfx_state
.tg_fb_type
== FB_TEXT
)
1458 c
= gfx_fb_getcolor();
1460 if (x
>= gfx_state
.tg_fb
.fb_width
||
1461 y
>= gfx_state
.tg_fb
.fb_height
)
1464 gfxfb_blt(&c
, GfxFbBltVideoFill
, 0, 0, x
, y
, 1, 1, 0);
1468 * draw rectangle in framebuffer using gfx coordinates.
1471 gfx_fb_drawrect(uint32_t x1
, uint32_t y1
, uint32_t x2
, uint32_t y2
,
1476 if (gfx_state
.tg_fb_type
== FB_TEXT
)
1479 c
= gfx_fb_getcolor();
1482 gfxfb_blt(&c
, GfxFbBltVideoFill
, 0, 0, x1
, y1
, x2
- x1
,
1485 gfxfb_blt(&c
, GfxFbBltVideoFill
, 0, 0, x1
, y1
, x2
- x1
, 1, 0);
1486 gfxfb_blt(&c
, GfxFbBltVideoFill
, 0, 0, x1
, y2
, x2
- x1
, 1, 0);
1487 gfxfb_blt(&c
, GfxFbBltVideoFill
, 0, 0, x1
, y1
, 1, y2
- y1
, 0);
1488 gfxfb_blt(&c
, GfxFbBltVideoFill
, 0, 0, x2
, y1
, 1, y2
- y1
, 0);
1493 gfx_fb_line(uint32_t x0
, uint32_t y0
, uint32_t x1
, uint32_t y1
, uint32_t wd
)
1496 int err
, e2
, x2
, y2
, ed
, width
;
1498 if (gfx_state
.tg_fb_type
== FB_TEXT
)
1502 sx
= x0
< x1
? 1 : -1;
1503 sy
= y0
< y1
? 1 : -1;
1504 dx
= x1
> x0
? x1
- x0
: x0
- x1
;
1505 dy
= y1
> y0
? y1
- y0
: y0
- y1
;
1507 ed
= dx
+ dy
== 0 ? 1: isqrt(dx
* dx
+ dy
* dy
);
1510 gfx_fb_setpixel(x0
, y0
);
1513 if ((e2
<< 1) >= -dx
) { /* x step */
1516 while (e2
< ed
* width
&&
1517 (y1
!= (uint32_t)y2
|| dx
> dy
)) {
1519 gfx_fb_setpixel(x0
, y2
);
1528 if ((e2
<< 1) <= dy
) { /* y step */
1530 while (e2
< ed
* width
&&
1531 (x1
!= (uint32_t)x2
|| dx
< dy
)) {
1533 gfx_fb_setpixel(x2
, y0
);
1545 * quadratic Bézier curve limited to gradients without sign change.
1548 gfx_fb_bezier(uint32_t x0
, uint32_t y0
, uint32_t x1
, uint32_t y1
, uint32_t x2
,
1549 uint32_t y2
, uint32_t wd
)
1551 int sx
, sy
, xx
, yy
, xy
, width
;
1552 int dx
, dy
, err
, curvature
;
1555 if (gfx_state
.tg_fb_type
== FB_TEXT
)
1563 curvature
= xx
*sy
- yy
*sx
;
1565 if (sx
*sx
+ sy
*sy
> xx
*xx
+yy
*yy
) {
1570 curvature
= -curvature
;
1572 if (curvature
!= 0) {
1574 sx
= x0
< x2
? 1 : -1;
1577 sy
= y0
< y2
? 1 : -1;
1582 if (curvature
* sx
* sy
< 0) {
1586 curvature
= -curvature
;
1588 dx
= 4 * sy
* curvature
* (x1
- x0
) + xx
- xy
;
1589 dy
= 4 * sx
* curvature
* (y0
- y1
) + yy
- xy
;
1594 for (i
= 0; i
<= width
; i
++)
1595 gfx_fb_setpixel(x0
+ i
, y0
);
1596 if (x0
== x2
&& y0
== y2
)
1597 return; /* last pixel -> curve finished */
1611 } while (dy
< dx
); /* gradient negates -> algorithm fails */
1613 gfx_fb_line(x0
, y0
, x2
, y2
, width
);
1617 * draw rectangle using terminal coordinates and current foreground color.
1620 gfx_term_drawrect(uint32_t ux1
, uint32_t uy1
, uint32_t ux2
, uint32_t uy2
)
1625 uint32_t vf_width
, vf_height
;
1628 if (gfx_state
.tg_fb_type
== FB_TEXT
)
1631 vf_width
= gfx_state
.tg_font
.vf_width
;
1632 vf_height
= gfx_state
.tg_font
.vf_height
;
1633 width
= vf_width
/ 4; /* line width */
1634 xshift
= (vf_width
- width
) / 2;
1635 yshift
= (vf_height
- width
) / 2;
1637 /* Shift coordinates */
1645 /* mark area used in terminal */
1646 r
.tr_begin
.tp_col
= ux1
;
1647 r
.tr_begin
.tp_row
= uy1
;
1648 r
.tr_end
.tp_col
= ux2
+ 1;
1649 r
.tr_end
.tp_row
= uy2
+ 1;
1651 term_image_display(&gfx_state
, &r
);
1654 * Draw horizontal lines width points thick, shifted from outer edge.
1656 x1
= (ux1
+ 1) * vf_width
+ gfx_state
.tg_origin
.tp_col
;
1657 y1
= uy1
* vf_height
+ gfx_state
.tg_origin
.tp_row
+ yshift
;
1658 x2
= ux2
* vf_width
+ gfx_state
.tg_origin
.tp_col
;
1659 gfx_fb_drawrect(x1
, y1
, x2
, y1
+ width
, 1);
1660 y2
= uy2
* vf_height
+ gfx_state
.tg_origin
.tp_row
;
1661 y2
+= vf_height
- yshift
- width
;
1662 gfx_fb_drawrect(x1
, y2
, x2
, y2
+ width
, 1);
1665 * Draw vertical lines width points thick, shifted from outer edge.
1667 x1
= ux1
* vf_width
+ gfx_state
.tg_origin
.tp_col
+ xshift
;
1668 y1
= uy1
* vf_height
+ gfx_state
.tg_origin
.tp_row
;
1670 y2
= uy2
* vf_height
+ gfx_state
.tg_origin
.tp_row
;
1671 gfx_fb_drawrect(x1
, y1
, x1
+ width
, y2
, 1);
1672 x1
= ux2
* vf_width
+ gfx_state
.tg_origin
.tp_col
;
1673 x1
+= vf_width
- xshift
- width
;
1674 gfx_fb_drawrect(x1
, y1
, x1
+ width
, y2
, 1);
1676 /* Draw upper left corner. */
1677 x1
= ux1
* vf_width
+ gfx_state
.tg_origin
.tp_col
+ xshift
;
1678 y1
= uy1
* vf_height
+ gfx_state
.tg_origin
.tp_row
;
1681 x2
= ux1
* vf_width
+ gfx_state
.tg_origin
.tp_col
;
1683 y2
= uy1
* vf_height
+ gfx_state
.tg_origin
.tp_row
+ yshift
;
1684 for (i
= 0; i
<= width
; i
++)
1685 gfx_fb_bezier(x1
+ i
, y1
, x1
+ i
, y2
+ i
, x2
, y2
+ i
, width
-i
);
1687 /* Draw lower left corner. */
1688 x1
= ux1
* vf_width
+ gfx_state
.tg_origin
.tp_col
;
1690 y1
= uy2
* vf_height
+ gfx_state
.tg_origin
.tp_row
;
1691 y1
+= vf_height
- yshift
;
1692 x2
= ux1
* vf_width
+ gfx_state
.tg_origin
.tp_col
+ xshift
;
1693 y2
= uy2
* vf_height
+ gfx_state
.tg_origin
.tp_row
;
1694 for (i
= 0; i
<= width
; i
++)
1695 gfx_fb_bezier(x1
, y1
- i
, x2
+ i
, y1
- i
, x2
+ i
, y2
, width
-i
);
1697 /* Draw upper right corner. */
1698 x1
= ux2
* vf_width
+ gfx_state
.tg_origin
.tp_col
;
1699 y1
= uy1
* vf_height
+ gfx_state
.tg_origin
.tp_row
+ yshift
;
1700 x2
= ux2
* vf_width
+ gfx_state
.tg_origin
.tp_col
;
1701 x2
+= vf_width
- xshift
- width
;
1702 y2
= uy1
* vf_height
+ gfx_state
.tg_origin
.tp_row
;
1704 for (i
= 0; i
<= width
; i
++)
1705 gfx_fb_bezier(x1
, y1
+ i
, x2
+ i
, y1
+ i
, x2
+ i
, y2
, width
-i
);
1707 /* Draw lower right corner. */
1708 x1
= ux2
* vf_width
+ gfx_state
.tg_origin
.tp_col
;
1709 y1
= uy2
* vf_height
+ gfx_state
.tg_origin
.tp_row
;
1710 y1
+= vf_height
- yshift
;
1711 x2
= ux2
* vf_width
+ gfx_state
.tg_origin
.tp_col
;
1712 x2
+= vf_width
- xshift
- width
;
1713 y2
= uy2
* vf_height
+ gfx_state
.tg_origin
.tp_row
;
1714 for (i
= 0; i
<= width
; i
++)
1715 gfx_fb_bezier(x1
, y1
- i
, x2
+ i
, y1
- i
, x2
+ i
, y2
, width
-i
);
1719 gfx_fb_putimage(png_t
*png
, uint32_t ux1
, uint32_t uy1
, uint32_t ux2
,
1720 uint32_t uy2
, uint32_t flags
)
1723 EFI_GRAPHICS_OUTPUT_BLT_PIXEL
*p
;
1725 struct paletteentry
*p
;
1728 uint32_t i
, j
, x
, y
, fheight
, fwidth
;
1735 trace
= (flags
& FL_PUTIMAGE_DEBUG
) != 0;
1737 if (gfx_state
.tg_fb_type
== FB_TEXT
) {
1739 printf("Framebuffer not active.\n");
1743 if (png
->color_type
!= PNG_TRUECOLOR_ALPHA
) {
1745 printf("Not truecolor image.\n");
1749 if (ux1
> gfx_state
.tg_fb
.fb_width
||
1750 uy1
> gfx_state
.tg_fb
.fb_height
) {
1752 printf("Top left coordinate off screen.\n");
1756 if (png
->width
> UINT16_MAX
|| png
->height
> UINT16_MAX
) {
1758 printf("Image too large.\n");
1762 if (png
->width
< 1 || png
->height
< 1) {
1764 printf("Image too small.\n");
1769 * If 0 was passed for either ux2 or uy2, then calculate the missing
1770 * part of the bottom right coordinate.
1773 if (ux2
== 0 && uy2
== 0) {
1774 /* Both 0, use the native resolution of the image */
1775 ux2
= ux1
+ png
->width
;
1776 uy2
= uy1
+ png
->height
;
1778 } else if (ux2
== 0) {
1779 /* Set ux2 from uy2/uy1 to maintain aspect ratio */
1780 ux2
= ux1
+ (png
->width
* (uy2
- uy1
)) / png
->height
;
1781 } else if (uy2
== 0) {
1782 /* Set uy2 from ux2/ux1 to maintain aspect ratio */
1783 uy2
= uy1
+ (png
->height
* (ux2
- ux1
)) / png
->width
;
1786 if (ux2
> gfx_state
.tg_fb
.fb_width
||
1787 uy2
> gfx_state
.tg_fb
.fb_height
) {
1789 printf("Bottom right coordinate off screen.\n");
1794 fheight
= uy2
- uy1
;
1797 * If the original image dimensions have been passed explicitly,
1800 if (fwidth
== png
->width
&& fheight
== png
->height
)
1805 * No top left X co-ordinate (real coordinates start at 1),
1806 * place as far right as it will fit.
1808 ux2
= gfx_state
.tg_fb
.fb_width
- gfx_state
.tg_origin
.tp_col
;
1814 * No top left Y co-ordinate (real coordinates start at 1),
1815 * place as far down as it will fit.
1817 uy2
= gfx_state
.tg_fb
.fb_height
- gfx_state
.tg_origin
.tp_row
;
1818 uy1
= uy2
- fheight
;
1821 if (ux1
>= ux2
|| uy1
>= uy2
) {
1823 printf("Image dimensions reversed.\n");
1827 if (fwidth
< 2 || fheight
< 2) {
1829 printf("Target area too small\n");
1834 printf("Image %ux%u -> %ux%u @%ux%u\n",
1835 png
->width
, png
->height
, fwidth
, fheight
, ux1
, uy1
);
1837 rect
.tr_begin
.tp_col
= ux1
/ gfx_state
.tg_font
.vf_width
;
1838 rect
.tr_begin
.tp_row
= uy1
/ gfx_state
.tg_font
.vf_height
;
1839 rect
.tr_end
.tp_col
= (ux1
+ fwidth
) / gfx_state
.tg_font
.vf_width
;
1840 rect
.tr_end
.tp_row
= (uy1
+ fheight
) / gfx_state
.tg_font
.vf_height
;
1843 * mark area used in terminal
1845 if (!(flags
& FL_PUTIMAGE_NOSCROLL
))
1846 term_image_display(&gfx_state
, &rect
);
1848 if ((flags
& FL_PUTIMAGE_BORDER
))
1849 gfx_fb_drawrect(ux1
, uy1
, ux2
, uy2
, 0);
1851 data
= malloc(fwidth
* fheight
* sizeof(*p
));
1855 printf("Out of memory.\n");
1860 * Build image for our framebuffer.
1863 /* Helper to calculate the pixel index from the source png */
1864 #define GETPIXEL(xx, yy) (((yy) * png->width + (xx)) * png->bpp)
1867 * For each of the x and y directions, calculate the number of pixels
1868 * in the source image that correspond to a single pixel in the target.
1869 * Use fixed-point arithmetic with 16-bits for each of the integer and
1872 const uint32_t wcstep
= ((png
->width
- 1) << 16) / (fwidth
- 1);
1873 const uint32_t hcstep
= ((png
->height
- 1) << 16) / (fheight
- 1);
1875 rs
= 8 - (fls(gfx_state
.tg_fb
.fb_mask_red
) -
1876 ffs(gfx_state
.tg_fb
.fb_mask_red
) + 1);
1877 gs
= 8 - (fls(gfx_state
.tg_fb
.fb_mask_green
) -
1878 ffs(gfx_state
.tg_fb
.fb_mask_green
) + 1);
1879 bs
= 8 - (fls(gfx_state
.tg_fb
.fb_mask_blue
) -
1880 ffs(gfx_state
.tg_fb
.fb_mask_blue
) + 1);
1883 for (y
= 0; y
< fheight
; y
++) {
1884 uint32_t hc2
= (hc
>> 9) & 0x7f;
1885 uint32_t hc1
= 0x80 - hc2
;
1887 uint32_t offset_y
= hc
>> 16;
1888 uint32_t offset_y1
= offset_y
+ 1;
1891 for (x
= 0; x
< fwidth
; x
++) {
1892 uint32_t wc2
= (wc
>> 9) & 0x7f;
1893 uint32_t wc1
= 0x80 - wc2
;
1895 uint32_t offset_x
= wc
>> 16;
1896 uint32_t offset_x1
= offset_x
+ 1;
1898 /* Target pixel index */
1904 g
= png
->image
[i
+ 1];
1905 b
= png
->image
[i
+ 2];
1906 a
= png
->image
[i
+ 3];
1910 uint32_t p00
= GETPIXEL(offset_x
, offset_y
);
1911 uint32_t p01
= GETPIXEL(offset_x
, offset_y1
);
1912 uint32_t p10
= GETPIXEL(offset_x1
, offset_y
);
1913 uint32_t p11
= GETPIXEL(offset_x1
, offset_y1
);
1916 * Given a 2x2 array of pixels in the source
1917 * image, combine them to produce a single
1918 * value for the pixel in the target image.
1919 * Each column of pixels is combined using
1920 * a weighted average where the top and bottom
1921 * pixels contribute hc1 and hc2 respectively.
1922 * The calculation for bottom pixel pB and
1924 * (pT * hc1 + pB * hc2) / (hc1 + hc2)
1925 * Once the values are determined for the two
1926 * columns of pixels, then the columns are
1927 * averaged together in the same way but using
1928 * wc1 and wc2 for the weightings.
1930 * Since hc1 and hc2 are chosen so that
1931 * hc1 + hc2 == 128 (and same for wc1 + wc2),
1932 * the >> 14 below is a quick way to divide by
1933 * (hc1 + hc2) * (wc1 + wc2)
1935 for (i
= 0; i
< 4; i
++)
1937 (png
->image
[p00
+ i
] * hc1
+
1938 png
->image
[p01
+ i
] * hc2
) * wc1
+
1939 (png
->image
[p10
+ i
] * hc1
+
1940 png
->image
[p11
+ i
] * hc2
) * wc2
)
1950 printf("r/g/b: %x/%x/%x\n", r
, g
, b
);
1952 * Rough colorspace reduction for 15/16 bit colors.
1955 p
[j
].Green
= g
>> gs
;
1956 p
[j
].Blue
= b
>> bs
;
1964 gfx_fb_cons_display(ux1
, uy1
, fwidth
, fheight
, data
);
1970 * Reset font flags to FONT_AUTO.
1973 reset_font_flags(void)
1975 struct fontlist
*fl
;
1977 STAILQ_FOREACH(fl
, &fonts
, font_next
) {
1978 fl
->font_flags
= FONT_AUTO
;
1982 /* Return w^2 + h^2 or 0, if the dimensions are unknown */
1984 edid_diagonal_squared(void)
1988 if (edid_info
== NULL
)
1991 w
= edid_info
->display
.max_horizontal_image_size
;
1992 h
= edid_info
->display
.max_vertical_image_size
;
1994 /* If either one is 0, we have aspect ratio, not size */
1995 if (w
== 0 || h
== 0)
1999 * some monitors encode the aspect ratio instead of the physical size.
2001 if ((w
== 16 && h
== 9) || (w
== 16 && h
== 10) ||
2002 (w
== 4 && h
== 3) || (w
== 5 && h
== 4))
2006 * translate cm to inch, note we scale by 100 here.
2011 /* Return w^2 + h^2 */
2012 return (w
* w
+ h
* h
);
2016 * calculate pixels per inch.
2023 di
= edid_diagonal_squared();
2027 dp
= gfx_state
.tg_fb
.fb_width
*
2028 gfx_state
.tg_fb
.fb_width
+
2029 gfx_state
.tg_fb
.fb_height
*
2030 gfx_state
.tg_fb
.fb_height
;
2032 return (isqrt(dp
/ di
));
2036 * Calculate font size from density independent pixels (dp):
2037 * ((16dp * ppi) / 160) * display_factor.
2038 * Here we are using fixed constants: 1dp == 160 ppi and
2041 * We are rounding font size up and are searching for font which is
2042 * not smaller than calculated size value.
2044 static vt_font_bitmap_data_t
*
2048 vt_font_bitmap_data_t
*font
= NULL
;
2049 struct fontlist
*fl
, *next
;
2051 /* Text mode is not supported here. */
2052 if (gfx_state
.tg_fb_type
== FB_TEXT
)
2055 ppi
= gfx_get_ppi();
2060 * We will search for 16dp font.
2061 * We are using scale up by 10 for roundup.
2063 size
= (16 * ppi
* 10) / 160;
2064 /* Apply display factor 2. */
2065 size
= roundup(size
* 2, 10) / 10;
2067 STAILQ_FOREACH(fl
, &fonts
, font_next
) {
2068 next
= STAILQ_NEXT(fl
, font_next
);
2071 * If this is last font or, if next font is smaller,
2072 * we have our font. Make sure, it actually is loaded.
2074 if (next
== NULL
|| next
->font_data
->vfbd_height
< size
) {
2075 font
= fl
->font_data
;
2076 if (font
->vfbd_font
== NULL
||
2077 fl
->font_flags
== FONT_RELOAD
) {
2078 if (fl
->font_load
!= NULL
&&
2079 fl
->font_name
!= NULL
)
2080 font
= fl
->font_load(fl
->font_name
);
2089 static vt_font_bitmap_data_t
*
2090 set_font(teken_unit_t
*rows
, teken_unit_t
*cols
, teken_unit_t h
, teken_unit_t w
)
2092 vt_font_bitmap_data_t
*font
= NULL
;
2093 struct fontlist
*fl
;
2094 unsigned height
= h
;
2098 * First check for manually loaded font.
2100 STAILQ_FOREACH(fl
, &fonts
, font_next
) {
2101 if (fl
->font_flags
== FONT_MANUAL
) {
2102 font
= fl
->font_data
;
2103 if (font
->vfbd_font
== NULL
&& fl
->font_load
!= NULL
&&
2104 fl
->font_name
!= NULL
) {
2105 font
= fl
->font_load(fl
->font_name
);
2107 if (font
== NULL
|| font
->vfbd_font
== NULL
)
2114 font
= gfx_get_font();
2117 *rows
= height
/ font
->vfbd_height
;
2118 *cols
= width
/ font
->vfbd_width
;
2123 * Find best font for these dimensions, or use default.
2124 * If height >= VT_FB_MAX_HEIGHT and width >= VT_FB_MAX_WIDTH,
2125 * do not use smaller font than our DEFAULT_FONT_DATA.
2127 STAILQ_FOREACH(fl
, &fonts
, font_next
) {
2128 font
= fl
->font_data
;
2129 if ((*rows
* font
->vfbd_height
<= height
&&
2130 *cols
* font
->vfbd_width
<= width
) ||
2131 (height
>= VT_FB_MAX_HEIGHT
&&
2132 width
>= VT_FB_MAX_WIDTH
&&
2133 font
->vfbd_height
== DEFAULT_FONT_DATA
.vfbd_height
&&
2134 font
->vfbd_width
== DEFAULT_FONT_DATA
.vfbd_width
)) {
2135 if (font
->vfbd_font
== NULL
||
2136 fl
->font_flags
== FONT_RELOAD
) {
2137 if (fl
->font_load
!= NULL
&&
2138 fl
->font_name
!= NULL
) {
2139 font
= fl
->font_load(fl
->font_name
);
2144 *rows
= height
/ font
->vfbd_height
;
2145 *cols
= width
/ font
->vfbd_width
;
2153 * We have fonts sorted smallest last, try it before
2154 * falling back to builtin.
2156 fl
= STAILQ_LAST(&fonts
, fontlist
, font_next
);
2157 if (fl
!= NULL
&& fl
->font_load
!= NULL
&&
2158 fl
->font_name
!= NULL
) {
2159 font
= fl
->font_load(fl
->font_name
);
2162 font
= &DEFAULT_FONT_DATA
;
2164 *rows
= height
/ font
->vfbd_height
;
2165 *cols
= width
/ font
->vfbd_width
;
2174 char clear
[] = { '\033', 'c' };
2176 /* Reset terminal */
2177 teken_input(&gfx_state
.tg_teken
, clear
, sizeof(clear
));
2178 gfx_state
.tg_functions
->tf_param(&gfx_state
, TP_SHOWCURSOR
, 0);
2182 setup_font(teken_gfx_t
*state
, teken_unit_t height
, teken_unit_t width
)
2184 vt_font_bitmap_data_t
*font_data
;
2185 teken_pos_t
*tp
= &state
->tg_tp
;
2190 * set_font() will select a appropriate sized font for
2191 * the number of rows and columns selected. If we don't
2192 * have a font that will fit, then it will use the
2193 * default builtin font and adjust the rows and columns
2194 * to fit on the screen.
2196 font_data
= set_font(&tp
->tp_row
, &tp
->tp_col
, height
, width
);
2198 if (font_data
== NULL
)
2199 panic("out of memory");
2201 for (i
= 0; i
< VFNT_MAPS
; i
++) {
2202 state
->tg_font
.vf_map
[i
] =
2203 font_data
->vfbd_font
->vf_map
[i
];
2204 state
->tg_font
.vf_map_count
[i
] =
2205 font_data
->vfbd_font
->vf_map_count
[i
];
2208 state
->tg_font
.vf_bytes
= font_data
->vfbd_font
->vf_bytes
;
2209 state
->tg_font
.vf_height
= font_data
->vfbd_font
->vf_height
;
2210 state
->tg_font
.vf_width
= font_data
->vfbd_font
->vf_width
;
2212 snprintf(env
, sizeof (env
), "%ux%u",
2213 state
->tg_font
.vf_width
, state
->tg_font
.vf_height
);
2214 env_setenv("screen.font", EV_VOLATILE
| EV_NOHOOK
,
2215 env
, font_set
, env_nounset
);
2218 /* Binary search for the glyph. Return 0 if not found. */
2220 font_bisearch(const vfnt_map_t
*map
, uint32_t len
, teken_char_t src
)
2222 unsigned min
, mid
, max
;
2227 /* Empty font map. */
2230 /* Character below minimal entry. */
2231 if (src
< map
[0].vfm_src
)
2233 /* Optimization: ASCII characters occur very often. */
2234 if (src
<= map
[0].vfm_src
+ map
[0].vfm_len
)
2235 return (src
- map
[0].vfm_src
+ map
[0].vfm_dst
);
2236 /* Character above maximum entry. */
2237 if (src
> map
[max
].vfm_src
+ map
[max
].vfm_len
)
2240 /* Binary search. */
2241 while (max
>= min
) {
2242 mid
= (min
+ max
) / 2;
2243 if (src
< map
[mid
].vfm_src
)
2245 else if (src
> map
[mid
].vfm_src
+ map
[mid
].vfm_len
)
2248 return (src
- map
[mid
].vfm_src
+ map
[mid
].vfm_dst
);
2255 * Return glyph bitmap. If glyph is not found, we will return bitmap
2256 * for the first (offset 0) glyph.
2259 font_lookup(const struct vt_font
*vf
, teken_char_t c
, const teken_attr_t
*a
)
2264 /* Substitute bold with normal if not found. */
2265 if (a
->ta_format
& TF_BOLD
) {
2266 dst
= font_bisearch(vf
->vf_map
[VFNT_MAP_BOLD
],
2267 vf
->vf_map_count
[VFNT_MAP_BOLD
], c
);
2271 dst
= font_bisearch(vf
->vf_map
[VFNT_MAP_NORMAL
],
2272 vf
->vf_map_count
[VFNT_MAP_NORMAL
], c
);
2275 stride
= howmany(vf
->vf_width
, 8) * vf
->vf_height
;
2276 return (&vf
->vf_bytes
[dst
* stride
]);
2280 load_mapping(int fd
, struct vt_font
*fp
, int n
)
2286 if (fp
->vf_map_count
[n
] == 0)
2289 size
= fp
->vf_map_count
[n
] * sizeof(*mp
);
2295 rv
= read(fd
, mp
, size
);
2296 if (rv
< 0 || (size_t)rv
!= size
) {
2297 free(fp
->vf_map
[n
]);
2298 fp
->vf_map
[n
] = NULL
;
2302 for (i
= 0; i
< fp
->vf_map_count
[n
]; i
++) {
2303 mp
[i
].vfm_src
= be32toh(mp
[i
].vfm_src
);
2304 mp
[i
].vfm_dst
= be16toh(mp
[i
].vfm_dst
);
2305 mp
[i
].vfm_len
= be16toh(mp
[i
].vfm_len
);
2311 builtin_mapping(struct vt_font
*fp
, int n
)
2314 struct vfnt_map
*mp
;
2319 if (fp
->vf_map_count
[n
] == 0)
2322 size
= fp
->vf_map_count
[n
] * sizeof(*mp
);
2328 memcpy(mp
, DEFAULT_FONT_DATA
.vfbd_font
->vf_map
[n
], size
);
2333 * Load font from builtin or from file.
2334 * We do need special case for builtin because the builtin font glyphs
2335 * are compressed and we do need to uncompress them.
2336 * Having single load_font() for both cases will help us to simplify
2337 * font switch handling.
2339 static vt_font_bitmap_data_t
*
2340 load_font(char *path
)
2344 struct font_header fh
;
2345 struct fontlist
*fl
;
2346 vt_font_bitmap_data_t
*bp
;
2351 /* Get our entry from the font list. */
2352 STAILQ_FOREACH(fl
, &fonts
, font_next
) {
2353 if (strcmp(fl
->font_name
, path
) == 0)
2357 return (NULL
); /* Should not happen. */
2360 if (bp
->vfbd_font
!= NULL
&& fl
->font_flags
!= FONT_RELOAD
)
2365 * Special case for builtin font.
2366 * Builtin font is the very first font we load, we do not have
2367 * previous loads to be released.
2369 if (fl
->font_flags
== FONT_BUILTIN
) {
2370 if ((fp
= calloc(1, sizeof(struct vt_font
))) == NULL
)
2373 fp
->vf_width
= DEFAULT_FONT_DATA
.vfbd_width
;
2374 fp
->vf_height
= DEFAULT_FONT_DATA
.vfbd_height
;
2376 fp
->vf_bytes
= malloc(DEFAULT_FONT_DATA
.vfbd_uncompressed_size
);
2377 if (fp
->vf_bytes
== NULL
) {
2382 bp
->vfbd_uncompressed_size
=
2383 DEFAULT_FONT_DATA
.vfbd_uncompressed_size
;
2384 bp
->vfbd_compressed_size
=
2385 DEFAULT_FONT_DATA
.vfbd_compressed_size
;
2387 if (lz4_decompress(DEFAULT_FONT_DATA
.vfbd_compressed_data
,
2389 DEFAULT_FONT_DATA
.vfbd_compressed_size
,
2390 DEFAULT_FONT_DATA
.vfbd_uncompressed_size
, 0) != 0) {
2396 for (i
= 0; i
< VFNT_MAPS
; i
++) {
2397 fp
->vf_map_count
[i
] =
2398 DEFAULT_FONT_DATA
.vfbd_font
->vf_map_count
[i
];
2399 if (builtin_mapping(fp
, i
) != 0)
2407 fd
= open(path
, O_RDONLY
);
2412 rv
= read(fd
, &fh
, size
);
2413 if (rv
< 0 || (size_t)rv
!= size
) {
2417 if (memcmp(fh
.fh_magic
, FONT_HEADER_MAGIC
, sizeof(fh
.fh_magic
)) != 0) {
2421 if ((fp
= calloc(1, sizeof(struct vt_font
))) == NULL
) {
2425 for (i
= 0; i
< VFNT_MAPS
; i
++)
2426 fp
->vf_map_count
[i
] = be32toh(fh
.fh_map_count
[i
]);
2428 glyphs
= be32toh(fh
.fh_glyph_count
);
2429 fp
->vf_width
= fh
.fh_width
;
2430 fp
->vf_height
= fh
.fh_height
;
2432 size
= howmany(fp
->vf_width
, 8) * fp
->vf_height
* glyphs
;
2433 bp
->vfbd_uncompressed_size
= size
;
2434 if ((fp
->vf_bytes
= malloc(size
)) == NULL
)
2437 rv
= read(fd
, fp
->vf_bytes
, size
);
2438 if (rv
< 0 || (size_t)rv
!= size
)
2440 for (i
= 0; i
< VFNT_MAPS
; i
++) {
2441 if (load_mapping(fd
, fp
, i
) != 0)
2446 * Reset builtin flag now as we have full font loaded.
2448 if (fl
->font_flags
== FONT_BUILTIN
)
2449 fl
->font_flags
= FONT_AUTO
;
2452 * Release previously loaded entries. We can do this now, as
2453 * the new font is loaded. Note, there can be no console
2454 * output till the new font is in place and teken is notified.
2455 * We do need to keep fl->font_data for glyph dimensions.
2457 STAILQ_FOREACH(fl
, &fonts
, font_next
) {
2458 if (fl
->font_data
->vfbd_font
== NULL
)
2461 for (i
= 0; i
< VFNT_MAPS
; i
++)
2462 free(fl
->font_data
->vfbd_font
->vf_map
[i
]);
2463 free(fl
->font_data
->vfbd_font
->vf_bytes
);
2464 free(fl
->font_data
->vfbd_font
);
2465 fl
->font_data
->vfbd_font
= NULL
;
2469 bp
->vfbd_compressed_size
= 0;
2477 for (i
= 0; i
< VFNT_MAPS
; i
++)
2478 free(fp
->vf_map
[i
]);
2487 SLIST_ENTRY(name_entry
) n_entry
;
2490 SLIST_HEAD(name_list
, name_entry
);
2492 /* Read font names from index file. */
2493 static struct name_list
*
2494 read_list(char *fonts
)
2496 struct name_list
*nl
;
2497 struct name_entry
*np
;
2504 dir
= strdup(fonts
);
2508 ptr
= strrchr(dir
, '/');
2511 fd
= open(fonts
, O_RDONLY
);
2515 nl
= malloc(sizeof(*nl
));
2522 while ((len
= fgetstr(buf
, sizeof (buf
), fd
)) >= 0) {
2523 if (*buf
== '#' || *buf
== '\0')
2526 if (bcmp(buf
, "MENU", 4) == 0)
2529 if (bcmp(buf
, "FONT", 4) == 0)
2532 ptr
= strchr(buf
, ':');
2538 np
= malloc(sizeof(*np
));
2541 return (nl
); /* return what we have */
2543 if (asprintf(&np
->n_name
, "%s/%s", dir
, buf
) < 0) {
2546 return (nl
); /* return what we have */
2548 SLIST_INSERT_HEAD(nl
, np
, n_entry
);
2556 * Read the font properties and insert new entry into the list.
2557 * The font list is built in descending order.
2560 insert_font(char *name
, FONT_FLAGS flags
)
2562 struct font_header fh
;
2563 struct fontlist
*fp
, *previous
, *entry
, *next
;
2572 if (flags
== FONT_BUILTIN
) {
2574 * We only install builtin font once, while setting up
2575 * initial console. Since this will happen very early,
2576 * we assume asprintf will not fail. Once we have access to
2577 * files, the builtin font will be replaced by font loaded
2580 if (!STAILQ_EMPTY(&fonts
))
2583 fh
.fh_width
= DEFAULT_FONT_DATA
.vfbd_width
;
2584 fh
.fh_height
= DEFAULT_FONT_DATA
.vfbd_height
;
2586 (void) asprintf(&font_name
, "%dx%d",
2587 DEFAULT_FONT_DATA
.vfbd_width
,
2588 DEFAULT_FONT_DATA
.vfbd_height
);
2590 fd
= open(name
, O_RDONLY
);
2593 rv
= read(fd
, &fh
, sizeof(fh
));
2595 if (rv
< 0 || (size_t)rv
!= sizeof(fh
))
2598 if (memcmp(fh
.fh_magic
, FONT_HEADER_MAGIC
,
2599 sizeof(fh
.fh_magic
)) != 0)
2601 font_name
= strdup(name
);
2604 if (font_name
== NULL
)
2608 * If we have an entry with the same glyph dimensions, replace
2609 * the file name and mark us. We only support unique dimensions.
2611 STAILQ_FOREACH(entry
, &fonts
, font_next
) {
2612 if (fh
.fh_width
== entry
->font_data
->vfbd_width
&&
2613 fh
.fh_height
== entry
->font_data
->vfbd_height
) {
2614 free(entry
->font_name
);
2615 entry
->font_name
= font_name
;
2616 entry
->font_flags
= FONT_RELOAD
;
2622 fp
= calloc(sizeof(*fp
), 1);
2627 fp
->font_data
= calloc(sizeof(*fp
->font_data
), 1);
2628 if (fp
->font_data
== NULL
) {
2633 fp
->font_name
= font_name
;
2634 fp
->font_flags
= flags
;
2635 fp
->font_load
= load_font
;
2636 fp
->font_data
->vfbd_width
= fh
.fh_width
;
2637 fp
->font_data
->vfbd_height
= fh
.fh_height
;
2639 if (STAILQ_EMPTY(&fonts
)) {
2640 STAILQ_INSERT_HEAD(&fonts
, fp
, font_next
);
2646 size
= fp
->font_data
->vfbd_width
* fp
->font_data
->vfbd_height
;
2648 STAILQ_FOREACH(entry
, &fonts
, font_next
) {
2649 vt_font_bitmap_data_t
*bd
;
2651 bd
= entry
->font_data
;
2652 /* Should fp be inserted before the entry? */
2653 if (size
> bd
->vfbd_width
* bd
->vfbd_height
) {
2654 if (previous
== NULL
) {
2655 STAILQ_INSERT_HEAD(&fonts
, fp
, font_next
);
2657 STAILQ_INSERT_AFTER(&fonts
, previous
, fp
,
2663 next
= STAILQ_NEXT(entry
, font_next
);
2665 size
> next
->font_data
->vfbd_width
*
2666 next
->font_data
->vfbd_height
) {
2667 STAILQ_INSERT_AFTER(&fonts
, entry
, fp
, font_next
);
2678 font_set(struct env_var
*ev __unused
, int flags __unused
, const void *value
)
2680 struct fontlist
*fl
;
2682 unsigned long x
= 0, y
= 0;
2685 * Attempt to extract values from "XxY" string. In case of error,
2686 * we have unmaching glyph dimensions and will just output the
2689 if (value
!= NULL
) {
2690 x
= strtoul(value
, &eptr
, 10);
2692 y
= strtoul(eptr
+ 1, &eptr
, 10);
2694 STAILQ_FOREACH(fl
, &fonts
, font_next
) {
2695 if (fl
->font_data
->vfbd_width
== x
&&
2696 fl
->font_data
->vfbd_height
== y
)
2700 /* Reset any FONT_MANUAL flag. */
2703 /* Mark this font manually loaded */
2704 fl
->font_flags
= FONT_MANUAL
;
2705 cons_update_mode(gfx_state
.tg_fb_type
!= FB_TEXT
);
2709 printf("Available fonts:\n");
2710 STAILQ_FOREACH(fl
, &fonts
, font_next
) {
2711 printf(" %dx%d\n", fl
->font_data
->vfbd_width
,
2712 fl
->font_data
->vfbd_height
);
2718 bios_text_font(bool use_vga_font
)
2721 (void) insert_font(VGA_8X16_FONT
, FONT_MANUAL
);
2723 (void) insert_font(DEFAULT_8X16_FONT
, FONT_MANUAL
);
2727 autoload_font(bool bios
)
2729 struct name_list
*nl
;
2730 struct name_entry
*np
;
2734 nl
= read_list("/boot/fonts/INDEX.fonts");
2738 while (!SLIST_EMPTY(nl
)) {
2739 np
= SLIST_FIRST(nl
);
2740 SLIST_REMOVE_HEAD(nl
, n_entry
);
2741 if (insert_font(np
->n_name
, FONT_AUTO
) == false)
2742 printf("failed to add font: %s\n", np
->n_name
);
2748 * If vga text mode was requested, load vga.font (8x16 bold) font.
2751 bios_text_font(true);
2754 (void) cons_update_mode(gfx_state
.tg_fb_type
!= FB_TEXT
);
2759 COMMAND_SET(load_font
, "loadfont", "load console font from file", command_font
);
2762 command_font(int argc
, char *argv
[])
2765 struct fontlist
*fl
;
2766 vt_font_bitmap_data_t
*bd
;
2774 while ((c
= getopt(argc
, argv
, "l")) != -1) {
2788 if (argc
> 1 || (list
&& argc
!= 0)) {
2789 printf("Usage: loadfont [-l] | [file.fnt]\n");
2794 STAILQ_FOREACH(fl
, &fonts
, font_next
) {
2795 printf("font %s: %dx%d%s\n", fl
->font_name
,
2796 fl
->font_data
->vfbd_width
,
2797 fl
->font_data
->vfbd_height
,
2798 fl
->font_data
->vfbd_font
== NULL
? "" : " loaded");
2807 char *name
= argv
[0];
2809 if (insert_font(name
, FONT_MANUAL
) == false) {
2810 printf("loadfont error: failed to load: %s\n", name
);
2814 (void) cons_update_mode(gfx_state
.tg_fb_type
!= FB_TEXT
);
2820 * Walk entire font list, release any loaded font, and set
2821 * autoload flag. The font list does have at least the builtin
2824 STAILQ_FOREACH(fl
, &fonts
, font_next
) {
2825 if (fl
->font_data
->vfbd_font
!= NULL
) {
2829 * Note the setup_font() is releasing
2832 for (i
= 0; i
< VFNT_MAPS
; i
++)
2833 free(bd
->vfbd_font
->vf_map
[i
]);
2834 free(fl
->font_data
->vfbd_font
);
2835 fl
->font_data
->vfbd_font
= NULL
;
2836 fl
->font_data
->vfbd_uncompressed_size
= 0;
2837 fl
->font_flags
= FONT_AUTO
;
2840 (void) cons_update_mode(gfx_state
.tg_fb_type
!= FB_TEXT
);
2846 gfx_get_edid_resolution(struct vesa_edid_info
*edid
, edid_res_list_t
*res
)
2848 struct resolution
*rp
, *p
;
2851 * Walk detailed timings tables (4).
2853 if ((edid
->display
.supported_features
2854 & EDID_FEATURE_PREFERRED_TIMING_MODE
) != 0) {
2855 /* Walk detailed timing descriptors (4) */
2856 for (int i
= 0; i
< DET_TIMINGS
; i
++) {
2858 * Reserved value 0 is not used for display descriptor.
2860 if (edid
->detailed_timings
[i
].pixel_clock
== 0)
2862 if ((rp
= malloc(sizeof(*rp
))) == NULL
)
2864 rp
->width
= GET_EDID_INFO_WIDTH(edid
, i
);
2865 rp
->height
= GET_EDID_INFO_HEIGHT(edid
, i
);
2866 if (rp
->width
> 0 && rp
->width
<= EDID_MAX_PIXELS
&&
2867 rp
->height
> 0 && rp
->height
<= EDID_MAX_LINES
)
2868 TAILQ_INSERT_TAIL(res
, rp
, next
);
2875 * Walk standard timings list (8).
2877 for (int i
= 0; i
< STD_TIMINGS
; i
++) {
2878 /* Is this field unused? */
2879 if (edid
->standard_timings
[i
] == 0x0101)
2882 if ((rp
= malloc(sizeof(*rp
))) == NULL
)
2885 rp
->width
= HSIZE(edid
->standard_timings
[i
]);
2886 switch (RATIO(edid
->standard_timings
[i
])) {
2888 rp
->height
= HSIZE(edid
->standard_timings
[i
]);
2889 if (edid
->header
.version
> 1 ||
2890 edid
->header
.revision
> 2) {
2891 rp
->height
= rp
->height
* 10 / 16;
2895 rp
->height
= HSIZE(edid
->standard_timings
[i
]) * 3 / 4;
2898 rp
->height
= HSIZE(edid
->standard_timings
[i
]) * 4 / 5;
2901 rp
->height
= HSIZE(edid
->standard_timings
[i
]) * 9 / 16;
2906 * Create resolution list in decreasing order, except keep
2907 * first entry (preferred timing mode).
2909 TAILQ_FOREACH(p
, res
, next
) {
2910 if (p
->width
* p
->height
< rp
->width
* rp
->height
) {
2911 /* Keep preferred mode first */
2912 if (TAILQ_FIRST(res
) == p
)
2913 TAILQ_INSERT_AFTER(res
, p
, rp
, next
);
2915 TAILQ_INSERT_BEFORE(p
, rp
, next
);
2918 if (TAILQ_NEXT(p
, next
) == NULL
) {
2919 TAILQ_INSERT_TAIL(res
, rp
, next
);
2924 return (!TAILQ_EMPTY(res
));
2928 build_font_module(vm_offset_t addr
)
2930 vt_font_bitmap_data_t
*bd
;
2932 struct preloaded_file
*fp
;
2936 struct font_info fi
;
2937 struct fontlist
*fl
;
2940 if (STAILQ_EMPTY(&fonts
))
2943 /* We can't load first */
2944 if ((file_findfile(NULL
, NULL
)) == NULL
) {
2945 printf("Can not load font module: %s\n",
2946 "the kernel is not loaded");
2950 /* helper pointers */
2952 STAILQ_FOREACH(fl
, &fonts
, font_next
) {
2953 if (gfx_state
.tg_font
.vf_width
== fl
->font_data
->vfbd_width
&&
2954 gfx_state
.tg_font
.vf_height
== fl
->font_data
->vfbd_height
) {
2956 * Kernel does have better built in font.
2958 if (fl
->font_flags
== FONT_BUILTIN
)
2969 fi
.fi_width
= fd
->vf_width
;
2970 checksum
= fi
.fi_width
;
2971 fi
.fi_height
= fd
->vf_height
;
2972 checksum
+= fi
.fi_height
;
2973 fi
.fi_bitmap_size
= bd
->vfbd_uncompressed_size
;
2974 checksum
+= fi
.fi_bitmap_size
;
2976 size
= roundup2(sizeof (struct font_info
), 8);
2977 for (i
= 0; i
< VFNT_MAPS
; i
++) {
2978 fi
.fi_map_count
[i
] = fd
->vf_map_count
[i
];
2979 checksum
+= fi
.fi_map_count
[i
];
2980 size
+= fd
->vf_map_count
[i
] * sizeof (struct vfnt_map
);
2981 size
+= roundup2(size
, 8);
2983 size
+= bd
->vfbd_uncompressed_size
;
2985 fi
.fi_checksum
= -checksum
;
2987 fp
= file_findfile(NULL
, md_kerntype
);
2989 panic("can't find kernel file");
2992 addr
+= archsw
.arch_copyin(&fi
, addr
, sizeof (struct font_info
));
2993 addr
= roundup2(addr
, 8);
2996 for (i
= 0; i
< VFNT_MAPS
; i
++) {
2997 if (fd
->vf_map_count
[i
] != 0) {
2998 addr
+= archsw
.arch_copyin(fd
->vf_map
[i
], addr
,
2999 fd
->vf_map_count
[i
] * sizeof (struct vfnt_map
));
3000 addr
= roundup2(addr
, 8);
3004 /* Copy the bitmap. */
3005 addr
+= archsw
.arch_copyin(fd
->vf_bytes
, addr
, fi
.fi_bitmap_size
);
3007 /* Looks OK so far; populate control structure */
3008 file_addmetadata(fp
, MODINFOMD_FONT
, sizeof(fontp
), &fontp
);
3013 build_splash_module(vm_offset_t addr
)
3015 struct preloaded_file
*fp
;
3016 struct splash_info si
;
3022 /* We can't load first */
3023 if ((file_findfile(NULL
, NULL
)) == NULL
) {
3024 printf("Can not load splash module: %s\n",
3025 "the kernel is not loaded");
3029 fp
= file_findfile(NULL
, md_kerntype
);
3031 panic("can't find kernel file");
3033 splash
= getenv("splash");
3038 if ((error
= png_open(&png
, splash
)) != PNG_NO_ERROR
) {
3042 si
.si_width
= png
.width
;
3043 si
.si_height
= png
.height
;
3044 si
.si_depth
= png
.bpp
;
3046 addr
+= archsw
.arch_copyin(&si
, addr
, sizeof (struct splash_info
));
3047 addr
= roundup2(addr
, 8);
3049 /* Copy the bitmap. */
3050 addr
+= archsw
.arch_copyin(png
.image
, addr
, png
.png_datalen
);
3052 printf("Loading splash ok\n");
3053 file_addmetadata(fp
, MODINFOMD_SPLASH
, sizeof(splashp
), &splashp
);