1 /*
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
5 * 1.0 of the CDDL.
6 *
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.
10 */
11
12 /*
13 * Copyright 2016 Toomas Soome <tsoome@me.com>
14 * Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
15 * Copyright 2020 RackTop Systems, Inc.
16 */
17
18 /*
19 * The workhorse here is gfxfb_blt(). It is implemented to mimic UEFI
20 * GOP Blt, and allows us to fill the rectangle on screen, copy
21 * rectangle from video to buffer and buffer to video and video to video.
22 * Such implementation does allow us to have almost identical implementation
23 * for both BIOS VBE and UEFI.
24 *
25 * ALL pixel data is assumed to be 32-bit BGRA (byte order Blue, Green, Red,
26 * Alpha) format, this allows us to only handle RGB data and not to worry
27 * about mixing RGB with indexed colors.
28 * Data exchange between memory buffer and video will translate BGRA
29 * and native format as following:
30 *
31 * 32-bit to/from 32-bit is trivial case.
32 * 32-bit to/from 24-bit is also simple - we just drop the alpha channel.
33 * 32-bit to/from 16-bit is more complicated, because we nee to handle
34 * data loss from 32-bit to 16-bit. While reading/writing from/to video, we
35 * need to apply masks of 16-bit color components. This will preserve
36 * colors for terminal text. For 32-bit truecolor PMG images, we need to
37 * translate 32-bit colors to 15/16 bit colors and this means data loss.
38 * There are different algorithms how to perform such color space reduction,
39 * we are currently using bitwise right shift to reduce color space and so far
40 * this technique seems to be sufficient (see also gfx_fb_putimage(), the
41 * end of for loop).
42 * 32-bit to/from 8-bit is the most troublesome because 8-bit colors are
43 * indexed. From video, we do get color indexes, and we do translate
44 * color index values to RGB. To write to video, we again need to translate
45 * RGB to color index. Additionally, we need to translate between VGA and
46 * Sun colors.
47 *
48 * Our internal color data is represented using BGRA format. But the hardware
49 * used indexed colors for 8-bit colors (0-255) and for this mode we do
50 * need to perform translation to/from BGRA and index values.
51 *
52 * - paletteentry RGB <-> index -
53 * BGRA BUFFER <----/ \ - VIDEO
54 * \ /
55 * - RGB (16/24/32) -
56 *
57 * To perform index to RGB translation, we use palette table generated
58 * from when we set up 8-bit mode video. We cannot read palette data from
59 * the hardware, because not all hardware supports reading it.
60 *
61 * BGRA to index is implemented in rgb_to_color_index() by searching
62 * palette array for closest match of RBG values.
63 *
64 * Note: In 8-bit mode, We do store first 16 colors to palette registers
65 * in VGA color order, this serves two purposes; firstly,
66 * if palette update is not supported, we still have correct 16 colors.
67 * Secondly, the kernel does get correct 16 colors when some other boot
68 * loader is used. However, the palette map for 8-bit colors is using
69 * Sun color ordering - this does allow us to skip translation
70 * from VGA colors to Sun colors, while we are reading RGB data.
71 */
72
73 #include <sys/cdefs.h>
74 #include <sys/param.h>
75 #include <stand.h>
76 #if defined(EFI)
77 #include <efi.h>
78 #include <efilib.h>
79 #include <Protocol/GraphicsOutput.h>
80 #else
81 #include <btxv86.h>
82 #include <vbe.h>
83 #endif
84 #include <sys/tem_impl.h>
85 #include <sys/consplat.h>
86 #include <sys/visual_io.h>
87 #include <sys/multiboot2.h>
88 #include <sys/font.h>
89 #include <sys/rgb.h>
90 #include <sys/endian.h>
91 #include <gfx_fb.h>
92 #include <pnglite.h>
93 #include <bootstrap.h>
94 #include <lz4.h>
95
96 /* VGA text mode does use bold font. */
97 #if !defined(VGA_8X16_FONT)
98 #define VGA_8X16_FONT "/boot/fonts/8x16b.fnt"
99 #endif
100 #if !defined(DEFAULT_8X16_FONT)
101 #define DEFAULT_8X16_FONT "/boot/fonts/8x16.fnt"
102 #endif
103
104 /*
105 * Global framebuffer struct, to be updated with mode changes.
106 */
107 multiboot_tag_framebuffer_t gfx_fb;
108
109 /* To support setenv, keep track of inverses and colors. */
110 static int gfx_inverse = 0;
111 static int gfx_inverse_screen = 0;
112 static uint8_t gfx_fg = DEFAULT_ANSI_FOREGROUND;
113 static uint8_t gfx_bg = DEFAULT_ANSI_BACKGROUND;
114 #if defined(EFI)
115 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *shadow_fb;
116 static EFI_GRAPHICS_OUTPUT_BLT_PIXEL *GlyphBuffer;
117 #else
118 struct paletteentry *shadow_fb;
119 static struct paletteentry *GlyphBuffer;
120 #endif
121 static size_t GlyphBufferSize;
122
123 int gfx_fb_cons_clear(struct vis_consclear *);
124 void gfx_fb_cons_copy(struct vis_conscopy *);
125 void gfx_fb_cons_display(struct vis_consdisplay *);
126
127 static bool insert_font(char *, FONT_FLAGS);
128
129 /*
130 * Set default operations to use bitmap based implementation.
131 * In case of UEFI, if GOP is available, we will switch to GOP based
132 * implementation.
133 *
134 * Also note, for UEFI we do attempt to boost the execution by setting
135 * Task Priority Level (TPL) to TPL_NOTIFY, which is highest priority
136 * usable in application.
137 */
138
139 /*
140 * Translate platform specific FB address.
141 */
142 static uint8_t *
gfx_get_fb_address(void)143 gfx_get_fb_address(void)
144 {
145 return ((uint8_t *)ptov(gfx_fb.framebuffer_common.framebuffer_addr));
146 }
147
148 /*
149 * Generic platform callbacks for tem.
150 */
151 void
plat_tem_get_prom_font_size(int * charheight,int * windowtop)152 plat_tem_get_prom_font_size(int *charheight, int *windowtop)
153 {
154 *charheight = 0;
155 *windowtop = 0;
156 }
157
158 void
plat_tem_get_colors(uint8_t * fg,uint8_t * bg)159 plat_tem_get_colors(uint8_t *fg, uint8_t *bg)
160 {
161 *fg = gfx_fg;
162 *bg = gfx_bg;
163 }
164
165 void
plat_tem_get_inverses(int * inverse,int * inverse_screen)166 plat_tem_get_inverses(int *inverse, int *inverse_screen)
167 {
168 *inverse = gfx_inverse;
169 *inverse_screen = gfx_inverse_screen;
170 }
171
172 /*
173 * Utility function to parse gfx mode line strings.
174 */
175 bool
gfx_parse_mode_str(char * str,int * x,int * y,int * depth)176 gfx_parse_mode_str(char *str, int *x, int *y, int *depth)
177 {
178 char *p, *end;
179
180 errno = 0;
181 p = str;
182 *x = strtoul(p, &end, 0);
183 if (*x == 0 || errno != 0)
184 return (false);
185 if (*end != 'x')
186 return (false);
187 p = end + 1;
188 *y = strtoul(p, &end, 0);
189 if (*y == 0 || errno != 0)
190 return (false);
191 if (*end != 'x') {
192 *depth = -1; /* auto select */
193 } else {
194 p = end + 1;
195 *depth = strtoul(p, &end, 0);
196 if (*depth == 0 || errno != 0 || *end != '\0')
197 return (false);
198 }
199
200 return (true);
201 }
202
203 uint32_t
gfx_fb_color_map(uint8_t index)204 gfx_fb_color_map(uint8_t index)
205 {
206 return (rgb_color_map(&rgb_info, index, 0xff));
207 }
208
209 static bool
color_name_to_ansi(const char * name,int * val)210 color_name_to_ansi(const char *name, int *val)
211 {
212 if (strcasecmp(name, "black") == 0) {
213 *val = ANSI_COLOR_BLACK;
214 return (true);
215 }
216 if (strcasecmp(name, "red") == 0) {
217 *val = ANSI_COLOR_RED;
218 return (true);
219 }
220 if (strcasecmp(name, "green") == 0) {
221 *val = ANSI_COLOR_GREEN;
222 return (true);
223 }
224 if (strcasecmp(name, "yellow") == 0) {
225 *val = ANSI_COLOR_YELLOW;
226 return (true);
227 }
228 if (strcasecmp(name, "blue") == 0) {
229 *val = ANSI_COLOR_BLUE;
230 return (true);
231 }
232 if (strcasecmp(name, "magenta") == 0) {
233 *val = ANSI_COLOR_MAGENTA;
234 return (true);
235 }
236 if (strcasecmp(name, "cyan") == 0) {
237 *val = ANSI_COLOR_CYAN;
238 return (true);
239 }
240 if (strcasecmp(name, "white") == 0) {
241 *val = ANSI_COLOR_WHITE;
242 return (true);
243 }
244 return (false);
245 }
246
247 /* Callback to check and set colors */
248 static int
gfx_set_colors(struct env_var * ev,int flags,const void * value)249 gfx_set_colors(struct env_var *ev, int flags, const void *value)
250 {
251 int val = 0, limit;
252 char buf[2];
253 const void *evalue;
254
255 if (value == NULL)
256 return (CMD_OK);
257
258 limit = 255;
259
260 if (color_name_to_ansi(value, &val)) {
261 snprintf(buf, sizeof (buf), "%d", val);
262 evalue = buf;
263 } else {
264 char *end;
265
266 errno = 0;
267 val = (int)strtol(value, &end, 0);
268 if (errno != 0 || *end != '\0') {
269 printf("Allowed values are either ansi color name or "
270 "number from range [0-255].\n");
271 return (CMD_OK);
272 }
273 evalue = value;
274 }
275
276 /* invalid value? */
277 if ((val < 0 || val > limit)) {
278 printf("Allowed values are either ansi color name or "
279 "number from range [0-255].\n");
280 return (CMD_OK);
281 }
282
283 if (strcmp(ev->ev_name, "tem.fg_color") == 0) {
284 /* is it already set? */
285 if (gfx_fg == val)
286 return (CMD_OK);
287 gfx_fg = val;
288 }
289 if (strcmp(ev->ev_name, "tem.bg_color") == 0) {
290 /* is it already set? */
291 if (gfx_bg == val)
292 return (CMD_OK);
293 gfx_bg = val;
294 }
295 env_setenv(ev->ev_name, flags | EV_NOHOOK, evalue, NULL, NULL);
296 plat_cons_update_mode(-1);
297 return (CMD_OK);
298 }
299
300 /* Callback to check and set inverses */
301 static int
gfx_set_inverses(struct env_var * ev,int flags,const void * value)302 gfx_set_inverses(struct env_var *ev, int flags, const void *value)
303 {
304 int t, f;
305
306 if (value == NULL)
307 return (CMD_OK);
308
309 t = strcmp(value, "true");
310 f = strcmp(value, "false");
311
312 /* invalid value? */
313 if (t != 0 && f != 0)
314 return (CMD_OK);
315
316 if (strcmp(ev->ev_name, "tem.inverse") == 0) {
317 /* is it already set? */
318 if (gfx_inverse == (t == 0))
319 return (CMD_OK);
320 gfx_inverse = (t == 0);
321 }
322 if (strcmp(ev->ev_name, "tem.inverse-screen") == 0) {
323 /* is it already set? */
324 if (gfx_inverse_screen == (t == 0))
325 return (CMD_OK);
326 gfx_inverse_screen = (t == 0);
327 }
328 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
329 plat_cons_update_mode(-1);
330 return (CMD_OK);
331 }
332
333 /*
334 * Initialize gfx framework.
335 */
336 void
gfx_framework_init(void)337 gfx_framework_init(void)
338 {
339 int rc, limit;
340 char *env, buf[2];
341
342 if (gfx_fb.framebuffer_common.framebuffer_bpp < 24)
343 limit = 7;
344 else
345 limit = 255;
346
347 /* set up tem inverse controls */
348 env = getenv("tem.inverse");
349 if (env != NULL) {
350 if (strcmp(env, "true") == 0)
351 gfx_inverse = 1;
352 unsetenv("tem.inverse");
353 }
354
355 env = getenv("tem.inverse-screen");
356 if (env != NULL) {
357 if (strcmp(env, "true") == 0)
358 gfx_inverse_screen = 1;
359 unsetenv("tem.inverse-screen");
360 }
361
362 if (gfx_inverse)
363 env = "true";
364 else
365 env = "false";
366
367 env_setenv("tem.inverse", EV_VOLATILE, env, gfx_set_inverses,
368 env_nounset);
369
370 if (gfx_inverse_screen)
371 env = "true";
372 else
373 env = "false";
374
375 env_setenv("tem.inverse-screen", EV_VOLATILE, env, gfx_set_inverses,
376 env_nounset);
377
378 /* set up tem color controls */
379 env = getenv("tem.fg_color");
380 if (env != NULL) {
381 rc = (int)strtol(env, NULL, 0);
382 if ((rc >= 0 && rc <= limit) && (rc <= 7 || rc >= 16))
383 gfx_fg = rc;
384 unsetenv("tem.fg_color");
385 }
386
387 env = getenv("tem.bg_color");
388 if (env != NULL) {
389 rc = (int)strtol(env, NULL, 0);
390 if ((rc >= 0 && rc <= limit) && (rc <= 7 || rc >= 16))
391 gfx_bg = rc;
392 unsetenv("tem.bg_color");
393 }
394
395 snprintf(buf, sizeof (buf), "%d", gfx_fg);
396 env_setenv("tem.fg_color", EV_VOLATILE, buf, gfx_set_colors,
397 env_nounset);
398 snprintf(buf, sizeof (buf), "%d", gfx_bg);
399 env_setenv("tem.bg_color", EV_VOLATILE, buf, gfx_set_colors,
400 env_nounset);
401
402 /*
403 * Setup font list to have builtin font.
404 */
405 (void) insert_font(NULL, FONT_BUILTIN);
406 }
407
408 /*
409 * Get indexed color from RGB. This function is used to write data to video
410 * memory when the adapter is set to use indexed colors.
411 * Since UEFI does only support 32-bit colors, we do not implement it for
412 * UEFI because there is no need for it and we do not have palette array
413 * for UEFI.
414 */
415 static uint8_t
rgb_to_color_index(uint8_t r,uint8_t g,uint8_t b)416 rgb_to_color_index(uint8_t r, uint8_t g, uint8_t b)
417 {
418 #if !defined(EFI)
419 uint32_t color, best, dist, k;
420 int diff;
421
422 color = 0;
423 best = 255 * 255 * 255;
424 for (k = 0; k < NCMAP; k++) {
425 diff = r - pe8[k].Red;
426 dist = diff * diff;
427 diff = g - pe8[k].Green;
428 dist += diff * diff;
429 diff = b - pe8[k].Blue;
430 dist += diff * diff;
431
432 /* Exact match, exit the loop */
433 if (dist == 0)
434 break;
435
436 if (dist < best) {
437 color = k;
438 best = dist;
439 }
440 }
441 if (k == NCMAP)
442 k = color;
443 return (k);
444 #else
445 (void) r;
446 (void) g;
447 (void) b;
448 return (0);
449 #endif
450 }
451
452 static void
gfx_mem_wr1(uint8_t * base,size_t size,uint32_t o,uint8_t v)453 gfx_mem_wr1(uint8_t *base, size_t size, uint32_t o, uint8_t v)
454 {
455
456 if (o >= size)
457 return;
458 *(uint8_t *)(base + o) = v;
459 }
460
461 static void
gfx_mem_wr2(uint8_t * base,size_t size,uint32_t o,uint16_t v)462 gfx_mem_wr2(uint8_t *base, size_t size, uint32_t o, uint16_t v)
463 {
464
465 if (o >= size)
466 return;
467 *(uint16_t *)(base + o) = v;
468 }
469
470 static void
gfx_mem_wr4(uint8_t * base,size_t size,uint32_t o,uint32_t v)471 gfx_mem_wr4(uint8_t *base, size_t size, uint32_t o, uint32_t v)
472 {
473
474 if (o >= size)
475 return;
476 *(uint32_t *)(base + o) = v;
477 }
478
479 static int
gfxfb_blt_fill(void * BltBuffer,uint32_t DestinationX,uint32_t DestinationY,uint32_t Width,uint32_t Height)480 gfxfb_blt_fill(void *BltBuffer,
481 uint32_t DestinationX, uint32_t DestinationY,
482 uint32_t Width, uint32_t Height)
483 {
484 #if defined(EFI)
485 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
486 #else
487 struct paletteentry *p;
488 #endif
489 uint32_t data, bpp, pitch, y, x;
490 size_t size;
491 off_t off;
492 uint8_t *destination;
493
494 if (BltBuffer == NULL)
495 return (EINVAL);
496
497 if (DestinationY + Height >
498 gfx_fb.framebuffer_common.framebuffer_height)
499 return (EINVAL);
500
501 if (DestinationX + Width > gfx_fb.framebuffer_common.framebuffer_width)
502 return (EINVAL);
503
504 if (Width == 0 || Height == 0)
505 return (EINVAL);
506
507 p = BltBuffer;
508 if (gfx_fb.framebuffer_common.framebuffer_bpp == 8) {
509 data = rgb_to_color_index(p->Red, p->Green, p->Blue);
510 } else {
511 data = (p->Red &
512 ((1 << gfx_fb.u.fb2.framebuffer_red_mask_size) - 1)) <<
513 gfx_fb.u.fb2.framebuffer_red_field_position;
514 data |= (p->Green &
515 ((1 << gfx_fb.u.fb2.framebuffer_green_mask_size) - 1)) <<
516 gfx_fb.u.fb2.framebuffer_green_field_position;
517 data |= (p->Blue &
518 ((1 << gfx_fb.u.fb2.framebuffer_blue_mask_size) - 1)) <<
519 gfx_fb.u.fb2.framebuffer_blue_field_position;
520 }
521
522 bpp = roundup2(gfx_fb.framebuffer_common.framebuffer_bpp, 8) >> 3;
523 pitch = gfx_fb.framebuffer_common.framebuffer_pitch;
524 destination = gfx_get_fb_address();
525 size = gfx_fb.framebuffer_common.framebuffer_height * pitch;
526
527 for (y = DestinationY; y < Height + DestinationY; y++) {
528 off = y * pitch + DestinationX * bpp;
529 for (x = 0; x < Width; x++) {
530 switch (bpp) {
531 case 1:
532 gfx_mem_wr1(destination, size, off,
533 (data < NCOLORS) ?
534 solaris_color_to_pc_color[data] : data);
535 break;
536 case 2:
537 gfx_mem_wr2(destination, size, off, data);
538 break;
539 case 3:
540 gfx_mem_wr1(destination, size, off,
541 (data >> 16) & 0xff);
542 gfx_mem_wr1(destination, size, off + 1,
543 (data >> 8) & 0xff);
544 gfx_mem_wr1(destination, size, off + 2,
545 data & 0xff);
546 break;
547 case 4:
548 gfx_mem_wr4(destination, size, off, data);
549 break;
550 default:
551 return (EINVAL);
552 }
553 off += bpp;
554 }
555 }
556
557 return (0);
558 }
559
560 static int
gfxfb_blt_video_to_buffer(void * BltBuffer,uint32_t SourceX,uint32_t SourceY,uint32_t DestinationX,uint32_t DestinationY,uint32_t Width,uint32_t Height,uint32_t Delta)561 gfxfb_blt_video_to_buffer(void *BltBuffer, uint32_t SourceX, uint32_t SourceY,
562 uint32_t DestinationX, uint32_t DestinationY,
563 uint32_t Width, uint32_t Height, uint32_t Delta)
564 {
565 #if defined(EFI)
566 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
567 #else
568 struct paletteentry *p;
569 #endif
570 uint32_t x, sy, dy;
571 uint32_t bpp, pitch, copybytes;
572 off_t off;
573 uint8_t *source, *destination, *sb;
574 uint8_t rm, rp, gm, gp, bm, bp;
575 bool bgra;
576
577 if (BltBuffer == NULL)
578 return (EINVAL);
579
580 if (SourceY + Height >
581 gfx_fb.framebuffer_common.framebuffer_height)
582 return (EINVAL);
583
584 if (SourceX + Width > gfx_fb.framebuffer_common.framebuffer_width)
585 return (EINVAL);
586
587 if (Width == 0 || Height == 0)
588 return (EINVAL);
589
590 if (Delta == 0)
591 Delta = Width * sizeof (*p);
592
593 bpp = roundup2(gfx_fb.framebuffer_common.framebuffer_bpp, 8) >> 3;
594 pitch = gfx_fb.framebuffer_common.framebuffer_pitch;
595
596 copybytes = Width * bpp;
597
598 rm = (1 << gfx_fb.u.fb2.framebuffer_red_mask_size) - 1;
599 rp = gfx_fb.u.fb2.framebuffer_red_field_position;
600 gm = (1 << gfx_fb.u.fb2.framebuffer_green_mask_size) - 1;
601 gp = gfx_fb.u.fb2.framebuffer_green_field_position;
602 bm = (1 << gfx_fb.u.fb2.framebuffer_blue_mask_size) - 1;
603 bp = gfx_fb.u.fb2.framebuffer_blue_field_position;
604 /* If FB pixel format is BGRA, we can use direct copy. */
605 bgra = bpp == 4 &&
606 gfx_fb.u.fb2.framebuffer_red_mask_size == 8 &&
607 gfx_fb.u.fb2.framebuffer_red_field_position == 16 &&
608 gfx_fb.u.fb2.framebuffer_green_mask_size == 8 &&
609 gfx_fb.u.fb2.framebuffer_green_field_position == 8 &&
610 gfx_fb.u.fb2.framebuffer_blue_mask_size == 8 &&
611 gfx_fb.u.fb2.framebuffer_blue_field_position == 0;
612
613 for (sy = SourceY, dy = DestinationY; dy < Height + DestinationY;
614 sy++, dy++) {
615 off = sy * pitch + SourceX * bpp;
616 source = gfx_get_fb_address() + off;
617 destination = (uint8_t *)BltBuffer + dy * Delta +
618 DestinationX * sizeof (*p);
619
620 if (bgra) {
621 bcopy(source, destination, copybytes);
622 } else {
623 for (x = 0; x < Width; x++) {
624 uint32_t c = 0;
625
626 p = (void *)(destination + x * sizeof (*p));
627 sb = source + x * bpp;
628 switch (bpp) {
629 case 1:
630 c = *sb;
631 break;
632 case 2:
633 c = *(uint16_t *)sb;
634 break;
635 case 3:
636 c = sb[0] << 16 | sb[1] << 8 | sb[2];
637 break;
638 case 4:
639 c = *(uint32_t *)sb;
640 break;
641 default:
642 return (EINVAL);
643 }
644
645 if (bpp == 1) {
646 *(uint32_t *)p = gfx_fb_color_map(
647 (c < NCOLORS) ?
648 pc_color_to_solaris_color[c] : c);
649 } else {
650 p->Red = (c >> rp) & rm;
651 p->Green = (c >> gp) & gm;
652 p->Blue = (c >> bp) & bm;
653 p->Reserved = 0;
654 }
655 }
656 }
657 }
658
659 return (0);
660 }
661
662 static int
gfxfb_blt_buffer_to_video(void * BltBuffer,uint32_t SourceX,uint32_t SourceY,uint32_t DestinationX,uint32_t DestinationY,uint32_t Width,uint32_t Height,uint32_t Delta)663 gfxfb_blt_buffer_to_video(void *BltBuffer, uint32_t SourceX, uint32_t SourceY,
664 uint32_t DestinationX, uint32_t DestinationY,
665 uint32_t Width, uint32_t Height, uint32_t Delta)
666 {
667 #if defined(EFI)
668 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
669 #else
670 struct paletteentry *p;
671 #endif
672 uint32_t x, sy, dy;
673 uint32_t bpp, pitch, copybytes;
674 off_t off;
675 uint8_t *source, *destination;
676 uint8_t rm, rp, gm, gp, bm, bp;
677 bool bgra;
678
679 if (BltBuffer == NULL)
680 return (EINVAL);
681
682 if (DestinationY + Height >
683 gfx_fb.framebuffer_common.framebuffer_height)
684 return (EINVAL);
685
686 if (DestinationX + Width > gfx_fb.framebuffer_common.framebuffer_width)
687 return (EINVAL);
688
689 if (Width == 0 || Height == 0)
690 return (EINVAL);
691
692 if (Delta == 0)
693 Delta = Width * sizeof (*p);
694
695 bpp = roundup2(gfx_fb.framebuffer_common.framebuffer_bpp, 8) >> 3;
696 pitch = gfx_fb.framebuffer_common.framebuffer_pitch;
697
698 copybytes = Width * bpp;
699
700 rm = (1 << gfx_fb.u.fb2.framebuffer_red_mask_size) - 1;
701 rp = gfx_fb.u.fb2.framebuffer_red_field_position;
702 gm = (1 << gfx_fb.u.fb2.framebuffer_green_mask_size) - 1;
703 gp = gfx_fb.u.fb2.framebuffer_green_field_position;
704 bm = (1 << gfx_fb.u.fb2.framebuffer_blue_mask_size) - 1;
705 bp = gfx_fb.u.fb2.framebuffer_blue_field_position;
706 /* If FB pixel format is BGRA, we can use direct copy. */
707 bgra = bpp == 4 &&
708 gfx_fb.u.fb2.framebuffer_red_mask_size == 8 &&
709 gfx_fb.u.fb2.framebuffer_red_field_position == 16 &&
710 gfx_fb.u.fb2.framebuffer_green_mask_size == 8 &&
711 gfx_fb.u.fb2.framebuffer_green_field_position == 8 &&
712 gfx_fb.u.fb2.framebuffer_blue_mask_size == 8 &&
713 gfx_fb.u.fb2.framebuffer_blue_field_position == 0;
714
715 for (sy = SourceY, dy = DestinationY; sy < Height + SourceY;
716 sy++, dy++) {
717 off = dy * pitch + DestinationX * bpp;
718 destination = gfx_get_fb_address() + off;
719
720 if (bgra) {
721 source = (uint8_t *)BltBuffer + sy * Delta +
722 SourceX * sizeof (*p);
723 bcopy(source, destination, copybytes);
724 } else {
725 for (x = 0; x < Width; x++) {
726 uint32_t c;
727
728 p = (void *)((uint8_t *)BltBuffer +
729 sy * Delta +
730 (SourceX + x) * sizeof (*p));
731 if (bpp == 1) {
732 c = rgb_to_color_index(p->Red,
733 p->Green, p->Blue);
734 } else {
735 c = (p->Red & rm) << rp |
736 (p->Green & gm) << gp |
737 (p->Blue & bm) << bp;
738 }
739 off = x * bpp;
740 switch (bpp) {
741 case 1:
742 gfx_mem_wr1(destination, copybytes,
743 off, (c < NCOLORS) ?
744 solaris_color_to_pc_color[c] : c);
745 break;
746 case 2:
747 gfx_mem_wr2(destination, copybytes,
748 off, c);
749 break;
750 case 3:
751 gfx_mem_wr1(destination, copybytes,
752 off, (c >> 16) & 0xff);
753 gfx_mem_wr1(destination, copybytes,
754 off + 1, (c >> 8) & 0xff);
755 gfx_mem_wr1(destination, copybytes,
756 off + 2, c & 0xff);
757 break;
758 case 4:
759 gfx_mem_wr4(destination, copybytes,
760 off, c);
761 break;
762 default:
763 return (EINVAL);
764 }
765 }
766 }
767 }
768
769 return (0);
770 }
771
772 static int
gfxfb_blt_video_to_video(uint32_t SourceX,uint32_t SourceY,uint32_t DestinationX,uint32_t DestinationY,uint32_t Width,uint32_t Height)773 gfxfb_blt_video_to_video(uint32_t SourceX, uint32_t SourceY,
774 uint32_t DestinationX, uint32_t DestinationY,
775 uint32_t Width, uint32_t Height)
776 {
777 uint32_t bpp, copybytes;
778 int pitch;
779 uint8_t *source, *destination;
780 off_t off;
781
782 if (SourceY + Height >
783 gfx_fb.framebuffer_common.framebuffer_height)
784 return (EINVAL);
785
786 if (SourceX + Width > gfx_fb.framebuffer_common.framebuffer_width)
787 return (EINVAL);
788
789 if (DestinationY + Height >
790 gfx_fb.framebuffer_common.framebuffer_height)
791 return (EINVAL);
792
793 if (DestinationX + Width > gfx_fb.framebuffer_common.framebuffer_width)
794 return (EINVAL);
795
796 if (Width == 0 || Height == 0)
797 return (EINVAL);
798
799 bpp = roundup2(gfx_fb.framebuffer_common.framebuffer_bpp, 8) >> 3;
800 pitch = gfx_fb.framebuffer_common.framebuffer_pitch;
801
802 copybytes = Width * bpp;
803
804 off = SourceY * pitch + SourceX * bpp;
805 source = gfx_get_fb_address() + off;
806 off = DestinationY * pitch + DestinationX * bpp;
807 destination = gfx_get_fb_address() + off;
808
809 /*
810 * To handle overlapping areas, set up reverse copy here.
811 */
812 if ((uintptr_t)destination > (uintptr_t)source) {
813 source += Height * pitch;
814 destination += Height * pitch;
815 pitch = -pitch;
816 }
817
818 while (Height-- > 0) {
819 bcopy(source, destination, copybytes);
820 source += pitch;
821 destination += pitch;
822 }
823
824 return (0);
825 }
826
827 static void
gfxfb_shadow_fill(uint32_t * BltBuffer,uint32_t DestinationX,uint32_t DestinationY,uint32_t Width,uint32_t Height)828 gfxfb_shadow_fill(uint32_t *BltBuffer,
829 uint32_t DestinationX, uint32_t DestinationY,
830 uint32_t Width, uint32_t Height)
831 {
832 uint32_t fbX, fbY;
833
834 if (shadow_fb == NULL)
835 return;
836
837 fbX = gfx_fb.framebuffer_common.framebuffer_width;
838 fbY = gfx_fb.framebuffer_common.framebuffer_height;
839
840 if (BltBuffer == NULL)
841 return;
842
843 if (DestinationX + Width > fbX)
844 Width = fbX - DestinationX;
845
846 if (DestinationY + Height > fbY)
847 Height = fbY - DestinationY;
848
849 uint32_t y2 = Height + DestinationY;
850 for (uint32_t y1 = DestinationY; y1 < y2; y1++) {
851 uint32_t off = y1 * fbX + DestinationX;
852
853 for (uint32_t x = 0; x < Width; x++) {
854 *(uint32_t *)&shadow_fb[off + x] = *BltBuffer;
855 }
856 }
857 }
858
859 int
gfxfb_blt(void * BltBuffer,GFXFB_BLT_OPERATION BltOperation,uint32_t SourceX,uint32_t SourceY,uint32_t DestinationX,uint32_t DestinationY,uint32_t Width,uint32_t Height,uint32_t Delta)860 gfxfb_blt(void *BltBuffer, GFXFB_BLT_OPERATION BltOperation,
861 uint32_t SourceX, uint32_t SourceY,
862 uint32_t DestinationX, uint32_t DestinationY,
863 uint32_t Width, uint32_t Height, uint32_t Delta)
864 {
865 int rv;
866 #if defined(EFI)
867 EFI_STATUS status;
868 EFI_TPL tpl;
869 extern EFI_GRAPHICS_OUTPUT_PROTOCOL *gop;
870
871 /*
872 * We assume Blt() does work, if not, we will need to build
873 * exception list case by case.
874 * Once boot services are off, we can not use GOP Blt().
875 */
876 if (gop != NULL && has_boot_services) {
877 tpl = BS->RaiseTPL(TPL_NOTIFY);
878 switch (BltOperation) {
879 case GfxFbBltVideoFill:
880 gfxfb_shadow_fill(BltBuffer, DestinationX,
881 DestinationY, Width, Height);
882 status = gop->Blt(gop, BltBuffer, EfiBltVideoFill,
883 SourceX, SourceY, DestinationX, DestinationY,
884 Width, Height, Delta);
885 break;
886
887 case GfxFbBltVideoToBltBuffer:
888 status = gop->Blt(gop, BltBuffer,
889 EfiBltVideoToBltBuffer,
890 SourceX, SourceY, DestinationX, DestinationY,
891 Width, Height, Delta);
892 break;
893
894 case GfxFbBltBufferToVideo:
895 status = gop->Blt(gop, BltBuffer, EfiBltBufferToVideo,
896 SourceX, SourceY, DestinationX, DestinationY,
897 Width, Height, Delta);
898 break;
899
900 case GfxFbBltVideoToVideo:
901 status = gop->Blt(gop, BltBuffer, EfiBltVideoToVideo,
902 SourceX, SourceY, DestinationX, DestinationY,
903 Width, Height, Delta);
904 break;
905
906 default:
907 status = EFI_INVALID_PARAMETER;
908 break;
909 }
910
911 switch (status) {
912 case EFI_SUCCESS:
913 rv = 0;
914 break;
915
916 case EFI_INVALID_PARAMETER:
917 rv = EINVAL;
918 break;
919
920 case EFI_DEVICE_ERROR:
921 default:
922 rv = EIO;
923 break;
924 }
925
926 BS->RestoreTPL(tpl);
927 return (rv);
928 }
929 #endif
930
931 switch (BltOperation) {
932 case GfxFbBltVideoFill:
933 gfxfb_shadow_fill(BltBuffer, DestinationX, DestinationY,
934 Width, Height);
935 rv = gfxfb_blt_fill(BltBuffer, DestinationX, DestinationY,
936 Width, Height);
937 break;
938
939 case GfxFbBltVideoToBltBuffer:
940 rv = gfxfb_blt_video_to_buffer(BltBuffer, SourceX, SourceY,
941 DestinationX, DestinationY, Width, Height, Delta);
942 break;
943
944 case GfxFbBltBufferToVideo:
945 rv = gfxfb_blt_buffer_to_video(BltBuffer, SourceX, SourceY,
946 DestinationX, DestinationY, Width, Height, Delta);
947 break;
948
949 case GfxFbBltVideoToVideo:
950 rv = gfxfb_blt_video_to_video(SourceX, SourceY,
951 DestinationX, DestinationY, Width, Height);
952 break;
953
954 default:
955 rv = EINVAL;
956 break;
957 }
958 return (rv);
959 }
960
961 /*
962 * visual io callbacks.
963 */
964 int
gfx_fb_cons_clear(struct vis_consclear * ca)965 gfx_fb_cons_clear(struct vis_consclear *ca)
966 {
967 int rv;
968 uint32_t width, height;
969
970 width = gfx_fb.framebuffer_common.framebuffer_width;
971 height = gfx_fb.framebuffer_common.framebuffer_height;
972
973 rv = gfxfb_blt(&ca->bg_color, GfxFbBltVideoFill, 0, 0,
974 0, 0, width, height, 0);
975
976 return (rv);
977 }
978
979 void
gfx_fb_cons_copy(struct vis_conscopy * ma)980 gfx_fb_cons_copy(struct vis_conscopy *ma)
981 {
982 #if defined(EFI)
983 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *source, *destination;
984 #else
985 struct paletteentry *source, *destination;
986 #endif
987 uint32_t width, height, bytes;
988 uint32_t sx, sy, dx, dy;
989 uint32_t pitch;
990 int step;
991
992 width = ma->e_col - ma->s_col + 1;
993 height = ma->e_row - ma->s_row + 1;
994
995 sx = ma->s_col;
996 sy = ma->s_row;
997 dx = ma->t_col;
998 dy = ma->t_row;
999
1000 if (sx + width > gfx_fb.framebuffer_common.framebuffer_width)
1001 width = gfx_fb.framebuffer_common.framebuffer_width - sx;
1002
1003 if (sy + height > gfx_fb.framebuffer_common.framebuffer_height)
1004 height = gfx_fb.framebuffer_common.framebuffer_height - sy;
1005
1006 if (dx + width > gfx_fb.framebuffer_common.framebuffer_width)
1007 width = gfx_fb.framebuffer_common.framebuffer_width - dx;
1008
1009 if (dy + height > gfx_fb.framebuffer_common.framebuffer_height)
1010 height = gfx_fb.framebuffer_common.framebuffer_height - dy;
1011
1012 if (width == 0 || height == 0)
1013 return;
1014
1015 /*
1016 * With no shadow fb, use video to video copy.
1017 */
1018 if (shadow_fb == NULL) {
1019 (void) gfxfb_blt(NULL, GfxFbBltVideoToVideo,
1020 sx, sy, dx, dy, width, height, 0);
1021 return;
1022 }
1023
1024 /*
1025 * With shadow fb, we need to copy data on both shadow and video,
1026 * to preserve the consistency. We only read data from shadow fb.
1027 */
1028
1029 step = 1;
1030 pitch = gfx_fb.framebuffer_common.framebuffer_width;
1031 bytes = width * sizeof (*shadow_fb);
1032
1033 /*
1034 * To handle overlapping areas, set up reverse copy here.
1035 */
1036 if (dy * pitch + dx > sy * pitch + sx) {
1037 sy += height;
1038 dy += height;
1039 step = -step;
1040 }
1041
1042 while (height-- > 0) {
1043 source = &shadow_fb[sy * pitch + sx];
1044 destination = &shadow_fb[dy * pitch + dx];
1045
1046 bcopy(source, destination, bytes);
1047 (void) gfxfb_blt(destination, GfxFbBltBufferToVideo,
1048 0, 0, dx, dy, width, 1, 0);
1049
1050 sy += step;
1051 dy += step;
1052 }
1053 }
1054
1055 /*
1056 * Implements alpha blending for RGBA data, could use pixels for arguments,
1057 * but byte stream seems more generic.
1058 * The generic alpha blending is:
1059 * blend = alpha * fg + (1.0 - alpha) * bg.
1060 * Since our alpha is not from range [0..1], we scale appropriately.
1061 */
1062 static uint8_t
alpha_blend(uint8_t fg,uint8_t bg,uint8_t alpha)1063 alpha_blend(uint8_t fg, uint8_t bg, uint8_t alpha)
1064 {
1065 uint16_t blend, h, l;
1066 uint8_t max_alpha;
1067
1068 /* 15/16 bit depths have alpha channel size less than 8 */
1069 max_alpha = (1 << (rgb_info.red.size + rgb_info.green.size +
1070 rgb_info.blue.size) / 3) - 1;
1071
1072 /* trivial corner cases */
1073 if (alpha == 0)
1074 return (bg);
1075 if (alpha >= max_alpha)
1076 return (fg);
1077 blend = (alpha * fg + (max_alpha - alpha) * bg);
1078 /* Division by max_alpha */
1079 h = blend >> 8;
1080 l = blend & max_alpha;
1081 if (h + l >= max_alpha)
1082 h++;
1083 return (h);
1084 }
1085
1086 /* Copy memory to framebuffer or to memory. */
1087 static void
bitmap_cpy(void * dst,void * src,size_t size)1088 bitmap_cpy(void *dst, void *src, size_t size)
1089 {
1090 #if defined(EFI)
1091 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *ps, *pd;
1092 #else
1093 struct paletteentry *ps, *pd;
1094 #endif
1095 uint32_t i;
1096 uint8_t a;
1097
1098 ps = src;
1099 pd = dst;
1100
1101 for (i = 0; i < size; i++) {
1102 a = ps[i].Reserved;
1103 pd[i].Red = alpha_blend(ps[i].Red, pd[i].Red, a);
1104 pd[i].Green = alpha_blend(ps[i].Green, pd[i].Green, a);
1105 pd[i].Blue = alpha_blend(ps[i].Blue, pd[i].Blue, a);
1106 pd[i].Reserved = a;
1107 }
1108 }
1109
1110 static void *
allocate_glyphbuffer(uint32_t width,uint32_t height)1111 allocate_glyphbuffer(uint32_t width, uint32_t height)
1112 {
1113 size_t size;
1114
1115 size = sizeof (*GlyphBuffer) * width * height;
1116 if (size != GlyphBufferSize) {
1117 free(GlyphBuffer);
1118 GlyphBuffer = malloc(size);
1119 if (GlyphBuffer == NULL)
1120 return (NULL);
1121 GlyphBufferSize = size;
1122 }
1123 return (GlyphBuffer);
1124 }
1125
1126 void
gfx_fb_cons_display(struct vis_consdisplay * da)1127 gfx_fb_cons_display(struct vis_consdisplay *da)
1128 {
1129 #if defined(EFI)
1130 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, *data;
1131 #else
1132 struct paletteentry *BltBuffer, *data;
1133 #endif
1134 uint32_t size;
1135
1136 /* make sure we will not write past FB */
1137 if ((uint32_t)da->col >= gfx_fb.framebuffer_common.framebuffer_width ||
1138 (uint32_t)da->row >= gfx_fb.framebuffer_common.framebuffer_height ||
1139 (uint32_t)da->col + da->width >
1140 gfx_fb.framebuffer_common.framebuffer_width ||
1141 (uint32_t)da->row + da->height >
1142 gfx_fb.framebuffer_common.framebuffer_height)
1143 return;
1144
1145 /*
1146 * If we do have shadow fb, we will use shadow to render data,
1147 * and copy shadow to video.
1148 */
1149 if (shadow_fb != NULL) {
1150 uint32_t pitch = gfx_fb.framebuffer_common.framebuffer_width;
1151 uint32_t dx, dy, width, height;
1152
1153 dx = da->col;
1154 dy = da->row;
1155 height = da->height;
1156 width = da->width;
1157
1158 data = (void *)da->data;
1159 /* Copy rectangle line by line. */
1160 for (uint32_t y = 0; y < height; y++) {
1161 BltBuffer = shadow_fb + dy * pitch + dx;
1162 bitmap_cpy(BltBuffer, &data[y * width], width);
1163 (void) gfxfb_blt(BltBuffer, GfxFbBltBufferToVideo,
1164 0, 0, dx, dy, width, 1, 0);
1165 dy++;
1166 }
1167 return;
1168 }
1169
1170 /*
1171 * Common data to display is glyph, use preallocated
1172 * glyph buffer.
1173 */
1174 if (tems.ts_pix_data_size != GlyphBufferSize)
1175 (void) allocate_glyphbuffer(da->width, da->height);
1176
1177 size = sizeof (*BltBuffer) * da->width * da->height;
1178 if (size == GlyphBufferSize) {
1179 BltBuffer = GlyphBuffer;
1180 } else {
1181 BltBuffer = malloc(size);
1182 }
1183 if (BltBuffer == NULL)
1184 return;
1185
1186 if (gfxfb_blt(BltBuffer, GfxFbBltVideoToBltBuffer,
1187 da->col, da->row, 0, 0, da->width, da->height, 0) == 0) {
1188 bitmap_cpy(BltBuffer, da->data, da->width * da->height);
1189 (void) gfxfb_blt(BltBuffer, GfxFbBltBufferToVideo,
1190 0, 0, da->col, da->row, da->width, da->height, 0);
1191 }
1192
1193 if (BltBuffer != GlyphBuffer)
1194 free(BltBuffer);
1195 }
1196
1197 static void
gfx_fb_cursor_impl(void * buf,uint32_t stride,uint32_t fg,uint32_t bg,struct vis_conscursor * ca)1198 gfx_fb_cursor_impl(void *buf, uint32_t stride, uint32_t fg, uint32_t bg,
1199 struct vis_conscursor *ca)
1200 {
1201 #if defined(EFI)
1202 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
1203 #else
1204 struct paletteentry *p;
1205 #endif
1206 union pixel {
1207 #if defined(EFI)
1208 EFI_GRAPHICS_OUTPUT_BLT_PIXEL p;
1209 #else
1210 struct paletteentry p;
1211 #endif
1212 uint32_t p32;
1213 } *row;
1214
1215 p = buf;
1216
1217 /*
1218 * Build inverse image of the glyph.
1219 * Since xor has self-inverse property, drawing cursor
1220 * second time on the same spot, will restore the original content.
1221 */
1222 for (screen_size_t i = 0; i < ca->height; i++) {
1223 row = (union pixel *)(p + i * stride);
1224 for (screen_size_t j = 0; j < ca->width; j++) {
1225 row[j].p32 = (row[j].p32 ^ fg) ^ bg;
1226 }
1227 }
1228 }
1229
1230 void
gfx_fb_display_cursor(struct vis_conscursor * ca)1231 gfx_fb_display_cursor(struct vis_conscursor *ca)
1232 {
1233 union pixel {
1234 #if defined(EFI)
1235 EFI_GRAPHICS_OUTPUT_BLT_PIXEL p;
1236 #else
1237 struct paletteentry p;
1238 #endif
1239 uint32_t p32;
1240 } fg, bg;
1241
1242 bcopy(&ca->fg_color, &fg.p32, sizeof (fg.p32));
1243 bcopy(&ca->bg_color, &bg.p32, sizeof (bg.p32));
1244
1245 if (shadow_fb == NULL &&
1246 allocate_glyphbuffer(ca->width, ca->height) != NULL) {
1247 if (gfxfb_blt(GlyphBuffer, GfxFbBltVideoToBltBuffer,
1248 ca->col, ca->row, 0, 0, ca->width, ca->height, 0) == 0)
1249 gfx_fb_cursor_impl(GlyphBuffer, ca->width,
1250 fg.p32, bg.p32, ca);
1251
1252 (void) gfxfb_blt(GlyphBuffer, GfxFbBltBufferToVideo, 0, 0,
1253 ca->col, ca->row, ca->width, ca->height, 0);
1254 return;
1255 }
1256
1257 uint32_t pitch = gfx_fb.framebuffer_common.framebuffer_width;
1258 uint32_t dx, dy, width, height;
1259
1260 dx = ca->col;
1261 dy = ca->row;
1262 width = ca->width;
1263 height = ca->height;
1264
1265 gfx_fb_cursor_impl(shadow_fb + dy * pitch + dx, pitch,
1266 fg.p32, bg.p32, ca);
1267 /* Copy rectangle line by line. */
1268 for (uint32_t y = 0; y < height; y++) {
1269 (void) gfxfb_blt(shadow_fb + dy * pitch + dx,
1270 GfxFbBltBufferToVideo, 0, 0, dx, dy, width, 1, 0);
1271 dy++;
1272 }
1273 }
1274
1275 /*
1276 * Public graphics primitives.
1277 */
1278
1279 static int
isqrt(int num)1280 isqrt(int num)
1281 {
1282 int res = 0;
1283 int bit = 1 << 30;
1284
1285 /* "bit" starts at the highest power of four <= the argument. */
1286 while (bit > num)
1287 bit >>= 2;
1288
1289 while (bit != 0) {
1290 if (num >= res + bit) {
1291 num -= res + bit;
1292 res = (res >> 1) + bit;
1293 } else
1294 res >>= 1;
1295 bit >>= 2;
1296 }
1297 return (res);
1298 }
1299
1300 /* set pixel in framebuffer using gfx coordinates */
1301 void
gfx_fb_setpixel(uint32_t x,uint32_t y)1302 gfx_fb_setpixel(uint32_t x, uint32_t y)
1303 {
1304 text_color_t fg, bg;
1305
1306 if (plat_stdout_is_framebuffer() == 0)
1307 return;
1308
1309 tem_get_colors((tem_vt_state_t)tems.ts_active, &fg, &bg);
1310
1311 if (x >= gfx_fb.framebuffer_common.framebuffer_width ||
1312 y >= gfx_fb.framebuffer_common.framebuffer_height)
1313 return;
1314
1315 gfxfb_blt(&fg.n, GfxFbBltVideoFill, 0, 0, x, y, 1, 1, 0);
1316 }
1317
1318 /*
1319 * draw rectangle in framebuffer using gfx coordinates.
1320 */
1321 void
gfx_fb_drawrect(uint32_t x1,uint32_t y1,uint32_t x2,uint32_t y2,uint32_t fill)1322 gfx_fb_drawrect(uint32_t x1, uint32_t y1, uint32_t x2, uint32_t y2,
1323 uint32_t fill)
1324 {
1325 text_color_t fg, bg;
1326
1327 if (plat_stdout_is_framebuffer() == 0)
1328 return;
1329
1330 tem_get_colors((tem_vt_state_t)tems.ts_active, &fg, &bg);
1331
1332 if (fill != 0) {
1333 gfxfb_blt(&fg.n, GfxFbBltVideoFill,
1334 0, 0, x1, y1, x2 - x1, y2 - y1, 0);
1335 } else {
1336 gfxfb_blt(&fg.n, GfxFbBltVideoFill,
1337 0, 0, x1, y1, x2 - x1, 1, 0);
1338 gfxfb_blt(&fg.n, GfxFbBltVideoFill,
1339 0, 0, x1, y2, x2 - x1, 1, 0);
1340 gfxfb_blt(&fg.n, GfxFbBltVideoFill,
1341 0, 0, x1, y1, 1, y2 - y1, 0);
1342 gfxfb_blt(&fg.n, GfxFbBltVideoFill,
1343 0, 0, x2, y1, 1, y2 - y1, 0);
1344 }
1345 }
1346
1347 void
gfx_fb_line(uint32_t x0,uint32_t y0,uint32_t x1,uint32_t y1,uint32_t wd)1348 gfx_fb_line(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t wd)
1349 {
1350 int dx, sx, dy, sy;
1351 int err, e2, x2, y2, ed, width;
1352
1353 if (plat_stdout_is_framebuffer() == 0)
1354 return;
1355
1356 width = wd;
1357 sx = x0 < x1? 1 : -1;
1358 sy = y0 < y1? 1 : -1;
1359 dx = x1 > x0? x1 - x0 : x0 - x1;
1360 dy = y1 > y0? y1 - y0 : y0 - y1;
1361 err = dx + dy;
1362 ed = dx + dy == 0 ? 1: isqrt(dx * dx + dy * dy);
1363
1364 for (;;) {
1365 gfx_fb_setpixel(x0, y0);
1366 e2 = err;
1367 x2 = x0;
1368 if ((e2 << 1) >= -dx) { /* x step */
1369 e2 += dy;
1370 y2 = y0;
1371 while (e2 < ed * width &&
1372 (y1 != (uint32_t)y2 || dx > dy)) {
1373 y2 += sy;
1374 gfx_fb_setpixel(x0, y2);
1375 e2 += dx;
1376 }
1377 if (x0 == x1)
1378 break;
1379 e2 = err;
1380 err -= dy;
1381 x0 += sx;
1382 }
1383 if ((e2 << 1) <= dy) { /* y step */
1384 e2 = dx-e2;
1385 while (e2 < ed * width &&
1386 (x1 != (uint32_t)x2 || dx < dy)) {
1387 x2 += sx;
1388 gfx_fb_setpixel(x2, y0);
1389 e2 += dy;
1390 }
1391 if (y0 == y1)
1392 break;
1393 err += dx;
1394 y0 += sy;
1395 }
1396 }
1397 }
1398
1399 /*
1400 * quadratic Bézier curve limited to gradients without sign change.
1401 */
1402 void
gfx_fb_bezier(uint32_t x0,uint32_t y0,uint32_t x1,uint32_t y1,uint32_t x2,uint32_t y2,uint32_t wd)1403 gfx_fb_bezier(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t x2,
1404 uint32_t y2, uint32_t wd)
1405 {
1406 int sx, sy, xx, yy, xy, width;
1407 int dx, dy, err, curvature;
1408 int i;
1409
1410 if (plat_stdout_is_framebuffer() == 0)
1411 return;
1412
1413 width = wd;
1414 sx = x2 - x1;
1415 sy = y2 - y1;
1416 xx = x0 - x1;
1417 yy = y0 - y1;
1418 curvature = xx*sy - yy*sx;
1419
1420 if (sx*sx + sy*sy > xx*xx+yy*yy) {
1421 x2 = x0;
1422 x0 = sx + x1;
1423 y2 = y0;
1424 y0 = sy + y1;
1425 curvature = -curvature;
1426 }
1427 if (curvature != 0) {
1428 xx += sx;
1429 sx = x0 < x2? 1 : -1;
1430 xx *= sx;
1431 yy += sy;
1432 sy = y0 < y2? 1 : -1;
1433 yy *= sy;
1434 xy = (xx*yy) << 1;
1435 xx *= xx;
1436 yy *= yy;
1437 if (curvature * sx * sy < 0) {
1438 xx = -xx;
1439 yy = -yy;
1440 xy = -xy;
1441 curvature = -curvature;
1442 }
1443 dx = 4 * sy * curvature * (x1 - x0) + xx - xy;
1444 dy = 4 * sx * curvature * (y0 - y1) + yy - xy;
1445 xx += xx;
1446 yy += yy;
1447 err = dx + dy + xy;
1448 do {
1449 for (i = 0; i <= width; i++)
1450 gfx_fb_setpixel(x0 + i, y0);
1451 if (x0 == x2 && y0 == y2)
1452 return; /* last pixel -> curve finished */
1453 y1 = 2 * err < dx;
1454 if (2 * err > dy) {
1455 x0 += sx;
1456 dx -= xy;
1457 dy += yy;
1458 err += dy;
1459 }
1460 if (y1 != 0) {
1461 y0 += sy;
1462 dy -= xy;
1463 dx += xx;
1464 err += dx;
1465 }
1466 } while (dy < dx); /* gradient negates -> algorithm fails */
1467 }
1468 gfx_fb_line(x0, y0, x2, y2, width);
1469 }
1470
1471 /*
1472 * draw rectangle using terminal coordinates and current foreground color.
1473 */
1474 void
gfx_term_drawrect(uint32_t ux1,uint32_t uy1,uint32_t ux2,uint32_t uy2)1475 gfx_term_drawrect(uint32_t ux1, uint32_t uy1, uint32_t ux2, uint32_t uy2)
1476 {
1477 int x1, y1, x2, y2;
1478 int xshift, yshift;
1479 int width, i;
1480 uint32_t vf_width, vf_height;
1481
1482 if (plat_stdout_is_framebuffer() == 0)
1483 return;
1484
1485 vf_width = tems.ts_font.vf_width;
1486 vf_height = tems.ts_font.vf_height;
1487 width = vf_width / 4; /* line width */
1488 xshift = (vf_width - width) / 2;
1489 yshift = (vf_height - width) / 2;
1490
1491 /* Shift coordinates */
1492 if (ux1 != 0)
1493 ux1--;
1494 if (uy1 != 0)
1495 uy1--;
1496 ux2--;
1497 uy2--;
1498
1499 /* mark area used in tem */
1500 tem_image_display(tems.ts_active, uy1, ux1, uy2 + 1, ux2 + 1);
1501
1502 /*
1503 * Draw horizontal lines width points thick, shifted from outer edge.
1504 */
1505 x1 = (ux1 + 1) * vf_width + tems.ts_p_offset.x;
1506 y1 = uy1 * vf_height + tems.ts_p_offset.y + yshift;
1507 x2 = ux2 * vf_width + tems.ts_p_offset.x;
1508 gfx_fb_drawrect(x1, y1, x2, y1 + width, 1);
1509 y2 = uy2 * vf_height + tems.ts_p_offset.y;
1510 y2 += vf_height - yshift - width;
1511 gfx_fb_drawrect(x1, y2, x2, y2 + width, 1);
1512
1513 /*
1514 * Draw vertical lines width points thick, shifted from outer edge.
1515 */
1516 x1 = ux1 * vf_width + tems.ts_p_offset.x + xshift;
1517 y1 = uy1 * vf_height + tems.ts_p_offset.y;
1518 y1 += vf_height;
1519 y2 = uy2 * vf_height + tems.ts_p_offset.y;
1520 gfx_fb_drawrect(x1, y1, x1 + width, y2, 1);
1521 x1 = ux2 * vf_width + tems.ts_p_offset.x;
1522 x1 += vf_width - xshift - width;
1523 gfx_fb_drawrect(x1, y1, x1 + width, y2, 1);
1524
1525 /* Draw upper left corner. */
1526 x1 = ux1 * vf_width + tems.ts_p_offset.x + xshift;
1527 y1 = uy1 * vf_height + tems.ts_p_offset.y;
1528 y1 += vf_height;
1529
1530 x2 = ux1 * vf_width + tems.ts_p_offset.x;
1531 x2 += vf_width;
1532 y2 = uy1 * vf_height + tems.ts_p_offset.y + yshift;
1533 for (i = 0; i <= width; i++)
1534 gfx_fb_bezier(x1 + i, y1, x1 + i, y2 + i, x2, y2 + i, width-i);
1535
1536 /* Draw lower left corner. */
1537 x1 = ux1 * vf_width + tems.ts_p_offset.x;
1538 x1 += vf_width;
1539 y1 = uy2 * vf_height + tems.ts_p_offset.y;
1540 y1 += vf_height - yshift;
1541 x2 = ux1 * vf_width + tems.ts_p_offset.x + xshift;
1542 y2 = uy2 * vf_height + tems.ts_p_offset.y;
1543 for (i = 0; i <= width; i++)
1544 gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i);
1545
1546 /* Draw upper right corner. */
1547 x1 = ux2 * vf_width + tems.ts_p_offset.x;
1548 y1 = uy1 * vf_height + tems.ts_p_offset.y + yshift;
1549 x2 = ux2 * vf_width + tems.ts_p_offset.x;
1550 x2 += vf_width - xshift - width;
1551 y2 = uy1 * vf_height + tems.ts_p_offset.y;
1552 y2 += vf_height;
1553 for (i = 0; i <= width; i++)
1554 gfx_fb_bezier(x1, y1 + i, x2 + i, y1 + i, x2 + i, y2, width-i);
1555
1556 /* Draw lower right corner. */
1557 x1 = ux2 * vf_width + tems.ts_p_offset.x;
1558 y1 = uy2 * vf_height + tems.ts_p_offset.y;
1559 y1 += vf_height - yshift;
1560 x2 = ux2 * vf_width + tems.ts_p_offset.x;
1561 x2 += vf_width - xshift - width;
1562 y2 = uy2 * vf_height + tems.ts_p_offset.y;
1563 for (i = 0; i <= width; i++)
1564 gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i);
1565 }
1566
1567 int
gfx_fb_putimage(png_t * png,uint32_t ux1,uint32_t uy1,uint32_t ux2,uint32_t uy2,uint32_t flags)1568 gfx_fb_putimage(png_t *png, uint32_t ux1, uint32_t uy1, uint32_t ux2,
1569 uint32_t uy2, uint32_t flags)
1570 {
1571 #if defined(EFI)
1572 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
1573 #else
1574 struct paletteentry *p;
1575 #endif
1576 struct vis_consdisplay da;
1577 uint32_t i, j, x, y, fheight, fwidth;
1578 uint8_t r, g, b, a;
1579 bool scale = false;
1580 bool trace = false;
1581
1582 trace = (flags & FL_PUTIMAGE_DEBUG) != 0;
1583
1584 if (plat_stdout_is_framebuffer() == 0) {
1585 if (trace)
1586 printf("Framebuffer not active.\n");
1587 return (1);
1588 }
1589
1590 if (png->color_type != PNG_TRUECOLOR_ALPHA) {
1591 if (trace)
1592 printf("Not truecolor image.\n");
1593 return (1);
1594 }
1595
1596 if (ux1 > gfx_fb.framebuffer_common.framebuffer_width ||
1597 uy1 > gfx_fb.framebuffer_common.framebuffer_height) {
1598 if (trace)
1599 printf("Top left coordinate off screen.\n");
1600 return (1);
1601 }
1602
1603 if (png->width > UINT16_MAX || png->height > UINT16_MAX) {
1604 if (trace)
1605 printf("Image too large.\n");
1606 return (1);
1607 }
1608
1609 if (png->width < 1 || png->height < 1) {
1610 if (trace)
1611 printf("Image too small.\n");
1612 return (1);
1613 }
1614
1615 /*
1616 * If 0 was passed for either ux2 or uy2, then calculate the missing
1617 * part of the bottom right coordinate.
1618 */
1619 scale = true;
1620 if (ux2 == 0 && uy2 == 0) {
1621 /* Both 0, use the native resolution of the image */
1622 ux2 = ux1 + png->width;
1623 uy2 = uy1 + png->height;
1624 scale = false;
1625 } else if (ux2 == 0) {
1626 /* Set ux2 from uy2/uy1 to maintain aspect ratio */
1627 ux2 = ux1 + (png->width * (uy2 - uy1)) / png->height;
1628 } else if (uy2 == 0) {
1629 /* Set uy2 from ux2/ux1 to maintain aspect ratio */
1630 uy2 = uy1 + (png->height * (ux2 - ux1)) / png->width;
1631 }
1632
1633 if (ux2 > gfx_fb.framebuffer_common.framebuffer_width ||
1634 uy2 > gfx_fb.framebuffer_common.framebuffer_height) {
1635 if (trace)
1636 printf("Bottom right coordinate off screen.\n");
1637 return (1);
1638 }
1639
1640 fwidth = ux2 - ux1;
1641 fheight = uy2 - uy1;
1642
1643 /*
1644 * If the original image dimensions have been passed explicitly,
1645 * disable scaling.
1646 */
1647 if (fwidth == png->width && fheight == png->height)
1648 scale = false;
1649
1650 if (ux1 == 0) {
1651 /*
1652 * No top left X co-ordinate (real coordinates start at 1),
1653 * place as far right as it will fit.
1654 */
1655 ux2 = gfx_fb.framebuffer_common.framebuffer_width -
1656 tems.ts_p_offset.x;
1657 ux1 = ux2 - fwidth;
1658 }
1659
1660 if (uy1 == 0) {
1661 /*
1662 * No top left Y co-ordinate (real coordinates start at 1),
1663 * place as far down as it will fit.
1664 */
1665 uy2 = gfx_fb.framebuffer_common.framebuffer_height -
1666 tems.ts_p_offset.y;
1667 uy1 = uy2 - fheight;
1668 }
1669
1670 if (ux1 >= ux2 || uy1 >= uy2) {
1671 if (trace)
1672 printf("Image dimensions reversed.\n");
1673 return (1);
1674 }
1675
1676 if (fwidth < 2 || fheight < 2) {
1677 if (trace)
1678 printf("Target area too small\n");
1679 return (1);
1680 }
1681
1682 if (trace)
1683 printf("Image %ux%u -> %ux%u @%ux%u\n",
1684 png->width, png->height, fwidth, fheight, ux1, uy1);
1685
1686 da.col = ux1;
1687 da.row = uy1;
1688 da.width = fwidth;
1689 da.height = fheight;
1690
1691 /*
1692 * mark area used in tem
1693 */
1694 if (!(flags & FL_PUTIMAGE_NOSCROLL)) {
1695 tem_image_display(tems.ts_active,
1696 da.row / tems.ts_font.vf_height,
1697 da.col / tems.ts_font.vf_width,
1698 (da.row + da.height) / tems.ts_font.vf_height,
1699 (da.col + da.width) / tems.ts_font.vf_width);
1700 }
1701
1702 if ((flags & FL_PUTIMAGE_BORDER))
1703 gfx_fb_drawrect(ux1, uy1, ux2, uy2, 0);
1704
1705 da.data = malloc(fwidth * fheight * sizeof (*p));
1706 p = (void *)da.data;
1707 if (da.data == NULL) {
1708 if (trace)
1709 printf("Out of memory.\n");
1710 return (1);
1711 }
1712
1713 /*
1714 * Build image for our framebuffer.
1715 */
1716
1717 /* Helper to calculate the pixel index from the source png */
1718 #define GETPIXEL(xx, yy) (((yy) * png->width + (xx)) * png->bpp)
1719
1720 /*
1721 * For each of the x and y directions, calculate the number of pixels
1722 * in the source image that correspond to a single pixel in the target.
1723 * Use fixed-point arithmetic with 16-bits for each of the integer and
1724 * fractional parts.
1725 */
1726 const uint32_t wcstep = ((png->width - 1) << 16) / (fwidth - 1);
1727 const uint32_t hcstep = ((png->height - 1) << 16) / (fheight - 1);
1728
1729 uint32_t hc = 0;
1730 for (y = 0; y < fheight; y++) {
1731 uint32_t hc2 = (hc >> 9) & 0x7f;
1732 uint32_t hc1 = 0x80 - hc2;
1733
1734 uint32_t offset_y = hc >> 16;
1735 uint32_t offset_y1 = offset_y + 1;
1736
1737 uint32_t wc = 0;
1738 for (x = 0; x < fwidth; x++) {
1739 uint32_t wc2 = (wc >> 9) & 0x7f;
1740 uint32_t wc1 = 0x80 - wc2;
1741
1742 uint32_t offset_x = wc >> 16;
1743 uint32_t offset_x1 = offset_x + 1;
1744
1745 /* Target pixel index */
1746 j = y * fwidth + x;
1747
1748 if (!scale) {
1749 i = GETPIXEL(x, y);
1750 r = png->image[i];
1751 g = png->image[i + 1];
1752 b = png->image[i + 2];
1753 a = png->image[i + 3];
1754 } else {
1755 uint8_t pixel[4];
1756
1757 uint32_t p00 = GETPIXEL(offset_x, offset_y);
1758 uint32_t p01 = GETPIXEL(offset_x, offset_y1);
1759 uint32_t p10 = GETPIXEL(offset_x1, offset_y);
1760 uint32_t p11 = GETPIXEL(offset_x1, offset_y1);
1761
1762 /*
1763 * Given a 2x2 array of pixels in the source
1764 * image, combine them to produce a single
1765 * value for the pixel in the target image.
1766 * Each column of pixels is combined using
1767 * a weighted average where the top and bottom
1768 * pixels contribute hc1 and hc2 respectively.
1769 * The calculation for bottom pixel pB and
1770 * top pixel pT is:
1771 * (pT * hc1 + pB * hc2) / (hc1 + hc2)
1772 * Once the values are determined for the two
1773 * columns of pixels, then the columns are
1774 * averaged together in the same way but using
1775 * wc1 and wc2 for the weightings.
1776 *
1777 * Since hc1 and hc2 are chosen so that
1778 * hc1 + hc2 == 128 (and same for wc1 + wc2),
1779 * the >> 14 below is a quick way to divide by
1780 * (hc1 + hc2) * (wc1 + wc2)
1781 */
1782 for (i = 0; i < 4; i++)
1783 pixel[i] = (
1784 (png->image[p00 + i] * hc1 +
1785 png->image[p01 + i] * hc2) * wc1 +
1786 (png->image[p10 + i] * hc1 +
1787 png->image[p11 + i] * hc2) * wc2)
1788 >> 14;
1789
1790 r = pixel[0];
1791 g = pixel[1];
1792 b = pixel[2];
1793 a = pixel[3];
1794 }
1795
1796 if (trace)
1797 printf("r/g/b: %x/%x/%x\n", r, g, b);
1798 /*
1799 * Rough colorspace reduction for 15/16 bit colors.
1800 */
1801 p[j].Red = r >>
1802 (8 - gfx_fb.u.fb2.framebuffer_red_mask_size);
1803 p[j].Green = g >>
1804 (8 - gfx_fb.u.fb2.framebuffer_green_mask_size);
1805 p[j].Blue = b >>
1806 (8 - gfx_fb.u.fb2.framebuffer_blue_mask_size);
1807 p[j].Reserved = a;
1808
1809 wc += wcstep;
1810 }
1811 hc += hcstep;
1812 }
1813
1814 gfx_fb_cons_display(&da);
1815 free(da.data);
1816 return (0);
1817 }
1818
1819 /* Return w^2 + h^2 or 0, if the dimensions are unknown */
1820 static unsigned
edid_diagonal_squared(void)1821 edid_diagonal_squared(void)
1822 {
1823 unsigned w, h;
1824
1825 if (edid_info == NULL)
1826 return (0);
1827
1828 w = edid_info->display.max_horizontal_image_size;
1829 h = edid_info->display.max_vertical_image_size;
1830
1831 /* If either one is 0, we have aspect ratio, not size */
1832 if (w == 0 || h == 0)
1833 return (0);
1834
1835 /*
1836 * some monitors encode the aspect ratio instead of the physical size.
1837 */
1838 if ((w == 16 && h == 9) || (w == 16 && h == 10) ||
1839 (w == 4 && h == 3) || (w == 5 && h == 4))
1840 return (0);
1841
1842 /*
1843 * translate cm to inch, note we scale by 100 here.
1844 */
1845 w = w * 100 / 254;
1846 h = h * 100 / 254;
1847
1848 /* Return w^2 + h^2 */
1849 return (w * w + h * h);
1850 }
1851
1852 /*
1853 * calculate pixels per inch.
1854 */
1855 static unsigned
gfx_get_ppi(void)1856 gfx_get_ppi(void)
1857 {
1858 unsigned dp, di;
1859
1860 di = edid_diagonal_squared();
1861 if (di == 0)
1862 return (0);
1863
1864 dp = gfx_fb.framebuffer_common.framebuffer_width *
1865 gfx_fb.framebuffer_common.framebuffer_width +
1866 gfx_fb.framebuffer_common.framebuffer_height *
1867 gfx_fb.framebuffer_common.framebuffer_height;
1868
1869 return (isqrt(dp / di));
1870 }
1871
1872 /*
1873 * Calculate font size from density independent pixels (dp):
1874 * ((16dp * ppi) / 160) * display_factor.
1875 * Here we are using fixed constants: 1dp == 160 ppi and
1876 * display_factor 2.
1877 *
1878 * We are rounding font size up and are searching for font which is
1879 * not smaller than calculated size value.
1880 */
1881 bitmap_data_t *
gfx_get_font(void)1882 gfx_get_font(void)
1883 {
1884 unsigned ppi, size;
1885 bitmap_data_t *font = NULL;
1886 struct fontlist *fl, *next;
1887
1888 /* Text mode is not supported here. */
1889 if (gfx_fb.framebuffer_common.framebuffer_type ==
1890 MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT)
1891 return (NULL);
1892
1893 ppi = gfx_get_ppi();
1894 if (ppi == 0)
1895 return (NULL);
1896
1897 /*
1898 * We will search for 16dp font.
1899 * We are using scale up by 10 for roundup.
1900 */
1901 size = (16 * ppi * 10) / 160;
1902 /* Apply display factor 2. */
1903 size = roundup(size * 2, 10) / 10;
1904
1905 STAILQ_FOREACH(fl, &fonts, font_next) {
1906 next = STAILQ_NEXT(fl, font_next);
1907 /*
1908 * If this is last font or, if next font is smaller,
1909 * we have our font. Make sure, it actually is loaded.
1910 */
1911 if (next == NULL || next->font_data->height < size) {
1912 font = fl->font_data;
1913 if (font->font == NULL ||
1914 fl->font_flags == FONT_RELOAD) {
1915 if (fl->font_load != NULL &&
1916 fl->font_name != NULL)
1917 font = fl->font_load(fl->font_name);
1918 }
1919 break;
1920 }
1921 }
1922
1923 return (font);
1924 }
1925
1926 static int
load_mapping(int fd,struct font * fp,int n)1927 load_mapping(int fd, struct font *fp, int n)
1928 {
1929 size_t i, size;
1930 ssize_t rv;
1931 struct font_map *mp;
1932
1933 if (fp->vf_map_count[n] == 0)
1934 return (0);
1935
1936 size = fp->vf_map_count[n] * sizeof (*mp);
1937 mp = malloc(size);
1938 if (mp == NULL)
1939 return (ENOMEM);
1940 fp->vf_map[n] = mp;
1941
1942 rv = read(fd, mp, size);
1943 if (rv < 0 || (size_t)rv != size) {
1944 free(fp->vf_map[n]);
1945 fp->vf_map[n] = NULL;
1946 return (EIO);
1947 }
1948
1949 for (i = 0; i < fp->vf_map_count[n]; i++) {
1950 mp[i].font_src = be32toh(mp[i].font_src);
1951 mp[i].font_dst = be16toh(mp[i].font_dst);
1952 mp[i].font_len = be16toh(mp[i].font_len);
1953 }
1954 return (0);
1955 }
1956
1957 static int
builtin_mapping(struct font * fp,int n)1958 builtin_mapping(struct font *fp, int n)
1959 {
1960 size_t size;
1961 struct font_map *mp;
1962
1963 if (n >= VFNT_MAPS)
1964 return (EINVAL);
1965
1966 if (fp->vf_map_count[n] == 0)
1967 return (0);
1968
1969 size = fp->vf_map_count[n] * sizeof (*mp);
1970 mp = malloc(size);
1971 if (mp == NULL)
1972 return (ENOMEM);
1973 fp->vf_map[n] = mp;
1974
1975 memcpy(mp, DEFAULT_FONT_DATA.font->vf_map[n], size);
1976 return (0);
1977 }
1978
1979 /*
1980 * Load font from builtin or from file.
1981 * We do need special case for builtin because the builtin font glyphs
1982 * are compressed and we do need to uncompress them.
1983 * Having single load_font() for both cases will help us to simplify
1984 * font switch handling.
1985 */
1986 static bitmap_data_t *
load_font(char * path)1987 load_font(char *path)
1988 {
1989 int fd, i;
1990 uint32_t glyphs;
1991 struct font_header fh;
1992 struct fontlist *fl;
1993 bitmap_data_t *bp;
1994 struct font *fp;
1995 size_t size;
1996 ssize_t rv;
1997
1998 /* Get our entry from the font list. */
1999 STAILQ_FOREACH(fl, &fonts, font_next) {
2000 if (strcmp(fl->font_name, path) == 0)
2001 break;
2002 }
2003 if (fl == NULL)
2004 return (NULL); /* Should not happen. */
2005
2006 bp = fl->font_data;
2007 if (bp->font != NULL && fl->font_flags != FONT_RELOAD)
2008 return (bp);
2009
2010 fd = -1;
2011 /*
2012 * Special case for builtin font.
2013 * Builtin font is the very first font we load, we do not have
2014 * previous loads to be released.
2015 */
2016 if (fl->font_flags == FONT_BUILTIN) {
2017 if ((fp = calloc(1, sizeof (struct font))) == NULL)
2018 return (NULL);
2019
2020 fp->vf_width = DEFAULT_FONT_DATA.width;
2021 fp->vf_height = DEFAULT_FONT_DATA.height;
2022
2023 fp->vf_bytes = malloc(DEFAULT_FONT_DATA.uncompressed_size);
2024 if (fp->vf_bytes == NULL) {
2025 free(fp);
2026 return (NULL);
2027 }
2028
2029 bp->uncompressed_size = DEFAULT_FONT_DATA.uncompressed_size;
2030 bp->compressed_size = DEFAULT_FONT_DATA.compressed_size;
2031
2032 if (lz4_decompress(DEFAULT_FONT_DATA.compressed_data,
2033 fp->vf_bytes,
2034 DEFAULT_FONT_DATA.compressed_size,
2035 DEFAULT_FONT_DATA.uncompressed_size, 0) != 0) {
2036 free(fp->vf_bytes);
2037 free(fp);
2038 return (NULL);
2039 }
2040
2041 for (i = 0; i < VFNT_MAPS; i++) {
2042 fp->vf_map_count[i] =
2043 DEFAULT_FONT_DATA.font->vf_map_count[i];
2044 if (builtin_mapping(fp, i) != 0)
2045 goto free_done;
2046 }
2047
2048 bp->font = fp;
2049 return (bp);
2050 }
2051
2052 fd = open(path, O_RDONLY);
2053 if (fd < 0) {
2054 return (NULL);
2055 }
2056
2057 size = sizeof (fh);
2058 rv = read(fd, &fh, size);
2059 if (rv < 0 || (size_t)rv != size) {
2060 bp = NULL;
2061 goto done;
2062 }
2063 if (memcmp(fh.fh_magic, FONT_HEADER_MAGIC, sizeof (fh.fh_magic)) != 0) {
2064 bp = NULL;
2065 goto done;
2066 }
2067 if ((fp = calloc(1, sizeof (struct font))) == NULL) {
2068 bp = NULL;
2069 goto done;
2070 }
2071 for (i = 0; i < VFNT_MAPS; i++)
2072 fp->vf_map_count[i] = be32toh(fh.fh_map_count[i]);
2073
2074 glyphs = be32toh(fh.fh_glyph_count);
2075 fp->vf_width = fh.fh_width;
2076 fp->vf_height = fh.fh_height;
2077
2078 size = howmany(fp->vf_width, 8) * fp->vf_height * glyphs;
2079 bp->uncompressed_size = size;
2080 if ((fp->vf_bytes = malloc(size)) == NULL)
2081 goto free_done;
2082
2083 rv = read(fd, fp->vf_bytes, size);
2084 if (rv < 0 || (size_t)rv != size)
2085 goto free_done;
2086 for (i = 0; i < VFNT_MAPS; i++) {
2087 if (load_mapping(fd, fp, i) != 0)
2088 goto free_done;
2089 }
2090
2091 /*
2092 * Reset builtin flag now as we have full font loaded.
2093 */
2094 if (fl->font_flags == FONT_BUILTIN)
2095 fl->font_flags = FONT_AUTO;
2096
2097 /*
2098 * Release previously loaded entries. We can do this now, as
2099 * the new font is loaded. Note, there can be no console
2100 * output till the new font is in place and tem is notified.
2101 * We do need to keep fl->font_data for glyph dimensions.
2102 */
2103 STAILQ_FOREACH(fl, &fonts, font_next) {
2104 if (fl->font_data->font == NULL)
2105 continue;
2106
2107 for (i = 0; i < VFNT_MAPS; i++)
2108 free(fl->font_data->font->vf_map[i]);
2109 free(fl->font_data->font->vf_bytes);
2110 free(fl->font_data->font);
2111 fl->font_data->font = NULL;
2112 }
2113
2114 bp->font = fp;
2115 bp->compressed_size = 0;
2116
2117 done:
2118 if (fd != -1)
2119 close(fd);
2120 return (bp);
2121
2122 free_done:
2123 for (i = 0; i < VFNT_MAPS; i++)
2124 free(fp->vf_map[i]);
2125 free(fp->vf_bytes);
2126 free(fp);
2127 bp = NULL;
2128 goto done;
2129 }
2130
2131
2132 struct name_entry {
2133 char *n_name;
2134 SLIST_ENTRY(name_entry) n_entry;
2135 };
2136
2137 SLIST_HEAD(name_list, name_entry);
2138
2139 /* Read font names from index file. */
2140 static struct name_list *
read_list(char * fonts)2141 read_list(char *fonts)
2142 {
2143 struct name_list *nl;
2144 struct name_entry *np;
2145 char buf[PATH_MAX];
2146 int fd, len;
2147
2148 fd = open(fonts, O_RDONLY);
2149 if (fd < 0)
2150 return (NULL);
2151
2152 nl = malloc(sizeof (*nl));
2153 if (nl == NULL) {
2154 close(fd);
2155 return (nl);
2156 }
2157
2158 SLIST_INIT(nl);
2159 while ((len = fgetstr(buf, sizeof (buf), fd)) > 0) {
2160 np = malloc(sizeof (*np));
2161 if (np == NULL) {
2162 close(fd);
2163 return (nl); /* return what we have */
2164 }
2165 np->n_name = strdup(buf);
2166 if (np->n_name == NULL) {
2167 free(np);
2168 close(fd);
2169 return (nl); /* return what we have */
2170 }
2171 SLIST_INSERT_HEAD(nl, np, n_entry);
2172 }
2173 close(fd);
2174 return (nl);
2175 }
2176
2177 /*
2178 * Read the font properties and insert new entry into the list.
2179 * The font list is built in descending order.
2180 */
2181 static bool
insert_font(char * name,FONT_FLAGS flags)2182 insert_font(char *name, FONT_FLAGS flags)
2183 {
2184 struct font_header fh;
2185 struct fontlist *fp, *previous, *entry, *next;
2186 size_t size;
2187 ssize_t rv;
2188 int fd;
2189 char *font_name;
2190
2191 font_name = NULL;
2192 if (flags == FONT_BUILTIN) {
2193 /*
2194 * We only install builtin font once, while setting up
2195 * initial console. Since this will happen very early,
2196 * we assume asprintf will not fail. Once we have access to
2197 * files, the builtin font will be replaced by font loaded
2198 * from file.
2199 */
2200 if (!STAILQ_EMPTY(&fonts))
2201 return (false);
2202
2203 fh.fh_width = DEFAULT_FONT_DATA.width;
2204 fh.fh_height = DEFAULT_FONT_DATA.height;
2205
2206 (void) asprintf(&font_name, "%dx%d",
2207 DEFAULT_FONT_DATA.width, DEFAULT_FONT_DATA.height);
2208 } else {
2209 fd = open(name, O_RDONLY);
2210 if (fd < 0)
2211 return (false);
2212 rv = read(fd, &fh, sizeof (fh));
2213 close(fd);
2214 if (rv < 0 || (size_t)rv != sizeof (fh))
2215 return (false);
2216
2217 if (memcmp(fh.fh_magic, FONT_HEADER_MAGIC,
2218 sizeof (fh.fh_magic)) != 0)
2219 return (false);
2220 font_name = strdup(name);
2221 }
2222
2223 if (font_name == NULL)
2224 return (false);
2225
2226 /*
2227 * If we have an entry with the same glyph dimensions, replace
2228 * the file name and mark us. We only support unique dimensions.
2229 */
2230 STAILQ_FOREACH(entry, &fonts, font_next) {
2231 if (fh.fh_width == entry->font_data->width &&
2232 fh.fh_height == entry->font_data->height) {
2233 free(entry->font_name);
2234 entry->font_name = font_name;
2235 entry->font_flags = FONT_RELOAD;
2236 return (true);
2237 }
2238 }
2239
2240 fp = calloc(sizeof (*fp), 1);
2241 if (fp == NULL) {
2242 free(font_name);
2243 return (false);
2244 }
2245 fp->font_data = calloc(sizeof (*fp->font_data), 1);
2246 if (fp->font_data == NULL) {
2247 free(font_name);
2248 free(fp);
2249 return (false);
2250 }
2251 fp->font_name = font_name;
2252 fp->font_flags = flags;
2253 fp->font_load = load_font;
2254 fp->font_data->width = fh.fh_width;
2255 fp->font_data->height = fh.fh_height;
2256
2257 if (STAILQ_EMPTY(&fonts)) {
2258 STAILQ_INSERT_HEAD(&fonts, fp, font_next);
2259 return (true);
2260 }
2261
2262 previous = NULL;
2263 size = fp->font_data->width * fp->font_data->height;
2264
2265 STAILQ_FOREACH(entry, &fonts, font_next) {
2266 /* Should fp be inserted before the entry? */
2267 if (size > entry->font_data->width * entry->font_data->height) {
2268 if (previous == NULL) {
2269 STAILQ_INSERT_HEAD(&fonts, fp, font_next);
2270 } else {
2271 STAILQ_INSERT_AFTER(&fonts, previous, fp,
2272 font_next);
2273 }
2274 return (true);
2275 }
2276 next = STAILQ_NEXT(entry, font_next);
2277 if (next == NULL ||
2278 size > next->font_data->width * next->font_data->height) {
2279 STAILQ_INSERT_AFTER(&fonts, entry, fp, font_next);
2280 return (true);
2281 }
2282 previous = entry;
2283 }
2284 return (true);
2285 }
2286
2287 static int
font_set(struct env_var * ev __unused,int flags __unused,const void * value)2288 font_set(struct env_var *ev __unused, int flags __unused, const void *value)
2289 {
2290 struct fontlist *fl;
2291 char *eptr;
2292 unsigned long x = 0, y = 0;
2293
2294 /*
2295 * Attempt to extract values from "XxY" string. In case of error,
2296 * we have unmaching glyph dimensions and will just output the
2297 * available values.
2298 */
2299 if (value != NULL) {
2300 x = strtoul(value, &eptr, 10);
2301 if (*eptr == 'x')
2302 y = strtoul(eptr + 1, &eptr, 10);
2303 }
2304 STAILQ_FOREACH(fl, &fonts, font_next) {
2305 if (fl->font_data->width == x && fl->font_data->height == y)
2306 break;
2307 }
2308 if (fl != NULL) {
2309 /* Reset any FONT_MANUAL flag. */
2310 reset_font_flags();
2311
2312 /* Mark this font manually loaded */
2313 fl->font_flags = FONT_MANUAL;
2314 /* Trigger tem update. */
2315 tems.update_font = true;
2316 plat_cons_update_mode(-1);
2317 return (CMD_OK);
2318 }
2319
2320 printf("Available fonts:\n");
2321 STAILQ_FOREACH(fl, &fonts, font_next) {
2322 printf(" %dx%d\n", fl->font_data->width,
2323 fl->font_data->height);
2324 }
2325 return (CMD_OK);
2326 }
2327
2328 void
bios_text_font(bool use_vga_font)2329 bios_text_font(bool use_vga_font)
2330 {
2331 if (use_vga_font)
2332 (void) insert_font(VGA_8X16_FONT, FONT_MANUAL);
2333 else
2334 (void) insert_font(DEFAULT_8X16_FONT, FONT_MANUAL);
2335 tems.update_font = true;
2336 }
2337
2338 void
autoload_font(bool bios)2339 autoload_font(bool bios)
2340 {
2341 struct name_list *nl;
2342 struct name_entry *np;
2343
2344 nl = read_list("/boot/fonts/fonts.dir");
2345 if (nl == NULL)
2346 return;
2347
2348 while (!SLIST_EMPTY(nl)) {
2349 np = SLIST_FIRST(nl);
2350 SLIST_REMOVE_HEAD(nl, n_entry);
2351 if (insert_font(np->n_name, FONT_AUTO) == false)
2352 printf("failed to add font: %s\n", np->n_name);
2353 free(np->n_name);
2354 free(np);
2355 }
2356
2357 unsetenv("screen-font");
2358 env_setenv("screen-font", EV_VOLATILE, NULL, font_set, env_nounset);
2359
2360 /*
2361 * If vga text mode was requested, load vga.font (8x16 bold) font.
2362 */
2363 if (bios) {
2364 bios_text_font(true);
2365 }
2366
2367 /* Trigger tem update. */
2368 tems.update_font = true;
2369 plat_cons_update_mode(-1);
2370 }
2371
2372 COMMAND_SET(load_font, "loadfont", "load console font from file", command_font);
2373
2374 static int
command_font(int argc,char * argv[])2375 command_font(int argc, char *argv[])
2376 {
2377 int i, c, rc = CMD_OK;
2378 struct fontlist *fl;
2379 bool list;
2380
2381 list = false;
2382 optind = 1;
2383 optreset = 1;
2384 rc = CMD_OK;
2385
2386 while ((c = getopt(argc, argv, "l")) != -1) {
2387 switch (c) {
2388 case 'l':
2389 list = true;
2390 break;
2391 case '?':
2392 default:
2393 return (CMD_ERROR);
2394 }
2395 }
2396
2397 argc -= optind;
2398 argv += optind;
2399
2400 if (argc > 1 || (list && argc != 0)) {
2401 printf("Usage: loadfont [-l] | [file.fnt]\n");
2402 return (CMD_ERROR);
2403 }
2404
2405 if (list) {
2406 STAILQ_FOREACH(fl, &fonts, font_next) {
2407 printf("font %s: %dx%d%s\n", fl->font_name,
2408 fl->font_data->width,
2409 fl->font_data->height,
2410 fl->font_data->font == NULL? "" : " loaded");
2411 }
2412 return (CMD_OK);
2413 }
2414
2415 if (argc == 1) {
2416 char *name = argv[0];
2417
2418 if (insert_font(name, FONT_MANUAL) == false) {
2419 printf("loadfont error: failed to load: %s\n", name);
2420 return (CMD_ERROR);
2421 }
2422
2423 tems.update_font = true;
2424 plat_cons_update_mode(-1);
2425 return (CMD_OK);
2426 }
2427
2428 if (argc == 0) {
2429 /*
2430 * Walk entire font list, release any loaded font, and set
2431 * autoload flag. The font list does have at least the builtin
2432 * default font.
2433 */
2434 STAILQ_FOREACH(fl, &fonts, font_next) {
2435 if (fl->font_data->font != NULL) {
2436 /* Note the tem is releasing font bytes */
2437 for (i = 0; i < VFNT_MAPS; i++)
2438 free(fl->font_data->font->vf_map[i]);
2439 free(fl->font_data->font);
2440 fl->font_data->font = NULL;
2441 fl->font_data->uncompressed_size = 0;
2442 fl->font_flags = FONT_AUTO;
2443 }
2444 }
2445 tems.update_font = true;
2446 plat_cons_update_mode(-1);
2447 }
2448 return (rc);
2449 }
2450
2451 bool
gfx_get_edid_resolution(struct vesa_edid_info * edid,edid_res_list_t * res)2452 gfx_get_edid_resolution(struct vesa_edid_info *edid, edid_res_list_t *res)
2453 {
2454 struct resolution *rp, *p;
2455
2456 /*
2457 * Walk detailed timings tables (4).
2458 */
2459 if ((edid->display.supported_features
2460 & EDID_FEATURE_PREFERRED_TIMING_MODE) != 0) {
2461 /* Walk detailed timing descriptors (4) */
2462 for (int i = 0; i < DET_TIMINGS; i++) {
2463 /*
2464 * Reserved value 0 is not used for display decriptor.
2465 */
2466 if (edid->detailed_timings[i].pixel_clock == 0)
2467 continue;
2468 if ((rp = malloc(sizeof (*rp))) == NULL)
2469 continue;
2470 rp->width = GET_EDID_INFO_WIDTH(edid, i);
2471 rp->height = GET_EDID_INFO_HEIGHT(edid, i);
2472 if (rp->width > 0 && rp->width <= EDID_MAX_PIXELS &&
2473 rp->height > 0 && rp->height <= EDID_MAX_LINES)
2474 TAILQ_INSERT_TAIL(res, rp, next);
2475 else
2476 free(rp);
2477 }
2478 }
2479
2480 /*
2481 * Walk standard timings list (8).
2482 */
2483 for (int i = 0; i < STD_TIMINGS; i++) {
2484 /* Is this field unused? */
2485 if (edid->standard_timings[i] == 0x0101)
2486 continue;
2487
2488 if ((rp = malloc(sizeof (*rp))) == NULL)
2489 continue;
2490
2491 rp->width = HSIZE(edid->standard_timings[i]);
2492 switch (RATIO(edid->standard_timings[i])) {
2493 case RATIO1_1:
2494 rp->height = HSIZE(edid->standard_timings[i]);
2495 if (edid->header.version > 1 ||
2496 edid->header.revision > 2) {
2497 rp->height = rp->height * 10 / 16;
2498 }
2499 break;
2500 case RATIO4_3:
2501 rp->height = HSIZE(edid->standard_timings[i]) * 3 / 4;
2502 break;
2503 case RATIO5_4:
2504 rp->height = HSIZE(edid->standard_timings[i]) * 4 / 5;
2505 break;
2506 case RATIO16_9:
2507 rp->height = HSIZE(edid->standard_timings[i]) * 9 / 16;
2508 break;
2509 }
2510
2511 /*
2512 * Create resolution list in decreasing order, except keep
2513 * first entry (preferred timing mode).
2514 */
2515 TAILQ_FOREACH(p, res, next) {
2516 if (p->width * p->height < rp->width * rp->height) {
2517 /* Keep preferred mode first */
2518 if (TAILQ_FIRST(res) == p)
2519 TAILQ_INSERT_AFTER(res, p, rp, next);
2520 else
2521 TAILQ_INSERT_BEFORE(p, rp, next);
2522 break;
2523 }
2524 if (TAILQ_NEXT(p, next) == NULL) {
2525 TAILQ_INSERT_TAIL(res, rp, next);
2526 break;
2527 }
2528 }
2529 }
2530 return (!TAILQ_EMPTY(res));
2531 }
2532