2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
13 * Copyright 2016 Toomas Soome <tsoome@me.com>
17 * Common functions to implement graphical framebuffer support for console.
20 #include <sys/cdefs.h>
21 #include <sys/param.h>
29 #include <sys/tem_impl.h>
30 #include <sys/consplat.h>
31 #include <sys/visual_io.h>
32 #include <sys/multiboot2.h>
34 #include <sys/endian.h>
37 #include <bootstrap.h>
40 * Global framebuffer struct, to be updated with mode changes.
42 multiboot_tag_framebuffer_t gfx_fb
;
44 /* To support setenv, keep track of inverses and colors. */
45 static int gfx_inverse
= 0;
46 static int gfx_inverse_screen
= 0;
47 static uint8_t gfx_fg
= DEFAULT_ANSI_FOREGROUND
;
48 static uint8_t gfx_bg
= DEFAULT_ANSI_BACKGROUND
;
50 static int gfx_fb_cons_clear(struct vis_consclear
*);
51 static void gfx_fb_cons_copy(struct vis_conscopy
*);
52 static void gfx_fb_cons_display(struct vis_consdisplay
*);
55 static int gfx_gop_cons_clear(uint32_t data
, uint32_t width
, uint32_t height
);
56 static void gfx_gop_cons_copy(struct vis_conscopy
*);
57 static void gfx_gop_cons_display(struct vis_consdisplay
*);
59 static int gfx_bm_cons_clear(uint32_t data
, uint32_t width
, uint32_t height
);
60 static void gfx_bm_cons_copy(struct vis_conscopy
*);
61 static void gfx_bm_cons_display(struct vis_consdisplay
*);
64 * Set default operations to use bitmap based implementation.
65 * In case of UEFI, if GOP is available, we will switch to GOP based
68 * Also note, for UEFI we do attempt to boost the execution by setting
69 * Task Priority Level (TPL) to TPL_NOTIFY, which is highest priority
70 * usable in application.
73 int (*gfx_cons_clear
)(uint32_t, uint32_t, uint32_t);
74 void (*gfx_cons_copy
)(struct vis_conscopy
*);
75 void (*gfx_cons_display
)(struct vis_consdisplay
*);
77 .gfx_cons_clear
= gfx_bm_cons_clear
,
78 .gfx_cons_copy
= gfx_bm_cons_copy
,
79 .gfx_cons_display
= gfx_bm_cons_display
83 * Translate platform specific FB address.
86 gfx_get_fb_address(void)
89 return ((uint8_t *)(uintptr_t)
90 gfx_fb
.framebuffer_common
.framebuffer_addr
);
92 return ((uint8_t *)PTOV((uint32_t)
93 gfx_fb
.framebuffer_common
.framebuffer_addr
& 0xffffffff));
98 * Generic platform callbacks for tem.
101 plat_tem_get_prom_font_size(int *charheight
, int *windowtop
)
108 plat_tem_get_colors(uint8_t *fg
, uint8_t *bg
)
115 plat_tem_get_inverses(int *inverse
, int *inverse_screen
)
117 *inverse
= gfx_inverse
;
118 *inverse_screen
= gfx_inverse_screen
;
122 * Utility function to parse gfx mode line strings.
125 gfx_parse_mode_str(char *str
, int *x
, int *y
, int *depth
)
131 *x
= strtoul(p
, &end
, 0);
132 if (*x
== 0 || errno
!= 0)
137 *y
= strtoul(p
, &end
, 0);
138 if (*y
== 0 || errno
!= 0)
141 *depth
= -1; /* auto select */
144 *depth
= strtoul(p
, &end
, 0);
145 if (*depth
== 0 || errno
!= 0 || *end
!= '\0')
153 * Support for color mapping.
156 gfx_fb_color_map(uint8_t index
)
162 if (gfx_fb
.framebuffer_common
.framebuffer_type
!=
163 MULTIBOOT_FRAMEBUFFER_TYPE_RGB
) {
164 if (index
< nitems (solaris_color_to_pc_color
))
165 return (solaris_color_to_pc_color
[index
]);
170 c
= cmap4_to_24
.red
[index
];
171 pos
= gfx_fb
.u
.fb2
.framebuffer_red_field_position
;
172 size
= gfx_fb
.u
.fb2
.framebuffer_red_mask_size
;
173 color
= ((c
>> (8 - size
)) & ((1 << size
) - 1)) << pos
;
175 c
= cmap4_to_24
.green
[index
];
176 pos
= gfx_fb
.u
.fb2
.framebuffer_green_field_position
;
177 size
= gfx_fb
.u
.fb2
.framebuffer_green_mask_size
;
178 color
|= ((c
>> (8 - size
)) & ((1 << size
) - 1)) << pos
;
180 c
= cmap4_to_24
.blue
[index
];
181 pos
= gfx_fb
.u
.fb2
.framebuffer_blue_field_position
;
182 size
= gfx_fb
.u
.fb2
.framebuffer_blue_mask_size
;
183 color
|= ((c
>> (8 - size
)) & ((1 << size
) - 1)) << pos
;
189 color_name_to_ansi(const char *name
, int *val
)
191 if (strcasecmp(name
, "black") == 0) {
192 *val
= ANSI_COLOR_BLACK
;
195 if (strcasecmp(name
, "red") == 0) {
196 *val
= ANSI_COLOR_RED
;
199 if (strcasecmp(name
, "green") == 0) {
200 *val
= ANSI_COLOR_GREEN
;
203 if (strcasecmp(name
, "yellow") == 0) {
204 *val
= ANSI_COLOR_YELLOW
;
207 if (strcasecmp(name
, "blue") == 0) {
208 *val
= ANSI_COLOR_BLUE
;
211 if (strcasecmp(name
, "magenta") == 0) {
212 *val
= ANSI_COLOR_MAGENTA
;
215 if (strcasecmp(name
, "cyan") == 0) {
216 *val
= ANSI_COLOR_CYAN
;
219 if (strcasecmp(name
, "white") == 0) {
220 *val
= ANSI_COLOR_WHITE
;
226 /* Callback to check and set colors */
228 gfx_set_colors(struct env_var
*ev
, int flags
, const void *value
)
237 if (color_name_to_ansi(value
, &val
)) {
238 snprintf(buf
, sizeof (buf
), "%d", val
);
244 val
= (int) strtol(value
, &end
, 0);
245 if (errno
!= 0 || *end
!= '\0') {
246 printf("Allowed values are either ansi color name or "
247 "number from range [0-7].\n");
254 if (val
< 0 || val
> 7) {
255 printf("Allowed values are either ansi color name or "
256 "number from range [0-7].\n");
260 if (strcmp(ev
->ev_name
, "tem.fg_color") == 0) {
261 /* is it already set? */
266 if (strcmp(ev
->ev_name
, "tem.bg_color") == 0) {
267 /* is it already set? */
272 env_setenv(ev
->ev_name
, flags
| EV_NOHOOK
, evalue
, NULL
, NULL
);
273 plat_cons_update_mode(-1);
277 /* Callback to check and set inverses */
279 gfx_set_inverses(struct env_var
*ev
, int flags
, const void *value
)
286 t
= strcmp(value
, "true");
287 f
= strcmp(value
, "false");
290 if (t
!= 0 && f
!= 0)
293 if (strcmp(ev
->ev_name
, "tem.inverse") == 0) {
294 /* is it already set? */
295 if (gfx_inverse
== (t
== 0))
297 gfx_inverse
= (t
== 0);
299 if (strcmp(ev
->ev_name
, "tem.inverse-screen") == 0) {
300 /* is it already set? */
301 if (gfx_inverse_screen
== (t
== 0))
303 gfx_inverse_screen
= (t
== 0);
305 env_setenv(ev
->ev_name
, flags
| EV_NOHOOK
, value
, NULL
, NULL
);
306 plat_cons_update_mode(-1);
311 * Initialize gfx framework.
314 gfx_framework_init(struct visual_ops
*fb_ops
)
319 extern EFI_GRAPHICS_OUTPUT
*gop
;
322 gfx_fb_ops
.gfx_cons_clear
= gfx_gop_cons_clear
;
323 gfx_fb_ops
.gfx_cons_copy
= gfx_gop_cons_copy
;
324 gfx_fb_ops
.gfx_cons_display
= gfx_gop_cons_display
;
328 /* Add visual io callbacks */
329 fb_ops
->cons_clear
= gfx_fb_cons_clear
;
330 fb_ops
->cons_copy
= gfx_fb_cons_copy
;
331 fb_ops
->cons_display
= gfx_fb_cons_display
;
333 /* set up tem inverse controls */
334 env
= getenv("tem.inverse");
336 if (strcmp(env
, "true") == 0)
338 unsetenv("tem.inverse");
341 env
= getenv("tem.inverse-screen");
343 if (strcmp(env
, "true") == 0)
344 gfx_inverse_screen
= 1;
345 unsetenv("tem.inverse-screen");
353 env_setenv("tem.inverse", EV_VOLATILE
, env
, gfx_set_inverses
,
356 if (gfx_inverse_screen
)
361 env_setenv("tem.inverse-screen", EV_VOLATILE
, env
, gfx_set_inverses
,
364 /* set up tem color controls */
365 env
= getenv("tem.fg_color");
367 rc
= (int) strtol(env
, NULL
, 0);
368 if (rc
>= 0 && rc
<= 7)
370 unsetenv("tem.fg_color");
373 env
= getenv("tem.bg_color");
375 rc
= (int) strtol(env
, NULL
, 0);
376 if (rc
>= 0 && rc
<= 7)
378 unsetenv("tem.bg_color");
381 snprintf(buf
, sizeof (buf
), "%d", gfx_fg
);
382 env_setenv("tem.fg_color", EV_VOLATILE
, buf
, gfx_set_colors
,
384 snprintf(buf
, sizeof (buf
), "%d", gfx_bg
);
385 env_setenv("tem.bg_color", EV_VOLATILE
, buf
, gfx_set_colors
,
390 * visual io callbacks.
395 gfx_gop_cons_clear(uint32_t data
, uint32_t width
, uint32_t height
)
397 EFI_GRAPHICS_OUTPUT_BLT_PIXEL
*BltBuffer
;
399 extern EFI_GRAPHICS_OUTPUT
*gop
;
401 BltBuffer
= (EFI_GRAPHICS_OUTPUT_BLT_PIXEL
*)&data
;
403 status
= gop
->Blt(gop
, BltBuffer
, EfiBltVideoFill
, 0, 0,
404 0, 0, width
, height
, 0);
406 if (EFI_ERROR(status
))
414 gfx_bm_cons_clear(uint32_t data
, uint32_t width
, uint32_t height
)
417 uint32_t *fb32
, pitch
;
421 fb
= gfx_get_fb_address();
422 pitch
= gfx_fb
.framebuffer_common
.framebuffer_pitch
;
424 switch (gfx_fb
.framebuffer_common
.framebuffer_bpp
) {
426 for (i
= 0; i
< height
; i
++) {
427 (void) memset(fb
+ i
* pitch
, data
, pitch
);
431 case 16: /* 16 bit */
432 for (i
= 0; i
< height
; i
++) {
433 fb16
= (uint16_t *)(fb
+ i
* pitch
);
434 for (j
= 0; j
< width
; j
++)
435 fb16
[j
] = (uint16_t)(data
& 0xffff);
438 case 24: /* 24 bit */
439 for (i
= 0; i
< height
; i
++) {
440 fb8
= fb
+ i
* pitch
;
441 for (j
= 0; j
< pitch
; j
+= 3) {
442 fb8
[j
] = (data
>> 16) & 0xff;
443 fb8
[j
+1] = (data
>> 8) & 0xff;
444 fb8
[j
+2] = data
& 0xff;
448 case 32: /* 32 bit */
449 for (i
= 0; i
< height
; i
++) {
450 fb32
= (uint32_t *)(fb
+ i
* pitch
);
451 for (j
= 0; j
< width
; j
++)
463 gfx_fb_cons_clear(struct vis_consclear
*ca
)
465 uint32_t data
, width
, height
;
471 data
= gfx_fb_color_map(ca
->bg_color
);
472 width
= gfx_fb
.framebuffer_common
.framebuffer_width
;
473 height
= gfx_fb
.framebuffer_common
.framebuffer_height
;
476 tpl
= BS
->RaiseTPL(TPL_NOTIFY
);
478 ret
= gfx_fb_ops
.gfx_cons_clear(data
, width
, height
);
487 gfx_gop_cons_copy(struct vis_conscopy
*ma
)
490 extern EFI_GRAPHICS_OUTPUT
*gop
;
492 width
= ma
->e_col
- ma
->s_col
+ 1;
493 height
= ma
->e_row
- ma
->s_row
+ 1;
495 (void) gop
->Blt(gop
, NULL
, EfiBltVideoToVideo
, ma
->s_col
, ma
->s_row
,
496 ma
->t_col
, ma
->t_row
, width
, height
, 0);
501 gfx_bm_cons_copy(struct vis_conscopy
*ma
)
503 uint32_t soffset
, toffset
;
504 uint32_t width
, height
;
505 uint8_t *src
, *dst
, *fb
;
508 fb
= gfx_get_fb_address();
509 bpp
= roundup2(gfx_fb
.framebuffer_common
.framebuffer_bpp
, 8) >> 3;
510 pitch
= gfx_fb
.framebuffer_common
.framebuffer_pitch
;
512 soffset
= ma
->s_col
* bpp
+ ma
->s_row
* pitch
;
513 toffset
= ma
->t_col
* bpp
+ ma
->t_row
* pitch
;
516 width
= (ma
->e_col
- ma
->s_col
+ 1) * bpp
;
517 height
= ma
->e_row
- ma
->s_row
+ 1;
519 if (toffset
<= soffset
) {
520 for (uint32_t i
= 0; i
< height
; i
++) {
521 uint32_t increment
= i
* pitch
;
522 (void)memmove(dst
+ increment
, src
+ increment
, width
);
525 for (int i
= height
- 1; i
>= 0; i
--) {
526 uint32_t increment
= i
* pitch
;
527 (void)memmove(dst
+ increment
, src
+ increment
, width
);
533 gfx_fb_cons_copy(struct vis_conscopy
*ma
)
538 tpl
= BS
->RaiseTPL(TPL_NOTIFY
);
541 gfx_fb_ops
.gfx_cons_copy(ma
);
548 * Implements alpha blending for RGBA data, could use pixels for arguments,
549 * but byte stream seems more generic.
550 * The generic alpha blending is:
551 * blend = alpha * fg + (1.0 - alpha) * bg.
552 * Since our alpha is not from range [0..1], we scale appropriately.
555 alpha_blend(uint8_t fg
, uint8_t bg
, uint8_t alpha
)
557 uint16_t blend
, h
, l
;
559 /* trivial corner cases */
564 blend
= (alpha
* fg
+ (0xFF - alpha
) * bg
);
565 /* Division by 0xFF */
573 /* Copy memory to framebuffer or to memory. */
575 bitmap_cpy(uint8_t *dst
, uint8_t *src
, uint32_t len
, int bpp
)
583 * we only implement alpha blending for depth 32,
584 * use memcpy for other cases.
586 for (i
= 0; i
< len
; i
+= bpp
) {
588 dst
[i
] = alpha_blend(src
[i
], dst
[i
], a
);
589 dst
[i
+1] = alpha_blend(src
[i
+1], dst
[i
+1], a
);
590 dst
[i
+2] = alpha_blend(src
[i
+2], dst
[i
+2], a
);
595 (void) memcpy(dst
, src
, len
);
602 gfx_gop_cons_display(struct vis_consdisplay
*da
)
604 EFI_GRAPHICS_OUTPUT_BLT_PIXEL
*BltBuffer
;
607 extern EFI_GRAPHICS_OUTPUT
*gop
;
609 bpp
= roundup2(gfx_fb
.framebuffer_common
.framebuffer_bpp
, 8) >> 3;
610 size
= sizeof (*BltBuffer
) * da
->width
* da
->height
;
611 BltBuffer
= malloc(size
);
612 if (BltBuffer
== NULL
&& gfx_get_fb_address() != NULL
) {
613 /* Fall back to bitmap implementation */
614 gfx_bm_cons_display(da
);
618 (void) gop
->Blt(gop
, BltBuffer
, EfiBltVideoToBltBuffer
,
619 da
->col
, da
->row
, 0, 0, da
->width
, da
->height
, 0);
620 bitmap_cpy((void *)BltBuffer
, da
->data
, size
, bpp
);
621 (void) gop
->Blt(gop
, BltBuffer
, EfiBltBufferToVideo
,
622 0, 0, da
->col
, da
->row
, da
->width
, da
->height
, 0);
628 gfx_bm_cons_display(struct vis_consdisplay
*da
)
630 uint32_t size
; /* write size per scanline */
631 uint8_t *fbp
; /* fb + calculated offset */
634 bpp
= roundup2(gfx_fb
.framebuffer_common
.framebuffer_bpp
, 8) >> 3;
635 pitch
= gfx_fb
.framebuffer_common
.framebuffer_pitch
;
637 size
= da
->width
* bpp
;
638 fbp
= gfx_get_fb_address();
639 fbp
+= da
->col
* bpp
+ da
->row
* pitch
;
641 /* write all scanlines in rectangle */
642 for (i
= 0; i
< da
->height
; i
++) {
643 uint8_t *dest
= fbp
+ i
* pitch
;
644 uint8_t *src
= da
->data
+ i
* size
;
645 bitmap_cpy(dest
, src
, size
, bpp
);
650 gfx_fb_cons_display(struct vis_consdisplay
*da
)
656 /* make sure we will not write past FB */
657 if ((uint32_t)da
->col
>= gfx_fb
.framebuffer_common
.framebuffer_width
||
658 (uint32_t)da
->row
>= gfx_fb
.framebuffer_common
.framebuffer_height
||
659 (uint32_t)da
->col
+ da
->width
>
660 gfx_fb
.framebuffer_common
.framebuffer_width
||
661 (uint32_t)da
->row
+ da
->height
>
662 gfx_fb
.framebuffer_common
.framebuffer_height
)
666 tpl
= BS
->RaiseTPL(TPL_NOTIFY
);
668 gfx_fb_ops
.gfx_cons_display(da
);
675 gfx_fb_display_cursor(struct vis_conscursor
*ca
)
678 uint32_t offset
, size
, *fb32
;
686 fb
= gfx_get_fb_address();
687 bpp
= roundup2(gfx_fb
.framebuffer_common
.framebuffer_bpp
, 8) >> 3;
688 pitch
= gfx_fb
.framebuffer_common
.framebuffer_pitch
;
690 size
= ca
->width
* bpp
;
693 * Build cursor image. We are building mirror image of data on
694 * frame buffer by (D xor FG) xor BG.
696 offset
= ca
->col
* bpp
+ ca
->row
* pitch
;
698 tpl
= BS
->RaiseTPL(TPL_NOTIFY
);
700 switch (gfx_fb
.framebuffer_common
.framebuffer_bpp
) {
702 fg
= ca
->fg_color
.mono
;
703 bg
= ca
->bg_color
.mono
;
704 for (int i
= 0; i
< ca
->height
; i
++) {
705 fb8
= fb
+ offset
+ i
* pitch
;
706 for (uint32_t j
= 0; j
< size
; j
+= 1) {
707 fb8
[j
] = (fb8
[j
] ^ (fg
& 0xff)) ^ (bg
& 0xff);
712 case 16: /* 16 bit */
713 fg
= ca
->fg_color
.sixteen
[0] << 8;
714 fg
|= ca
->fg_color
.sixteen
[1];
715 bg
= ca
->bg_color
.sixteen
[0] << 8;
716 bg
|= ca
->bg_color
.sixteen
[1];
717 for (int i
= 0; i
< ca
->height
; i
++) {
718 fb16
= (uint16_t *)(fb
+ offset
+ i
* pitch
);
719 for (int j
= 0; j
< ca
->width
; j
++) {
720 fb16
[j
] = (fb16
[j
] ^ (fg
& 0xffff)) ^
725 case 24: /* 24 bit */
726 fg
= ca
->fg_color
.twentyfour
[0] << 16;
727 fg
|= ca
->fg_color
.twentyfour
[1] << 8;
728 fg
|= ca
->fg_color
.twentyfour
[2];
729 bg
= ca
->bg_color
.twentyfour
[0] << 16;
730 bg
|= ca
->bg_color
.twentyfour
[1] << 8;
731 bg
|= ca
->bg_color
.twentyfour
[2];
733 for (int i
= 0; i
< ca
->height
; i
++) {
734 fb8
= fb
+ offset
+ i
* pitch
;
735 for (uint32_t j
= 0; j
< size
; j
+= 3) {
736 fb8
[j
] = (fb8
[j
] ^ ((fg
>> 16) & 0xff)) ^
738 fb8
[j
+1] = (fb8
[j
+1] ^ ((fg
>> 8) & 0xff)) ^
740 fb8
[j
+2] = (fb8
[j
+2] ^ (fg
& 0xff)) ^
745 case 32: /* 32 bit */
746 fg
= ca
->fg_color
.twentyfour
[0] << 16;
747 fg
|= ca
->fg_color
.twentyfour
[1] << 8;
748 fg
|= ca
->fg_color
.twentyfour
[2];
749 bg
= ca
->bg_color
.twentyfour
[0] << 16;
750 bg
|= ca
->bg_color
.twentyfour
[1] << 8;
751 bg
|= ca
->bg_color
.twentyfour
[2];
752 for (int i
= 0; i
< ca
->height
; i
++) {
753 fb32
= (uint32_t *)(fb
+ offset
+ i
* pitch
);
754 for (int j
= 0; j
< ca
->width
; j
++)
755 fb32
[j
] = (fb32
[j
] ^ fg
) ^ bg
;
765 * Public graphics primitives.
774 /* "bit" starts at the highest power of four <= the argument. */
779 if (num
>= res
+ bit
) {
781 res
= (res
>> 1) + bit
;
789 /* set pixel in framebuffer using gfx coordinates */
791 gfx_fb_setpixel(uint32_t x
, uint32_t y
)
793 uint32_t c
, offset
, pitch
, bpp
;
797 if (plat_stdout_is_framebuffer() == 0)
800 tem_get_colors((tem_vt_state_t
)tems
.ts_active
, &fg
, &bg
);
801 c
= gfx_fb_color_map(fg
);
803 if (x
>= gfx_fb
.framebuffer_common
.framebuffer_width
||
804 y
>= gfx_fb
.framebuffer_common
.framebuffer_height
)
807 fb
= gfx_get_fb_address();
808 pitch
= gfx_fb
.framebuffer_common
.framebuffer_pitch
;
809 bpp
= roundup2(gfx_fb
.framebuffer_common
.framebuffer_bpp
, 8) >> 3;
811 offset
= y
* pitch
+ x
* bpp
;
812 switch (gfx_fb
.framebuffer_common
.framebuffer_bpp
) {
814 fb
[offset
] = c
& 0xff;
818 *(uint16_t *)(fb
+ offset
) = c
& 0xffff;
821 fb
[offset
] = (c
>> 16) & 0xff;
822 fb
[offset
+ 1] = (c
>> 8) & 0xff;
823 fb
[offset
+ 2] = c
& 0xff;
826 *(uint32_t *)(fb
+ offset
) = c
;
832 * draw rectangle in framebuffer using gfx coordinates.
833 * The function is borrowed from fbsd vt_fb.c
836 gfx_fb_drawrect(uint32_t x1
, uint32_t y1
, uint32_t x2
, uint32_t y2
,
841 if (plat_stdout_is_framebuffer() == 0)
844 for (y
= y1
; y
<= y2
; y
++) {
845 if (fill
|| (y
== y1
) || (y
== y2
)) {
846 for (x
= x1
; x
<= x2
; x
++)
847 gfx_fb_setpixel(x
, y
);
849 gfx_fb_setpixel(x1
, y
);
850 gfx_fb_setpixel(x2
, y
);
856 gfx_fb_line(uint32_t x0
, uint32_t y0
, uint32_t x1
, uint32_t y1
, uint32_t wd
)
859 int err
, e2
, x2
, y2
, ed
, width
;
861 if (plat_stdout_is_framebuffer() == 0)
865 sx
= x0
< x1
? 1 : -1;
866 sy
= y0
< y1
? 1 : -1;
867 dx
= x1
> x0
? x1
- x0
: x0
- x1
;
868 dy
= y1
> y0
? y1
- y0
: y0
- y1
;
870 ed
= dx
+ dy
== 0 ? 1: isqrt(dx
* dx
+ dy
* dy
);
873 gfx_fb_setpixel(x0
, y0
);
876 if ((e2
<< 1) >= -dx
) { /* x step */
879 while (e2
< ed
* width
&&
880 (y1
!= (uint32_t)y2
|| dx
> dy
)) {
882 gfx_fb_setpixel(x0
, y2
);
891 if ((e2
<< 1) <= dy
) { /* y step */
893 while (e2
< ed
* width
&&
894 (x1
!= (uint32_t)x2
|| dx
< dy
)) {
896 gfx_fb_setpixel(x2
, y0
);
908 * quadratic Bézier curve limited to gradients without sign change.
911 gfx_fb_bezier(uint32_t x0
, uint32_t y0
, uint32_t x1
, uint32_t y1
, uint32_t x2
,
912 uint32_t y2
, uint32_t wd
)
914 int sx
, sy
, xx
, yy
, xy
, width
;
915 int dx
, dy
, err
, curvature
;
918 if (plat_stdout_is_framebuffer() == 0)
926 curvature
= xx
*sy
- yy
*sx
;
928 if (sx
*sx
+ sy
*sy
> xx
*xx
+yy
*yy
) {
933 curvature
= -curvature
;
935 if (curvature
!= 0) {
937 sx
= x0
< x2
? 1 : -1;
940 sy
= y0
< y2
? 1 : -1;
945 if (curvature
* sx
* sy
< 0) {
949 curvature
= -curvature
;
951 dx
= 4 * sy
* curvature
* (x1
- x0
) + xx
- xy
;
952 dy
= 4 * sx
* curvature
* (y0
- y1
) + yy
- xy
;
957 for (i
= 0; i
<= width
; i
++)
958 gfx_fb_setpixel(x0
+ i
, y0
);
959 if (x0
== x2
&& y0
== y2
)
960 return; /* last pixel -> curve finished */
974 } while (dy
< dx
); /* gradient negates -> algorithm fails */
976 gfx_fb_line(x0
, y0
, x2
, y2
, width
);
980 * draw rectangle using terminal coordinates and current foreground color.
983 gfx_term_drawrect(uint32_t ux1
, uint32_t uy1
, uint32_t ux2
, uint32_t uy2
)
988 uint32_t vf_width
, vf_height
;
990 if (plat_stdout_is_framebuffer() == 0)
993 vf_width
= tems
.ts_font
.vf_width
;
994 vf_height
= tems
.ts_font
.vf_height
;
995 width
= vf_width
/ 4; /* line width */
996 xshift
= (vf_width
- width
) / 2;
997 yshift
= (vf_height
- width
) / 2;
998 /* Terminal coordinates start from (1,1) */
1004 /* mark area used in tem */
1005 tem_image_display(tems
.ts_active
, uy1
- 1, ux1
- 1, uy2
, ux2
);
1008 * Draw horizontal lines width points thick, shifted from outer edge.
1010 x1
= (ux1
+ 1) * vf_width
+ tems
.ts_p_offset
.x
;
1011 y1
= uy1
* vf_height
+ tems
.ts_p_offset
.y
+ yshift
;
1012 x2
= ux2
* vf_width
+ tems
.ts_p_offset
.x
;
1013 gfx_fb_drawrect(x1
, y1
, x2
, y1
+ width
, 1);
1014 y2
= uy2
* vf_height
+ tems
.ts_p_offset
.y
;
1015 y2
+= vf_height
- yshift
- width
;
1016 gfx_fb_drawrect(x1
, y2
, x2
, y2
+ width
, 1);
1019 * Draw vertical lines width points thick, shifted from outer edge.
1021 x1
= ux1
* vf_width
+ tems
.ts_p_offset
.x
+ xshift
;
1022 y1
= uy1
* vf_height
+ tems
.ts_p_offset
.y
;
1024 y2
= uy2
* vf_height
+ tems
.ts_p_offset
.y
;
1025 gfx_fb_drawrect(x1
, y1
, x1
+ width
, y2
, 1);
1026 x1
= ux2
* vf_width
+ tems
.ts_p_offset
.x
;
1027 x1
+= vf_width
- xshift
- width
;
1028 gfx_fb_drawrect(x1
, y1
, x1
+ width
, y2
, 1);
1030 /* Draw upper left corner. */
1031 x1
= ux1
* vf_width
+ tems
.ts_p_offset
.x
+ xshift
;
1032 y1
= uy1
* vf_height
+ tems
.ts_p_offset
.y
;
1035 x2
= ux1
* vf_width
+ tems
.ts_p_offset
.x
;
1037 y2
= uy1
* vf_height
+ tems
.ts_p_offset
.y
+ yshift
;
1038 for (i
= 0; i
<= width
; i
++)
1039 gfx_fb_bezier(x1
+ i
, y1
, x1
+ i
, y2
+ i
, x2
, y2
+ i
, width
-i
);
1041 /* Draw lower left corner. */
1042 x1
= ux1
* vf_width
+ tems
.ts_p_offset
.x
;
1044 y1
= uy2
* vf_height
+ tems
.ts_p_offset
.y
;
1045 y1
+= vf_height
- yshift
;
1046 x2
= ux1
* vf_width
+ tems
.ts_p_offset
.x
+ xshift
;
1047 y2
= uy2
* vf_height
+ tems
.ts_p_offset
.y
;
1048 for (i
= 0; i
<= width
; i
++)
1049 gfx_fb_bezier(x1
, y1
- i
, x2
+ i
, y1
- i
, x2
+ i
, y2
, width
-i
);
1051 /* Draw upper right corner. */
1052 x1
= ux2
* vf_width
+ tems
.ts_p_offset
.x
;
1053 y1
= uy1
* vf_height
+ tems
.ts_p_offset
.y
+ yshift
;
1054 x2
= ux2
* vf_width
+ tems
.ts_p_offset
.x
;
1055 x2
+= vf_width
- xshift
- width
;
1056 y2
= uy1
* vf_height
+ tems
.ts_p_offset
.y
;
1058 for (i
= 0; i
<= width
; i
++)
1059 gfx_fb_bezier(x1
, y1
+ i
, x2
+ i
, y1
+ i
, x2
+ i
, y2
, width
-i
);
1061 /* Draw lower right corner. */
1062 x1
= ux2
* vf_width
+ tems
.ts_p_offset
.x
;
1063 y1
= uy2
* vf_height
+ tems
.ts_p_offset
.y
;
1064 y1
+= vf_height
- yshift
;
1065 x2
= ux2
* vf_width
+ tems
.ts_p_offset
.x
;
1066 x2
+= vf_width
- xshift
- width
;
1067 y2
= uy2
* vf_height
+ tems
.ts_p_offset
.y
;
1068 for (i
= 0; i
<= width
; i
++)
1069 gfx_fb_bezier(x1
, y1
- i
, x2
+ i
, y1
- i
, x2
+ i
, y2
, width
-i
);
1073 gfx_fb_putimage(png_t
*png
)
1075 struct vis_consdisplay da
;
1076 uint32_t i
, j
, height
, width
, color
;
1078 uint8_t r
, g
, b
, a
, *p
;
1080 if (plat_stdout_is_framebuffer() == 0 ||
1081 png
->color_type
!= PNG_TRUECOLOR_ALPHA
) {
1085 bpp
= roundup2(gfx_fb
.framebuffer_common
.framebuffer_bpp
, 8) >> 3;
1087 height
= png
->height
;
1088 da
.width
= png
->width
;
1089 da
.height
= png
->height
;
1090 da
.col
= gfx_fb
.framebuffer_common
.framebuffer_width
-
1093 da
.row
= gfx_fb
.framebuffer_common
.framebuffer_height
-
1095 da
.row
-= da
.height
;
1098 * mark area used in tem
1100 tem_image_display(tems
.ts_active
, da
.row
/ tems
.ts_font
.vf_height
- 1,
1101 da
.col
/ tems
.ts_font
.vf_width
- 1,
1102 tems
.ts_c_dimension
.height
- 1,
1103 tems
.ts_c_dimension
.width
- 1);
1105 da
.data
= malloc(width
* height
* bpp
);
1106 if (da
.data
== NULL
)
1110 * Build image for our framebuffer.
1112 for (i
= 0; i
< height
* width
* png
->bpp
; i
+= png
->bpp
) {
1114 g
= png
->image
[i
+1];
1115 b
= png
->image
[i
+2];
1116 a
= png
->image
[i
+3];
1118 j
= i
/ png
->bpp
* bpp
;
1119 color
= r
>> (8 - gfx_fb
.u
.fb2
.framebuffer_red_mask_size
)
1120 << gfx_fb
.u
.fb2
.framebuffer_red_field_position
;
1121 color
|= g
>> (8 - gfx_fb
.u
.fb2
.framebuffer_green_mask_size
)
1122 << gfx_fb
.u
.fb2
.framebuffer_green_field_position
;
1123 color
|= b
>> (8 - gfx_fb
.u
.fb2
.framebuffer_blue_mask_size
)
1124 << gfx_fb
.u
.fb2
.framebuffer_blue_field_position
;
1126 switch (gfx_fb
.framebuffer_common
.framebuffer_bpp
) {
1128 uint32_t best
, dist
, k
;
1132 best
= 256 * 256 * 256;
1133 for (k
= 0; k
< 16; k
++) {
1134 diff
= r
- cmap4_to_24
.red
[k
];
1136 diff
= g
- cmap4_to_24
.green
[k
];
1137 dist
+= diff
* diff
;
1138 diff
= b
- cmap4_to_24
.blue
[k
];
1139 dist
+= diff
* diff
;
1148 da
.data
[j
] = solaris_color_to_pc_color
[color
];
1153 *(uint16_t *)(da
.data
+j
) = color
;
1156 p
= (uint8_t *)&color
;
1158 da
.data
[j
+1] = p
[1];
1159 da
.data
[j
+2] = p
[2];
1163 *(uint32_t *)(da
.data
+j
) = color
;
1168 gfx_fb_cons_display(&da
);
1174 load_mapping(int fd
, struct font
*fp
, int n
)
1178 struct font_map
*mp
;
1180 if (fp
->vf_map_count
[n
] == 0)
1183 size
= fp
->vf_map_count
[n
] * sizeof (*mp
);
1189 rv
= read(fd
, mp
, size
);
1190 if (rv
< 0 || (size_t)rv
!= size
) {
1191 free(fp
->vf_map
[n
]);
1192 fp
->vf_map
[n
] = NULL
;
1196 for (i
= 0; i
< fp
->vf_map_count
[n
]; i
++) {
1197 mp
[i
].font_src
= be32toh(mp
[i
].font_src
);
1198 mp
[i
].font_dst
= be16toh(mp
[i
].font_dst
);
1199 mp
[i
].font_len
= be16toh(mp
[i
].font_len
);
1204 /* Load font from file. */
1205 static bitmap_data_t
*
1206 load_font(char *path
)
1210 struct font_header fh
;
1211 struct fontlist
*fl
;
1212 bitmap_data_t
*bp
= NULL
;
1217 /* Get our entry from the font list. */
1218 STAILQ_FOREACH(fl
, &fonts
, font_next
) {
1219 if (strcmp(fl
->font_name
, path
) == 0)
1223 return (NULL
); /* Should not happen. */
1225 if (bp
->font
!= NULL
)
1228 fd
= open(path
, O_RDONLY
);
1234 rv
= read(fd
, &fh
, size
);
1235 if (rv
< 0 || (size_t)rv
!= size
) {
1239 if (memcmp(fh
.fh_magic
, FONT_HEADER_MAGIC
, sizeof (fh
.fh_magic
)) != 0) {
1243 if ((fp
= calloc(1, sizeof (struct font
))) == NULL
) {
1247 for (i
= 0; i
< VFNT_MAPS
; i
++)
1248 fp
->vf_map_count
[i
] = be32toh(fh
.fh_map_count
[i
]);
1250 glyphs
= be32toh(fh
.fh_glyph_count
);
1251 fp
->vf_width
= fh
.fh_width
;
1252 fp
->vf_height
= fh
.fh_height
;
1254 bp
->uncompressed_size
= howmany(bp
->width
, 8) * bp
->height
* glyphs
;
1255 size
= bp
->uncompressed_size
;
1256 if ((fp
->vf_bytes
= malloc(size
)) == NULL
)
1259 rv
= read(fd
, fp
->vf_bytes
, size
);
1260 if (rv
< 0 || (size_t)rv
!= size
)
1262 for (i
= 0; i
< VFNT_MAPS
; i
++) {
1263 if (load_mapping(fd
, fp
, i
) != 0)
1269 * Release previously loaded entry. We can do this now, as
1270 * the new font is loaded. Note, there can be no console
1271 * output till the new font is in place and tem is notified.
1272 * We do need to keep fl->font_data for glyph dimensions.
1274 STAILQ_FOREACH(fl
, &fonts
, font_next
) {
1275 if (fl
->font_data
->width
== bp
->width
&&
1276 fl
->font_data
->height
== bp
->height
)
1279 if (fl
->font_data
->font
!= NULL
) {
1280 for (i
= 0; i
< VFNT_MAPS
; i
++)
1281 free(fl
->font_data
->font
->vf_map
[i
]);
1283 /* Unset vf_bytes pointer in tem. */
1284 if (tems
.ts_font
.vf_bytes
==
1285 fl
->font_data
->font
->vf_bytes
) {
1286 tems
.ts_font
.vf_bytes
= NULL
;
1288 free(fl
->font_data
->font
->vf_bytes
);
1289 free(fl
->font_data
->font
);
1290 fl
->font_data
->font
= NULL
;
1291 fl
->font_data
->uncompressed_size
= 0;
1292 fl
->font_flags
= FONT_AUTO
;
1296 /* free the uncompressed builtin font data in tem. */
1297 free(tems
.ts_font
.vf_bytes
);
1298 tems
.ts_font
.vf_bytes
= NULL
;
1305 for (i
= 0; i
< VFNT_MAPS
; i
++)
1306 free(fp
->vf_map
[i
]);
1316 SLIST_ENTRY(name_entry
) n_entry
;
1319 SLIST_HEAD(name_list
, name_entry
);
1321 /* Read font names from index file. */
1322 static struct name_list
*
1323 read_list(char *fonts
)
1325 struct name_list
*nl
;
1326 struct name_entry
*np
;
1330 fd
= open(fonts
, O_RDONLY
);
1334 nl
= malloc(sizeof (*nl
));
1341 while ((len
= fgetstr(buf
, sizeof (buf
), fd
)) > 0) {
1342 np
= malloc(sizeof (*np
));
1345 return (nl
); /* return what we have */
1347 np
->n_name
= strdup(buf
);
1348 if (np
->n_name
== NULL
) {
1351 return (nl
); /* return what we have */
1353 SLIST_INSERT_HEAD(nl
, np
, n_entry
);
1360 * Read the font properties and insert new entry into the list.
1361 * The font list is built in descending order.
1364 insert_font(char *name
)
1366 struct font_header fh
;
1367 struct fontlist
*fp
, *previous
, *entry
, *next
;
1373 fd
= open(name
, O_RDONLY
);
1376 rv
= read(fd
, &fh
, sizeof (fh
));
1378 if (rv
< 0 || (size_t)rv
!= sizeof (fh
))
1381 if (memcmp(fh
.fh_magic
, FONT_HEADER_MAGIC
, sizeof (fh
.fh_magic
)) != 0)
1384 font_name
= strdup(name
);
1385 if (font_name
== NULL
)
1389 * If we have an entry with the same glyph dimensions, just replace
1390 * the file name. We only support unique dimensions.
1392 STAILQ_FOREACH(entry
, &fonts
, font_next
) {
1393 if (fh
.fh_width
== entry
->font_data
->width
&&
1394 fh
.fh_height
== entry
->font_data
->height
) {
1395 free(entry
->font_name
);
1396 entry
->font_name
= font_name
;
1401 fp
= calloc(sizeof (*fp
), 1);
1406 fp
->font_data
= calloc(sizeof (*fp
->font_data
), 1);
1407 if (fp
->font_data
== NULL
) {
1412 fp
->font_name
= font_name
;
1413 fp
->font_flags
= FONT_AUTO
;
1414 fp
->font_load
= load_font
;
1415 fp
->font_data
->width
= fh
.fh_width
;
1416 fp
->font_data
->height
= fh
.fh_height
;
1418 if (STAILQ_EMPTY(&fonts
)) {
1419 STAILQ_INSERT_HEAD(&fonts
, fp
, font_next
);
1424 size
= fp
->font_data
->width
* fp
->font_data
->height
;
1426 STAILQ_FOREACH(entry
, &fonts
, font_next
) {
1427 /* Should fp be inserted before the entry? */
1429 entry
->font_data
->width
* entry
->font_data
->height
) {
1430 if (previous
== NULL
) {
1431 STAILQ_INSERT_HEAD(&fonts
, fp
, font_next
);
1433 STAILQ_INSERT_AFTER(&fonts
, previous
, fp
,
1438 next
= STAILQ_NEXT(entry
, font_next
);
1440 size
> next
->font_data
->width
* next
->font_data
->height
) {
1441 STAILQ_INSERT_AFTER(&fonts
, entry
, fp
, font_next
);
1450 font_set(struct env_var
*ev __unused
, int flags __unused
, const void *value
)
1452 struct fontlist
*fl
, *tmp
;
1454 unsigned long x
= 0, y
= 0;
1457 * Attempt to extract values from "XxY" string. In case of error,
1458 * we have unmaching glyph dimensions and will just output the
1461 if (value
!= NULL
) {
1462 x
= strtoul(value
, &eptr
, 10);
1464 y
= strtoul(eptr
+ 1, &eptr
, 10);
1466 STAILQ_FOREACH(fl
, &fonts
, font_next
) {
1467 if (fl
->font_data
->width
== x
&& fl
->font_data
->height
== y
)
1471 /* Reset any FONT_MANUAL flag. */
1472 STAILQ_FOREACH(tmp
, &fonts
, font_next
)
1473 tmp
->font_flags
= FONT_AUTO
;
1475 fl
->font_flags
= FONT_MANUAL
;
1476 /* Trigger tem update. */
1477 tems
.update_font
= true;
1478 plat_cons_update_mode(-1);
1482 printf("Available fonts:\n");
1483 STAILQ_FOREACH(fl
, &fonts
, font_next
) {
1484 printf(" %dx%d\n", fl
->font_data
->width
,
1485 fl
->font_data
->height
);
1493 struct name_list
*nl
;
1494 struct name_entry
*np
;
1496 nl
= read_list("/boot/fonts/fonts.dir");
1500 while (!SLIST_EMPTY(nl
)) {
1501 np
= SLIST_FIRST(nl
);
1502 SLIST_REMOVE_HEAD(nl
, n_entry
);
1503 if (insert_font(np
->n_name
) == false)
1504 printf("failed to add font: %s\n", np
->n_name
);
1509 unsetenv("screen-font");
1510 env_setenv("screen-font", EV_VOLATILE
, NULL
, font_set
, env_nounset
);
1511 /* Trigger tem update. */
1512 tems
.update_font
= true;
1513 plat_cons_update_mode(-1);
1516 COMMAND_SET(load_font
, "loadfont", "load console font from file", command_font
);
1519 command_font(int argc
, char *argv
[])
1522 struct fontlist
*fl
;
1526 printf("Usage: loadfont [file.fnt]\n");
1531 char *name
= argv
[1];
1533 if (insert_font(name
) == false) {
1534 printf("loadfont error: failed to load: %s\n", name
);
1538 bd
= load_font(name
);
1540 printf("loadfont error: failed to load: %s\n", name
);
1544 /* Get the font list entry and mark it manually loaded. */
1545 STAILQ_FOREACH(fl
, &fonts
, font_next
) {
1546 if (strcmp(fl
->font_name
, name
) == 0)
1547 fl
->font_flags
= FONT_MANUAL
;
1549 tems
.update_font
= true;
1550 plat_cons_update_mode(-1);
1556 * Walk entire font list, release any loaded font, and set
1557 * autoload flag. If the font list is empty, the tem will
1558 * get the builtin default.
1560 STAILQ_FOREACH(fl
, &fonts
, font_next
) {
1561 if (fl
->font_data
->font
!= NULL
) {
1562 /* Note the tem is releasing font bytes */
1563 for (i
= 0; i
< VFNT_MAPS
; i
++)
1564 free(fl
->font_data
->font
->vf_map
[i
]);
1565 free(fl
->font_data
->font
);
1566 fl
->font_data
->font
= NULL
;
1567 fl
->font_data
->uncompressed_size
= 0;
1568 fl
->font_flags
= FONT_AUTO
;
1571 tems
.update_font
= true;
1572 plat_cons_update_mode(-1);