xref: /freebsd/stand/common/gfx_fb.c (revision e0c4386e7e71d93b0edc0c8fa156263fc4a8b0b6)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright 2020 Toomas Soome
5  * Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
6  * Copyright 2020 RackTop Systems, Inc.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 /*
31  * The workhorse here is gfxfb_blt(). It is implemented to mimic UEFI
32  * GOP Blt, and allows us to fill the rectangle on screen, copy
33  * rectangle from video to buffer and buffer to video and video to video.
34  * Such implementation does allow us to have almost identical implementation
35  * for both BIOS VBE and UEFI.
36  *
37  * ALL pixel data is assumed to be 32-bit BGRA (byte order Blue, Green, Red,
38  * Alpha) format, this allows us to only handle RGB data and not to worry
39  * about mixing RGB with indexed colors.
40  * Data exchange between memory buffer and video will translate BGRA
41  * and native format as following:
42  *
43  * 32-bit to/from 32-bit is trivial case.
44  * 32-bit to/from 24-bit is also simple - we just drop the alpha channel.
45  * 32-bit to/from 16-bit is more complicated, because we nee to handle
46  * data loss from 32-bit to 16-bit. While reading/writing from/to video, we
47  * need to apply masks of 16-bit color components. This will preserve
48  * colors for terminal text. For 32-bit truecolor PMG images, we need to
49  * translate 32-bit colors to 15/16 bit colors and this means data loss.
50  * There are different algorithms how to perform such color space reduction,
51  * we are currently using bitwise right shift to reduce color space and so far
52  * this technique seems to be sufficient (see also gfx_fb_putimage(), the
53  * end of for loop).
54  * 32-bit to/from 8-bit is the most troublesome because 8-bit colors are
55  * indexed. From video, we do get color indexes, and we do translate
56  * color index values to RGB. To write to video, we again need to translate
57  * RGB to color index. Additionally, we need to translate between VGA and
58  * console colors.
59  *
60  * Our internal color data is represented using BGRA format. But the hardware
61  * used indexed colors for 8-bit colors (0-255) and for this mode we do
62  * need to perform translation to/from BGRA and index values.
63  *
64  *                   - paletteentry RGB <-> index -
65  * BGRA BUFFER <----/                              \ - VIDEO
66  *                  \                              /
67  *                   -  RGB (16/24/32)            -
68  *
69  * To perform index to RGB translation, we use palette table generated
70  * from when we set up 8-bit mode video. We cannot read palette data from
71  * the hardware, because not all hardware supports reading it.
72  *
73  * BGRA to index is implemented in rgb_to_color_index() by searching
74  * palette array for closest match of RBG values.
75  *
76  * Note: In 8-bit mode, We do store first 16 colors to palette registers
77  * in VGA color order, this serves two purposes; firstly,
78  * if palette update is not supported, we still have correct 16 colors.
79  * Secondly, the kernel does get correct 16 colors when some other boot
80  * loader is used. However, the palette map for 8-bit colors is using
81  * console color ordering - this does allow us to skip translation
82  * from VGA colors to console colors, while we are reading RGB data.
83  */
84 
85 #include <sys/param.h>
86 #include <stand.h>
87 #include <teken.h>
88 #include <gfx_fb.h>
89 #include <sys/font.h>
90 #include <sys/linker.h>
91 #include <sys/module.h>
92 #include <sys/stdint.h>
93 #include <sys/endian.h>
94 #include <pnglite.h>
95 #include <bootstrap.h>
96 #include <lz4.h>
97 #if defined(EFI)
98 #include <efi.h>
99 #include <efilib.h>
100 #else
101 #include <vbe.h>
102 #endif
103 
104 /* VGA text mode does use bold font. */
105 #if !defined(VGA_8X16_FONT)
106 #define	VGA_8X16_FONT		"/boot/fonts/8x16b.fnt"
107 #endif
108 #if !defined(DEFAULT_8X16_FONT)
109 #define	DEFAULT_8X16_FONT	"/boot/fonts/8x16.fnt"
110 #endif
111 
112 /*
113  * Must be sorted by font size in descending order
114  */
115 font_list_t fonts = STAILQ_HEAD_INITIALIZER(fonts);
116 
117 #define	DEFAULT_FONT_DATA	font_data_8x16
118 extern vt_font_bitmap_data_t	font_data_8x16;
119 teken_gfx_t gfx_state = { 0 };
120 
121 static struct {
122 	unsigned char r;	/* Red percentage value. */
123 	unsigned char g;	/* Green percentage value. */
124 	unsigned char b;	/* Blue percentage value. */
125 } color_def[NCOLORS] = {
126 	{0,	0,	0},	/* black */
127 	{50,	0,	0},	/* dark red */
128 	{0,	50,	0},	/* dark green */
129 	{77,	63,	0},	/* dark yellow */
130 	{20,	40,	64},	/* dark blue */
131 	{50,	0,	50},	/* dark magenta */
132 	{0,	50,	50},	/* dark cyan */
133 	{75,	75,	75},	/* light gray */
134 
135 	{18,	20,	21},	/* dark gray */
136 	{100,	0,	0},	/* light red */
137 	{0,	100,	0},	/* light green */
138 	{100,	100,	0},	/* light yellow */
139 	{45,	62,	81},	/* light blue */
140 	{100,	0,	100},	/* light magenta */
141 	{0,	100,	100},	/* light cyan */
142 	{100,	100,	100},	/* white */
143 };
144 uint32_t cmap[NCMAP];
145 
146 /*
147  * Between console's palette and VGA's one:
148  *  - blue and red are swapped (1 <-> 4)
149  *  - yellow and cyan are swapped (3 <-> 6)
150  */
151 const int cons_to_vga_colors[NCOLORS] = {
152 	0,  4,  2,  6,  1,  5,  3,  7,
153 	8, 12, 10, 14,  9, 13, 11, 15
154 };
155 
156 static const int vga_to_cons_colors[NCOLORS] = {
157 	0,  1,  2,  3,  4,  5,  6,  7,
158 	8,  9, 10, 11,  12, 13, 14, 15
159 };
160 
161 struct text_pixel *screen_buffer;
162 #if defined(EFI)
163 static EFI_GRAPHICS_OUTPUT_BLT_PIXEL *GlyphBuffer;
164 #else
165 static struct paletteentry *GlyphBuffer;
166 #endif
167 static size_t GlyphBufferSize;
168 
169 static bool insert_font(char *, FONT_FLAGS);
170 static int font_set(struct env_var *, int, const void *);
171 static void * allocate_glyphbuffer(uint32_t, uint32_t);
172 static void gfx_fb_cursor_draw(teken_gfx_t *, const teken_pos_t *, bool);
173 
174 /*
175  * Initialize gfx framework.
176  */
177 void
178 gfx_framework_init(void)
179 {
180 	/*
181 	 * Setup font list to have builtin font.
182 	 */
183 	(void) insert_font(NULL, FONT_BUILTIN);
184 }
185 
186 static uint8_t *
187 gfx_get_fb_address(void)
188 {
189 	return (ptov((uint32_t)gfx_state.tg_fb.fb_addr));
190 }
191 
192 /*
193  * Utility function to parse gfx mode line strings.
194  */
195 bool
196 gfx_parse_mode_str(char *str, int *x, int *y, int *depth)
197 {
198 	char *p, *end;
199 
200 	errno = 0;
201 	p = str;
202 	*x = strtoul(p, &end, 0);
203 	if (*x == 0 || errno != 0)
204 		return (false);
205 	if (*end != 'x')
206 		return (false);
207 	p = end + 1;
208 	*y = strtoul(p, &end, 0);
209 	if (*y == 0 || errno != 0)
210 		return (false);
211 	if (*end != 'x') {
212 		*depth = -1;    /* auto select */
213 	} else {
214 		p = end + 1;
215 		*depth = strtoul(p, &end, 0);
216 		if (*depth == 0 || errno != 0 || *end != '\0')
217 			return (false);
218 	}
219 
220 	return (true);
221 }
222 
223 static uint32_t
224 rgb_color_map(uint8_t index, uint32_t rmax, int roffset,
225     uint32_t gmax, int goffset, uint32_t bmax, int boffset)
226 {
227 	uint32_t color, code, gray, level;
228 
229 	if (index < NCOLORS) {
230 #define	CF(_f, _i) ((_f ## max * color_def[(_i)]._f / 100) << _f ## offset)
231 		return (CF(r, index) | CF(g, index) | CF(b, index));
232 #undef  CF
233         }
234 
235 #define	CF(_f, _c) ((_f ## max & _c) << _f ## offset)
236         /* 6x6x6 color cube */
237         if (index > 15 && index < 232) {
238                 uint32_t red, green, blue;
239 
240                 for (red = 0; red < 6; red++) {
241                         for (green = 0; green < 6; green++) {
242                                 for (blue = 0; blue < 6; blue++) {
243                                         code = 16 + (red * 36) +
244                                             (green * 6) + blue;
245                                         if (code != index)
246                                                 continue;
247                                         red = red ? (red * 40 + 55) : 0;
248                                         green = green ? (green * 40 + 55) : 0;
249                                         blue = blue ? (blue * 40 + 55) : 0;
250                                         color = CF(r, red);
251 					color |= CF(g, green);
252 					color |= CF(b, blue);
253 					return (color);
254                                 }
255                         }
256                 }
257         }
258 
259         /* colors 232-255 are a grayscale ramp */
260         for (gray = 0; gray < 24; gray++) {
261                 level = (gray * 10) + 8;
262                 code = 232 + gray;
263                 if (code == index)
264                         break;
265         }
266         return (CF(r, level) | CF(g, level) | CF(b, level));
267 #undef  CF
268 }
269 
270 /*
271  * Support for color mapping.
272  * For 8, 24 and 32 bit depth, use mask size 8.
273  * 15/16 bit depth needs to use mask size from mode,
274  * or we will lose color information from 32-bit to 15/16 bit translation.
275  */
276 uint32_t
277 gfx_fb_color_map(uint8_t index)
278 {
279 	int rmask, gmask, bmask;
280 	int roff, goff, boff, bpp;
281 
282 	roff = ffs(gfx_state.tg_fb.fb_mask_red) - 1;
283         goff = ffs(gfx_state.tg_fb.fb_mask_green) - 1;
284         boff = ffs(gfx_state.tg_fb.fb_mask_blue) - 1;
285 	bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3;
286 
287 	if (bpp == 2)
288 		rmask = gfx_state.tg_fb.fb_mask_red >> roff;
289 	else
290 		rmask = 0xff;
291 
292 	if (bpp == 2)
293 		gmask = gfx_state.tg_fb.fb_mask_green >> goff;
294 	else
295 		gmask = 0xff;
296 
297 	if (bpp == 2)
298 		bmask = gfx_state.tg_fb.fb_mask_blue >> boff;
299 	else
300 		bmask = 0xff;
301 
302 	return (rgb_color_map(index, rmask, 16, gmask, 8, bmask, 0));
303 }
304 
305 /*
306  * Get indexed color from RGB. This function is used to write data to video
307  * memory when the adapter is set to use indexed colors.
308  * Since UEFI does only support 32-bit colors, we do not implement it for
309  * UEFI because there is no need for it and we do not have palette array
310  * for UEFI.
311  */
312 static uint8_t
313 rgb_to_color_index(uint8_t r, uint8_t g, uint8_t b)
314 {
315 #if !defined(EFI)
316 	uint32_t color, best, dist, k;
317 	int diff;
318 
319 	color = 0;
320 	best = 255 * 255 * 255;
321 	for (k = 0; k < NCMAP; k++) {
322 		diff = r - pe8[k].Red;
323 		dist = diff * diff;
324 		diff = g - pe8[k].Green;
325 		dist += diff * diff;
326 		diff = b - pe8[k].Blue;
327 		dist += diff * diff;
328 
329 		/* Exact match, exit the loop */
330 		if (dist == 0)
331 			break;
332 
333 		if (dist < best) {
334 			color = k;
335 			best = dist;
336 		}
337 	}
338 	if (k == NCMAP)
339 		k = color;
340 	return (k);
341 #else
342 	(void) r;
343 	(void) g;
344 	(void) b;
345 	return (0);
346 #endif
347 }
348 
349 int
350 generate_cons_palette(uint32_t *palette, int format,
351     uint32_t rmax, int roffset, uint32_t gmax, int goffset,
352     uint32_t bmax, int boffset)
353 {
354 	int i;
355 
356 	switch (format) {
357 	case COLOR_FORMAT_VGA:
358 		for (i = 0; i < NCOLORS; i++)
359 			palette[i] = cons_to_vga_colors[i];
360 		for (; i < NCMAP; i++)
361 			palette[i] = i;
362 		break;
363 	case COLOR_FORMAT_RGB:
364 		for (i = 0; i < NCMAP; i++)
365 			palette[i] = rgb_color_map(i, rmax, roffset,
366 			    gmax, goffset, bmax, boffset);
367 		break;
368 	default:
369 		return (ENODEV);
370 	}
371 
372 	return (0);
373 }
374 
375 static void
376 gfx_mem_wr1(uint8_t *base, size_t size, uint32_t o, uint8_t v)
377 {
378 
379 	if (o >= size)
380 		return;
381 	*(uint8_t *)(base + o) = v;
382 }
383 
384 static void
385 gfx_mem_wr2(uint8_t *base, size_t size, uint32_t o, uint16_t v)
386 {
387 
388 	if (o >= size)
389 		return;
390 	*(uint16_t *)(base + o) = v;
391 }
392 
393 static void
394 gfx_mem_wr4(uint8_t *base, size_t size, uint32_t o, uint32_t v)
395 {
396 
397 	if (o >= size)
398 		return;
399 	*(uint32_t *)(base + o) = v;
400 }
401 
402 static int gfxfb_blt_fill(void *BltBuffer,
403     uint32_t DestinationX, uint32_t DestinationY,
404     uint32_t Width, uint32_t Height)
405 {
406 #if defined(EFI)
407 	EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
408 #else
409 	struct paletteentry *p;
410 #endif
411 	uint32_t data, bpp, pitch, y, x;
412 	int roff, goff, boff;
413 	size_t size;
414 	off_t off;
415 	uint8_t *destination;
416 
417 	if (BltBuffer == NULL)
418 		return (EINVAL);
419 
420 	if (DestinationY + Height > gfx_state.tg_fb.fb_height)
421 		return (EINVAL);
422 
423 	if (DestinationX + Width > gfx_state.tg_fb.fb_width)
424 		return (EINVAL);
425 
426 	if (Width == 0 || Height == 0)
427 		return (EINVAL);
428 
429 	p = BltBuffer;
430 	roff = ffs(gfx_state.tg_fb.fb_mask_red) - 1;
431 	goff = ffs(gfx_state.tg_fb.fb_mask_green) - 1;
432 	boff = ffs(gfx_state.tg_fb.fb_mask_blue) - 1;
433 
434 	if (gfx_state.tg_fb.fb_bpp == 8) {
435 		data = rgb_to_color_index(p->Red, p->Green, p->Blue);
436 	} else {
437 		data = (p->Red &
438 		    (gfx_state.tg_fb.fb_mask_red >> roff)) << roff;
439 		data |= (p->Green &
440 		    (gfx_state.tg_fb.fb_mask_green >> goff)) << goff;
441 		data |= (p->Blue &
442 		    (gfx_state.tg_fb.fb_mask_blue >> boff)) << boff;
443 	}
444 
445 	bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3;
446 	pitch = gfx_state.tg_fb.fb_stride * bpp;
447 	destination = gfx_get_fb_address();
448 	size = gfx_state.tg_fb.fb_size;
449 
450 	for (y = DestinationY; y < Height + DestinationY; y++) {
451 		off = y * pitch + DestinationX * bpp;
452 		for (x = 0; x < Width; x++) {
453 			switch (bpp) {
454 			case 1:
455 				gfx_mem_wr1(destination, size, off,
456 				    (data < NCOLORS) ?
457 				    cons_to_vga_colors[data] : data);
458 				break;
459 			case 2:
460 				gfx_mem_wr2(destination, size, off, data);
461 				break;
462 			case 3:
463 				gfx_mem_wr1(destination, size, off,
464 				    (data >> 16) & 0xff);
465 				gfx_mem_wr1(destination, size, off + 1,
466 				    (data >> 8) & 0xff);
467 				gfx_mem_wr1(destination, size, off + 2,
468 				    data & 0xff);
469 				break;
470 			case 4:
471 				gfx_mem_wr4(destination, size, off, data);
472 				break;
473 			default:
474 				return (EINVAL);
475 			}
476 			off += bpp;
477 		}
478 	}
479 
480 	return (0);
481 }
482 
483 static int
484 gfxfb_blt_video_to_buffer(void *BltBuffer, uint32_t SourceX, uint32_t SourceY,
485     uint32_t DestinationX, uint32_t DestinationY,
486     uint32_t Width, uint32_t Height, uint32_t Delta)
487 {
488 #if defined(EFI)
489 	EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
490 #else
491 	struct paletteentry *p;
492 #endif
493 	uint32_t x, sy, dy;
494 	uint32_t bpp, pitch, copybytes;
495 	off_t off;
496 	uint8_t *source, *destination, *sb;
497 	uint8_t rm, rp, gm, gp, bm, bp;
498 	bool bgra;
499 
500 	if (BltBuffer == NULL)
501 		return (EINVAL);
502 
503 	if (SourceY + Height >
504 	    gfx_state.tg_fb.fb_height)
505 		return (EINVAL);
506 
507 	if (SourceX + Width > gfx_state.tg_fb.fb_width)
508 		return (EINVAL);
509 
510 	if (Width == 0 || Height == 0)
511 		return (EINVAL);
512 
513 	if (Delta == 0)
514 		Delta = Width * sizeof (*p);
515 
516 	bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3;
517 	pitch = gfx_state.tg_fb.fb_stride * bpp;
518 
519 	copybytes = Width * bpp;
520 
521 	rp = ffs(gfx_state.tg_fb.fb_mask_red) - 1;
522 	gp = ffs(gfx_state.tg_fb.fb_mask_green) - 1;
523 	bp = ffs(gfx_state.tg_fb.fb_mask_blue) - 1;
524 	rm = gfx_state.tg_fb.fb_mask_red >> rp;
525 	gm = gfx_state.tg_fb.fb_mask_green >> gp;
526 	bm = gfx_state.tg_fb.fb_mask_blue >> bp;
527 
528 	/* If FB pixel format is BGRA, we can use direct copy. */
529 	bgra = bpp == 4 &&
530 	    ffs(rm) - 1 == 8 && rp == 16 &&
531 	    ffs(gm) - 1 == 8 && gp == 8 &&
532 	    ffs(bm) - 1 == 8 && bp == 0;
533 
534 	for (sy = SourceY, dy = DestinationY; dy < Height + DestinationY;
535 	    sy++, dy++) {
536 		off = sy * pitch + SourceX * bpp;
537 		source = gfx_get_fb_address() + off;
538 		destination = (uint8_t *)BltBuffer + dy * Delta +
539 		    DestinationX * sizeof (*p);
540 
541 		if (bgra) {
542 			bcopy(source, destination, copybytes);
543 		} else {
544 			for (x = 0; x < Width; x++) {
545 				uint32_t c = 0;
546 
547 				p = (void *)(destination + x * sizeof (*p));
548 				sb = source + x * bpp;
549 				switch (bpp) {
550 				case 1:
551 					c = *sb;
552 					break;
553 				case 2:
554 					c = *(uint16_t *)sb;
555 					break;
556 				case 3:
557 					c = sb[0] << 16 | sb[1] << 8 | sb[2];
558 					break;
559 				case 4:
560 					c = *(uint32_t *)sb;
561 					break;
562 				default:
563 					return (EINVAL);
564 				}
565 
566 				if (bpp == 1) {
567 					*(uint32_t *)p = gfx_fb_color_map(
568 					    (c < 16) ?
569 					    vga_to_cons_colors[c] : c);
570 				} else {
571 					p->Red = (c >> rp) & rm;
572 					p->Green = (c >> gp) & gm;
573 					p->Blue = (c >> bp) & bm;
574 					p->Reserved = 0;
575 				}
576 			}
577 		}
578 	}
579 
580 	return (0);
581 }
582 
583 static int
584 gfxfb_blt_buffer_to_video(void *BltBuffer, uint32_t SourceX, uint32_t SourceY,
585     uint32_t DestinationX, uint32_t DestinationY,
586     uint32_t Width, uint32_t Height, uint32_t Delta)
587 {
588 #if defined(EFI)
589 	EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
590 #else
591 	struct paletteentry *p;
592 #endif
593 	uint32_t x, sy, dy;
594 	uint32_t bpp, pitch, copybytes;
595 	off_t off;
596 	uint8_t *source, *destination;
597 	uint8_t rm, rp, gm, gp, bm, bp;
598 	bool bgra;
599 
600 	if (BltBuffer == NULL)
601 		return (EINVAL);
602 
603 	if (DestinationY + Height >
604 	    gfx_state.tg_fb.fb_height)
605 		return (EINVAL);
606 
607 	if (DestinationX + Width > gfx_state.tg_fb.fb_width)
608 		return (EINVAL);
609 
610 	if (Width == 0 || Height == 0)
611 		return (EINVAL);
612 
613 	if (Delta == 0)
614 		Delta = Width * sizeof (*p);
615 
616 	bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3;
617 	pitch = gfx_state.tg_fb.fb_stride * bpp;
618 
619 	copybytes = Width * bpp;
620 
621 	rp = ffs(gfx_state.tg_fb.fb_mask_red) - 1;
622 	gp = ffs(gfx_state.tg_fb.fb_mask_green) - 1;
623 	bp = ffs(gfx_state.tg_fb.fb_mask_blue) - 1;
624 	rm = gfx_state.tg_fb.fb_mask_red >> rp;
625 	gm = gfx_state.tg_fb.fb_mask_green >> gp;
626 	bm = gfx_state.tg_fb.fb_mask_blue >> bp;
627 
628 	/* If FB pixel format is BGRA, we can use direct copy. */
629 	bgra = bpp == 4 &&
630 	    ffs(rm) - 1 == 8 && rp == 16 &&
631 	    ffs(gm) - 1 == 8 && gp == 8 &&
632 	    ffs(bm) - 1 == 8 && bp == 0;
633 
634 	for (sy = SourceY, dy = DestinationY; sy < Height + SourceY;
635 	    sy++, dy++) {
636 		off = dy * pitch + DestinationX * bpp;
637 		destination = gfx_get_fb_address() + off;
638 
639 		if (bgra) {
640 			source = (uint8_t *)BltBuffer + sy * Delta +
641 			    SourceX * sizeof (*p);
642 			bcopy(source, destination, copybytes);
643 		} else {
644 			for (x = 0; x < Width; x++) {
645 				uint32_t c;
646 
647 				p = (void *)((uint8_t *)BltBuffer +
648 				    sy * Delta +
649 				    (SourceX + x) * sizeof (*p));
650 				if (bpp == 1) {
651 					c = rgb_to_color_index(p->Red,
652 					    p->Green, p->Blue);
653 				} else {
654 					c = (p->Red & rm) << rp |
655 					    (p->Green & gm) << gp |
656 					    (p->Blue & bm) << bp;
657 				}
658 				off = x * bpp;
659 				switch (bpp) {
660 				case 1:
661 					gfx_mem_wr1(destination, copybytes,
662 					    off, (c < 16) ?
663 					    cons_to_vga_colors[c] : c);
664 					break;
665 				case 2:
666 					gfx_mem_wr2(destination, copybytes,
667 					    off, c);
668 					break;
669 				case 3:
670 					gfx_mem_wr1(destination, copybytes,
671 					    off, (c >> 16) & 0xff);
672 					gfx_mem_wr1(destination, copybytes,
673 					    off + 1, (c >> 8) & 0xff);
674 					gfx_mem_wr1(destination, copybytes,
675 					    off + 2, c & 0xff);
676 					break;
677 				case 4:
678 					gfx_mem_wr4(destination, copybytes,
679 					    x * bpp, c);
680 					break;
681 				default:
682 					return (EINVAL);
683 				}
684 			}
685 		}
686 	}
687 
688 	return (0);
689 }
690 
691 static int
692 gfxfb_blt_video_to_video(uint32_t SourceX, uint32_t SourceY,
693     uint32_t DestinationX, uint32_t DestinationY,
694     uint32_t Width, uint32_t Height)
695 {
696 	uint32_t bpp, copybytes;
697 	int pitch;
698 	uint8_t *source, *destination;
699 	off_t off;
700 
701 	if (SourceY + Height >
702 	    gfx_state.tg_fb.fb_height)
703 		return (EINVAL);
704 
705 	if (SourceX + Width > gfx_state.tg_fb.fb_width)
706 		return (EINVAL);
707 
708 	if (DestinationY + Height >
709 	    gfx_state.tg_fb.fb_height)
710 		return (EINVAL);
711 
712 	if (DestinationX + Width > gfx_state.tg_fb.fb_width)
713 		return (EINVAL);
714 
715 	if (Width == 0 || Height == 0)
716 		return (EINVAL);
717 
718 	bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3;
719 	pitch = gfx_state.tg_fb.fb_stride * bpp;
720 
721 	copybytes = Width * bpp;
722 
723 	off = SourceY * pitch + SourceX * bpp;
724 	source = gfx_get_fb_address() + off;
725 	off = DestinationY * pitch + DestinationX * bpp;
726 	destination = gfx_get_fb_address() + off;
727 
728 	if ((uintptr_t)destination > (uintptr_t)source) {
729 		source += Height * pitch;
730 		destination += Height * pitch;
731 		pitch = -pitch;
732 	}
733 
734 	while (Height-- > 0) {
735 		bcopy(source, destination, copybytes);
736 		source += pitch;
737 		destination += pitch;
738 	}
739 
740 	return (0);
741 }
742 
743 static void
744 gfxfb_shadow_fill(uint32_t *BltBuffer,
745     uint32_t DestinationX, uint32_t DestinationY,
746     uint32_t Width, uint32_t Height)
747 {
748 	uint32_t fbX, fbY;
749 
750 	if (gfx_state.tg_shadow_fb == NULL)
751 		return;
752 
753 	fbX = gfx_state.tg_fb.fb_width;
754 	fbY = gfx_state.tg_fb.fb_height;
755 
756 	if (BltBuffer == NULL)
757 		return;
758 
759 	if (DestinationX + Width > fbX)
760 		Width = fbX - DestinationX;
761 
762 	if (DestinationY + Height > fbY)
763 		Height = fbY - DestinationY;
764 
765 	uint32_t y2 = Height + DestinationY;
766 	for (uint32_t y1 = DestinationY; y1 < y2; y1++) {
767 		uint32_t off = y1 * fbX + DestinationX;
768 
769 		for (uint32_t x = 0; x < Width; x++) {
770 			gfx_state.tg_shadow_fb[off + x] = *BltBuffer;
771 		}
772 	}
773 }
774 
775 int
776 gfxfb_blt(void *BltBuffer, GFXFB_BLT_OPERATION BltOperation,
777     uint32_t SourceX, uint32_t SourceY,
778     uint32_t DestinationX, uint32_t DestinationY,
779     uint32_t Width, uint32_t Height, uint32_t Delta)
780 {
781 	int rv;
782 #if defined(EFI)
783 	EFI_STATUS status;
784 	EFI_GRAPHICS_OUTPUT *gop = gfx_state.tg_private;
785 	EFI_TPL tpl;
786 
787 	/*
788 	 * We assume Blt() does work, if not, we will need to build exception
789 	 * list case by case. We only have boot services during part of our
790 	 * exectution. Once terminate boot services, these operations cannot be
791 	 * done as they are provided by protocols that disappear when exit
792 	 * boot services.
793 	 */
794 	if (gop != NULL && boot_services_active) {
795 		tpl = BS->RaiseTPL(TPL_NOTIFY);
796 		switch (BltOperation) {
797 		case GfxFbBltVideoFill:
798 			gfxfb_shadow_fill(BltBuffer, DestinationX,
799 			    DestinationY, Width, Height);
800 			status = gop->Blt(gop, BltBuffer, EfiBltVideoFill,
801 			    SourceX, SourceY, DestinationX, DestinationY,
802 			    Width, Height, Delta);
803 			break;
804 
805 		case GfxFbBltVideoToBltBuffer:
806 			status = gop->Blt(gop, BltBuffer,
807 			    EfiBltVideoToBltBuffer,
808 			    SourceX, SourceY, DestinationX, DestinationY,
809 			    Width, Height, Delta);
810 			break;
811 
812 		case GfxFbBltBufferToVideo:
813 			status = gop->Blt(gop, BltBuffer, EfiBltBufferToVideo,
814 			    SourceX, SourceY, DestinationX, DestinationY,
815 			    Width, Height, Delta);
816 			break;
817 
818 		case GfxFbBltVideoToVideo:
819 			status = gop->Blt(gop, BltBuffer, EfiBltVideoToVideo,
820 			    SourceX, SourceY, DestinationX, DestinationY,
821 			    Width, Height, Delta);
822 			break;
823 
824 		default:
825 			status = EFI_INVALID_PARAMETER;
826 			break;
827 		}
828 
829 		switch (status) {
830 		case EFI_SUCCESS:
831 			rv = 0;
832 			break;
833 
834 		case EFI_INVALID_PARAMETER:
835 			rv = EINVAL;
836 			break;
837 
838 		case EFI_DEVICE_ERROR:
839 		default:
840 			rv = EIO;
841 			break;
842 		}
843 
844 		BS->RestoreTPL(tpl);
845 		return (rv);
846 	}
847 #endif
848 
849 	switch (BltOperation) {
850 	case GfxFbBltVideoFill:
851 		gfxfb_shadow_fill(BltBuffer, DestinationX, DestinationY,
852 		    Width, Height);
853 		rv = gfxfb_blt_fill(BltBuffer, DestinationX, DestinationY,
854 		    Width, Height);
855 		break;
856 
857 	case GfxFbBltVideoToBltBuffer:
858 		rv = gfxfb_blt_video_to_buffer(BltBuffer, SourceX, SourceY,
859 		    DestinationX, DestinationY, Width, Height, Delta);
860 		break;
861 
862 	case GfxFbBltBufferToVideo:
863 		rv = gfxfb_blt_buffer_to_video(BltBuffer, SourceX, SourceY,
864 		    DestinationX, DestinationY, Width, Height, Delta);
865 		break;
866 
867 	case GfxFbBltVideoToVideo:
868 		rv = gfxfb_blt_video_to_video(SourceX, SourceY,
869 		    DestinationX, DestinationY, Width, Height);
870 		break;
871 
872 	default:
873 		rv = EINVAL;
874 		break;
875 	}
876 	return (rv);
877 }
878 
879 void
880 gfx_bitblt_bitmap(teken_gfx_t *state, const uint8_t *glyph,
881     const teken_attr_t *a, uint32_t alpha, bool cursor)
882 {
883 	uint32_t width, height;
884 	uint32_t fgc, bgc, bpl, cc, o;
885 	int bpp, bit, byte;
886 	bool invert = false;
887 
888 	bpp = 4;		/* We only generate BGRA */
889 	width = state->tg_font.vf_width;
890 	height = state->tg_font.vf_height;
891 	bpl = (width + 7) / 8;  /* Bytes per source line. */
892 
893 	fgc = a->ta_fgcolor;
894 	bgc = a->ta_bgcolor;
895 	if (a->ta_format & TF_BOLD)
896 		fgc |= TC_LIGHT;
897 	if (a->ta_format & TF_BLINK)
898 		bgc |= TC_LIGHT;
899 
900 	fgc = gfx_fb_color_map(fgc);
901 	bgc = gfx_fb_color_map(bgc);
902 
903 	if (a->ta_format & TF_REVERSE)
904 		invert = !invert;
905 	if (cursor)
906 		invert = !invert;
907 	if (invert) {
908 		uint32_t tmp;
909 
910 		tmp = fgc;
911 		fgc = bgc;
912 		bgc = tmp;
913 	}
914 
915 	alpha = alpha << 24;
916 	fgc |= alpha;
917 	bgc |= alpha;
918 
919 	for (uint32_t y = 0; y < height; y++) {
920 		for (uint32_t x = 0; x < width; x++) {
921 			byte = y * bpl + x / 8;
922 			bit = 0x80 >> (x % 8);
923 			o = y * width * bpp + x * bpp;
924 			cc = glyph[byte] & bit ? fgc : bgc;
925 
926 			gfx_mem_wr4(state->tg_glyph,
927 			    state->tg_glyph_size, o, cc);
928 		}
929 	}
930 }
931 
932 /*
933  * Draw prepared glyph on terminal point p.
934  */
935 static void
936 gfx_fb_printchar(teken_gfx_t *state, const teken_pos_t *p)
937 {
938 	unsigned x, y, width, height;
939 
940 	width = state->tg_font.vf_width;
941 	height = state->tg_font.vf_height;
942 	x = state->tg_origin.tp_col + p->tp_col * width;
943 	y = state->tg_origin.tp_row + p->tp_row * height;
944 
945 	gfx_fb_cons_display(x, y, width, height, state->tg_glyph);
946 }
947 
948 /*
949  * Store char with its attribute to buffer and put it on screen.
950  */
951 void
952 gfx_fb_putchar(void *arg, const teken_pos_t *p, teken_char_t c,
953     const teken_attr_t *a)
954 {
955 	teken_gfx_t *state = arg;
956 	const uint8_t *glyph;
957 	int idx;
958 
959 	idx = p->tp_col + p->tp_row * state->tg_tp.tp_col;
960 	if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row)
961 		return;
962 
963 	/* remove the cursor */
964 	if (state->tg_cursor_visible)
965 		gfx_fb_cursor_draw(state, &state->tg_cursor, false);
966 
967 	screen_buffer[idx].c = c;
968 	screen_buffer[idx].a = *a;
969 
970 	glyph = font_lookup(&state->tg_font, c, a);
971 	gfx_bitblt_bitmap(state, glyph, a, 0xff, false);
972 	gfx_fb_printchar(state, p);
973 
974 	/* display the cursor */
975 	if (state->tg_cursor_visible) {
976 		const teken_pos_t *c;
977 
978 		c = teken_get_cursor(&state->tg_teken);
979 		gfx_fb_cursor_draw(state, c, true);
980 	}
981 }
982 
983 void
984 gfx_fb_fill(void *arg, const teken_rect_t *r, teken_char_t c,
985     const teken_attr_t *a)
986 {
987 	teken_gfx_t *state = arg;
988 	const uint8_t *glyph;
989 	teken_pos_t p;
990 	struct text_pixel *row;
991 
992 	/* remove the cursor */
993 	if (state->tg_cursor_visible)
994 		gfx_fb_cursor_draw(state, &state->tg_cursor, false);
995 
996 	glyph = font_lookup(&state->tg_font, c, a);
997 	gfx_bitblt_bitmap(state, glyph, a, 0xff, false);
998 
999 	for (p.tp_row = r->tr_begin.tp_row; p.tp_row < r->tr_end.tp_row;
1000 	    p.tp_row++) {
1001 		row = &screen_buffer[p.tp_row * state->tg_tp.tp_col];
1002 		for (p.tp_col = r->tr_begin.tp_col;
1003 		    p.tp_col < r->tr_end.tp_col; p.tp_col++) {
1004 			row[p.tp_col].c = c;
1005 			row[p.tp_col].a = *a;
1006 			gfx_fb_printchar(state, &p);
1007 		}
1008 	}
1009 
1010 	/* display the cursor */
1011 	if (state->tg_cursor_visible) {
1012 		const teken_pos_t *c;
1013 
1014 		c = teken_get_cursor(&state->tg_teken);
1015 		gfx_fb_cursor_draw(state, c, true);
1016 	}
1017 }
1018 
1019 static void
1020 gfx_fb_cursor_draw(teken_gfx_t *state, const teken_pos_t *pos, bool on)
1021 {
1022 	const uint8_t *glyph;
1023 	teken_pos_t p;
1024 	int idx;
1025 
1026 	p = *pos;
1027 	if (p.tp_col >= state->tg_tp.tp_col)
1028 		p.tp_col = state->tg_tp.tp_col - 1;
1029 	if (p.tp_row >= state->tg_tp.tp_row)
1030 		p.tp_row = state->tg_tp.tp_row - 1;
1031 	idx = p.tp_col + p.tp_row * state->tg_tp.tp_col;
1032 	if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row)
1033 		return;
1034 
1035 	glyph = font_lookup(&state->tg_font, screen_buffer[idx].c,
1036 	    &screen_buffer[idx].a);
1037 	gfx_bitblt_bitmap(state, glyph, &screen_buffer[idx].a, 0xff, on);
1038 	gfx_fb_printchar(state, &p);
1039 
1040 	state->tg_cursor = p;
1041 }
1042 
1043 void
1044 gfx_fb_cursor(void *arg, const teken_pos_t *p)
1045 {
1046 	teken_gfx_t *state = arg;
1047 
1048 	/* Switch cursor off in old location and back on in new. */
1049 	if (state->tg_cursor_visible) {
1050 		gfx_fb_cursor_draw(state, &state->tg_cursor, false);
1051 		gfx_fb_cursor_draw(state, p, true);
1052 	}
1053 }
1054 
1055 void
1056 gfx_fb_param(void *arg, int cmd, unsigned int value)
1057 {
1058 	teken_gfx_t *state = arg;
1059 	const teken_pos_t *c;
1060 
1061 	switch (cmd) {
1062 	case TP_SETLOCALCURSOR:
1063 		/*
1064 		 * 0 means normal (usually block), 1 means hidden, and
1065 		 * 2 means blinking (always block) for compatibility with
1066 		 * syscons.  We don't support any changes except hiding,
1067 		 * so must map 2 to 0.
1068 		 */
1069 		value = (value == 1) ? 0 : 1;
1070 		/* FALLTHROUGH */
1071 	case TP_SHOWCURSOR:
1072 		c = teken_get_cursor(&state->tg_teken);
1073 		gfx_fb_cursor_draw(state, c, true);
1074 		if (value != 0)
1075 			state->tg_cursor_visible = true;
1076 		else
1077 			state->tg_cursor_visible = false;
1078 		break;
1079 	default:
1080 		/* Not yet implemented */
1081 		break;
1082 	}
1083 }
1084 
1085 bool
1086 is_same_pixel(struct text_pixel *px1, struct text_pixel *px2)
1087 {
1088 	if (px1->c != px2->c)
1089 		return (false);
1090 
1091 	/* Is there image stored? */
1092 	if ((px1->a.ta_format & TF_IMAGE) ||
1093 	    (px2->a.ta_format & TF_IMAGE))
1094 		return (false);
1095 
1096 	if (px1->a.ta_format != px2->a.ta_format)
1097 		return (false);
1098 	if (px1->a.ta_fgcolor != px2->a.ta_fgcolor)
1099 		return (false);
1100 	if (px1->a.ta_bgcolor != px2->a.ta_bgcolor)
1101 		return (false);
1102 
1103 	return (true);
1104 }
1105 
1106 static void
1107 gfx_fb_copy_area(teken_gfx_t *state, const teken_rect_t *s,
1108     const teken_pos_t *d)
1109 {
1110 	uint32_t sx, sy, dx, dy, width, height;
1111 	uint32_t pitch, bytes;
1112 	int step;
1113 
1114 	width = state->tg_font.vf_width;
1115 	height = state->tg_font.vf_height;
1116 
1117 	sx = s->tr_begin.tp_col * width;
1118 	sy = s->tr_begin.tp_row * height;
1119 	dx = d->tp_col * width;
1120 	dy = d->tp_row * height;
1121 
1122 	width *= (s->tr_end.tp_col - s->tr_begin.tp_col + 1);
1123 
1124 	/*
1125 	 * With no shadow fb, use video to video copy.
1126 	 */
1127 	if (state->tg_shadow_fb == NULL) {
1128 		(void) gfxfb_blt(NULL, GfxFbBltVideoToVideo,
1129 		    sx + state->tg_origin.tp_col,
1130 		    sy + state->tg_origin.tp_row,
1131 		    dx + state->tg_origin.tp_col,
1132 		    dy + state->tg_origin.tp_row,
1133 		    width, height, 0);
1134 		return;
1135 	}
1136 
1137 	/*
1138 	 * With shadow fb, we need to copy data on both shadow and video,
1139 	 * to preserve the consistency. We only read data from shadow fb.
1140 	 */
1141 
1142 	step = 1;
1143 	pitch = state->tg_fb.fb_width;
1144 	bytes = width * sizeof (*state->tg_shadow_fb);
1145 
1146 	/*
1147 	 * To handle overlapping areas, set up reverse copy here.
1148 	 */
1149 	if (dy * pitch + dx > sy * pitch + sx) {
1150 		sy += height;
1151 		dy += height;
1152 		step = -step;
1153 	}
1154 
1155 	while (height-- > 0) {
1156 		uint32_t *source = &state->tg_shadow_fb[sy * pitch + sx];
1157 		uint32_t *destination = &state->tg_shadow_fb[dy * pitch + dx];
1158 
1159 		bcopy(source, destination, bytes);
1160 		(void) gfxfb_blt(destination, GfxFbBltBufferToVideo,
1161 		    0, 0, dx + state->tg_origin.tp_col,
1162 		    dy + state->tg_origin.tp_row, width, 1, 0);
1163 
1164 		sy += step;
1165 		dy += step;
1166 	}
1167 }
1168 
1169 static void
1170 gfx_fb_copy_line(teken_gfx_t *state, int ncol, teken_pos_t *s, teken_pos_t *d)
1171 {
1172 	teken_rect_t sr;
1173 	teken_pos_t dp;
1174 	unsigned soffset, doffset;
1175 	bool mark = false;
1176 	int x;
1177 
1178 	soffset = s->tp_col + s->tp_row * state->tg_tp.tp_col;
1179 	doffset = d->tp_col + d->tp_row * state->tg_tp.tp_col;
1180 
1181 	for (x = 0; x < ncol; x++) {
1182 		if (is_same_pixel(&screen_buffer[soffset + x],
1183 		    &screen_buffer[doffset + x])) {
1184 			if (mark) {
1185 				gfx_fb_copy_area(state, &sr, &dp);
1186 				mark = false;
1187 			}
1188 		} else {
1189 			screen_buffer[doffset + x] = screen_buffer[soffset + x];
1190 			if (mark) {
1191 				/* update end point */
1192 				sr.tr_end.tp_col = s->tp_col + x;
1193 			} else {
1194 				/* set up new rectangle */
1195 				mark = true;
1196 				sr.tr_begin.tp_col = s->tp_col + x;
1197 				sr.tr_begin.tp_row = s->tp_row;
1198 				sr.tr_end.tp_col = s->tp_col + x;
1199 				sr.tr_end.tp_row = s->tp_row;
1200 				dp.tp_col = d->tp_col + x;
1201 				dp.tp_row = d->tp_row;
1202 			}
1203 		}
1204 	}
1205 	if (mark) {
1206 		gfx_fb_copy_area(state, &sr, &dp);
1207 	}
1208 }
1209 
1210 void
1211 gfx_fb_copy(void *arg, const teken_rect_t *r, const teken_pos_t *p)
1212 {
1213 	teken_gfx_t *state = arg;
1214 	unsigned doffset, soffset;
1215 	teken_pos_t d, s;
1216 	int nrow, ncol, y; /* Has to be signed - >= 0 comparison */
1217 
1218 	/*
1219 	 * Copying is a little tricky. We must make sure we do it in
1220 	 * correct order, to make sure we don't overwrite our own data.
1221 	 */
1222 
1223 	nrow = r->tr_end.tp_row - r->tr_begin.tp_row;
1224 	ncol = r->tr_end.tp_col - r->tr_begin.tp_col;
1225 
1226 	if (p->tp_row + nrow > state->tg_tp.tp_row ||
1227 	    p->tp_col + ncol > state->tg_tp.tp_col)
1228 		return;
1229 
1230 	soffset = r->tr_begin.tp_col + r->tr_begin.tp_row * state->tg_tp.tp_col;
1231 	doffset = p->tp_col + p->tp_row * state->tg_tp.tp_col;
1232 
1233 	/* remove the cursor */
1234 	if (state->tg_cursor_visible)
1235 		gfx_fb_cursor_draw(state, &state->tg_cursor, false);
1236 
1237 	/*
1238 	 * Copy line by line.
1239 	 */
1240 	if (doffset <= soffset) {
1241 		s = r->tr_begin;
1242 		d = *p;
1243 		for (y = 0; y < nrow; y++) {
1244 			s.tp_row = r->tr_begin.tp_row + y;
1245 			d.tp_row = p->tp_row + y;
1246 
1247 			gfx_fb_copy_line(state, ncol, &s, &d);
1248 		}
1249 	} else {
1250 		for (y = nrow - 1; y >= 0; y--) {
1251 			s.tp_row = r->tr_begin.tp_row + y;
1252 			d.tp_row = p->tp_row + y;
1253 
1254 			gfx_fb_copy_line(state, ncol, &s, &d);
1255 		}
1256 	}
1257 
1258 	/* display the cursor */
1259 	if (state->tg_cursor_visible) {
1260 		const teken_pos_t *c;
1261 
1262 		c = teken_get_cursor(&state->tg_teken);
1263 		gfx_fb_cursor_draw(state, c, true);
1264 	}
1265 }
1266 
1267 /*
1268  * Implements alpha blending for RGBA data, could use pixels for arguments,
1269  * but byte stream seems more generic.
1270  * The generic alpha blending is:
1271  * blend = alpha * fg + (1.0 - alpha) * bg.
1272  * Since our alpha is not from range [0..1], we scale appropriately.
1273  */
1274 static uint8_t
1275 alpha_blend(uint8_t fg, uint8_t bg, uint8_t alpha)
1276 {
1277 	uint16_t blend, h, l;
1278 
1279 	/* trivial corner cases */
1280 	if (alpha == 0)
1281 		return (bg);
1282 	if (alpha == 0xFF)
1283 		return (fg);
1284 	blend = (alpha * fg + (0xFF - alpha) * bg);
1285 	/* Division by 0xFF */
1286 	h = blend >> 8;
1287 	l = blend & 0xFF;
1288 	if (h + l >= 0xFF)
1289 		h++;
1290 	return (h);
1291 }
1292 
1293 /*
1294  * Implements alpha blending for RGBA data, could use pixels for arguments,
1295  * but byte stream seems more generic.
1296  * The generic alpha blending is:
1297  * blend = alpha * fg + (1.0 - alpha) * bg.
1298  * Since our alpha is not from range [0..1], we scale appropriately.
1299  */
1300 static void
1301 bitmap_cpy(void *dst, void *src, uint32_t size)
1302 {
1303 #if defined(EFI)
1304 	EFI_GRAPHICS_OUTPUT_BLT_PIXEL *ps, *pd;
1305 #else
1306 	struct paletteentry *ps, *pd;
1307 #endif
1308 	uint32_t i;
1309 	uint8_t a;
1310 
1311 	ps = src;
1312 	pd = dst;
1313 
1314 	/*
1315 	 * we only implement alpha blending for depth 32.
1316 	 */
1317 	for (i = 0; i < size; i ++) {
1318 		a = ps[i].Reserved;
1319 		pd[i].Red = alpha_blend(ps[i].Red, pd[i].Red, a);
1320 		pd[i].Green = alpha_blend(ps[i].Green, pd[i].Green, a);
1321 		pd[i].Blue = alpha_blend(ps[i].Blue, pd[i].Blue, a);
1322 		pd[i].Reserved = a;
1323 	}
1324 }
1325 
1326 static void *
1327 allocate_glyphbuffer(uint32_t width, uint32_t height)
1328 {
1329 	size_t size;
1330 
1331 	size = sizeof (*GlyphBuffer) * width * height;
1332 	if (size != GlyphBufferSize) {
1333 		free(GlyphBuffer);
1334 		GlyphBuffer = malloc(size);
1335 		if (GlyphBuffer == NULL)
1336 			return (NULL);
1337 		GlyphBufferSize = size;
1338 	}
1339 	return (GlyphBuffer);
1340 }
1341 
1342 void
1343 gfx_fb_cons_display(uint32_t x, uint32_t y, uint32_t width, uint32_t height,
1344     void *data)
1345 {
1346 #if defined(EFI)
1347 	EFI_GRAPHICS_OUTPUT_BLT_PIXEL *buf, *p;
1348 #else
1349 	struct paletteentry *buf, *p;
1350 #endif
1351 	size_t size;
1352 
1353 	/*
1354 	 * If we do have shadow fb, we will use shadow to render data,
1355 	 * and copy shadow to video.
1356 	 */
1357 	if (gfx_state.tg_shadow_fb != NULL) {
1358 		uint32_t pitch = gfx_state.tg_fb.fb_width;
1359 
1360 		/* Copy rectangle line by line. */
1361 		p = data;
1362 		for (uint32_t sy = 0; sy < height; sy++) {
1363 			buf = (void *)(gfx_state.tg_shadow_fb +
1364 			    (y - gfx_state.tg_origin.tp_row) * pitch +
1365 			    x - gfx_state.tg_origin.tp_col);
1366 			bitmap_cpy(buf, &p[sy * width], width);
1367 			(void) gfxfb_blt(buf, GfxFbBltBufferToVideo,
1368 			    0, 0, x, y, width, 1, 0);
1369 			y++;
1370 		}
1371 		return;
1372 	}
1373 
1374 	/*
1375 	 * Common data to display is glyph, use preallocated
1376 	 * glyph buffer.
1377 	 */
1378         if (gfx_state.tg_glyph_size != GlyphBufferSize)
1379                 (void) allocate_glyphbuffer(width, height);
1380 
1381 	size = width * height * sizeof(*buf);
1382 	if (size == GlyphBufferSize)
1383 		buf = GlyphBuffer;
1384 	else
1385 		buf = malloc(size);
1386 	if (buf == NULL)
1387 		return;
1388 
1389 	if (gfxfb_blt(buf, GfxFbBltVideoToBltBuffer, x, y, 0, 0,
1390 	    width, height, 0) == 0) {
1391 		bitmap_cpy(buf, data, width * height);
1392 		(void) gfxfb_blt(buf, GfxFbBltBufferToVideo, 0, 0, x, y,
1393 		    width, height, 0);
1394 	}
1395 	if (buf != GlyphBuffer)
1396 		free(buf);
1397 }
1398 
1399 /*
1400  * Public graphics primitives.
1401  */
1402 
1403 static int
1404 isqrt(int num)
1405 {
1406 	int res = 0;
1407 	int bit = 1 << 30;
1408 
1409 	/* "bit" starts at the highest power of four <= the argument. */
1410 	while (bit > num)
1411 		bit >>= 2;
1412 
1413 	while (bit != 0) {
1414 		if (num >= res + bit) {
1415 			num -= res + bit;
1416 			res = (res >> 1) + bit;
1417 		} else {
1418 			res >>= 1;
1419 		}
1420 		bit >>= 2;
1421 	}
1422 	return (res);
1423 }
1424 
1425 static uint32_t
1426 gfx_fb_getcolor(void)
1427 {
1428 	uint32_t c;
1429 	const teken_attr_t *ap;
1430 
1431 	ap = teken_get_curattr(&gfx_state.tg_teken);
1432         if (ap->ta_format & TF_REVERSE) {
1433 		c = ap->ta_bgcolor;
1434 		if (ap->ta_format & TF_BLINK)
1435 			c |= TC_LIGHT;
1436 	} else {
1437 		c = ap->ta_fgcolor;
1438 		if (ap->ta_format & TF_BOLD)
1439 			c |= TC_LIGHT;
1440 	}
1441 
1442 	return (gfx_fb_color_map(c));
1443 }
1444 
1445 /* set pixel in framebuffer using gfx coordinates */
1446 void
1447 gfx_fb_setpixel(uint32_t x, uint32_t y)
1448 {
1449 	uint32_t c;
1450 
1451 	if (gfx_state.tg_fb_type == FB_TEXT)
1452 		return;
1453 
1454 	c = gfx_fb_getcolor();
1455 
1456 	if (x >= gfx_state.tg_fb.fb_width ||
1457 	    y >= gfx_state.tg_fb.fb_height)
1458 		return;
1459 
1460 	gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x, y, 1, 1, 0);
1461 }
1462 
1463 /*
1464  * draw rectangle in framebuffer using gfx coordinates.
1465  */
1466 void
1467 gfx_fb_drawrect(uint32_t x1, uint32_t y1, uint32_t x2, uint32_t y2,
1468     uint32_t fill)
1469 {
1470 	uint32_t c;
1471 
1472 	if (gfx_state.tg_fb_type == FB_TEXT)
1473 		return;
1474 
1475 	c = gfx_fb_getcolor();
1476 
1477 	if (fill != 0) {
1478 		gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x1, y1, x2 - x1,
1479 		    y2 - y1, 0);
1480 	} else {
1481 		gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x1, y1, x2 - x1, 1, 0);
1482 		gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x1, y2, x2 - x1, 1, 0);
1483 		gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x1, y1, 1, y2 - y1, 0);
1484 		gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x2, y1, 1, y2 - y1, 0);
1485 	}
1486 }
1487 
1488 void
1489 gfx_fb_line(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t wd)
1490 {
1491 	int dx, sx, dy, sy;
1492 	int err, e2, x2, y2, ed, width;
1493 
1494 	if (gfx_state.tg_fb_type == FB_TEXT)
1495 		return;
1496 
1497 	width = wd;
1498 	sx = x0 < x1? 1 : -1;
1499 	sy = y0 < y1? 1 : -1;
1500 	dx = x1 > x0? x1 - x0 : x0 - x1;
1501 	dy = y1 > y0? y1 - y0 : y0 - y1;
1502 	err = dx + dy;
1503 	ed = dx + dy == 0 ? 1: isqrt(dx * dx + dy * dy);
1504 
1505 	for (;;) {
1506 		gfx_fb_setpixel(x0, y0);
1507 		e2 = err;
1508 		x2 = x0;
1509 		if ((e2 << 1) >= -dx) {		/* x step */
1510 			e2 += dy;
1511 			y2 = y0;
1512 			while (e2 < ed * width &&
1513 			    (y1 != (uint32_t)y2 || dx > dy)) {
1514 				y2 += sy;
1515 				gfx_fb_setpixel(x0, y2);
1516 				e2 += dx;
1517 			}
1518 			if (x0 == x1)
1519 				break;
1520 			e2 = err;
1521 			err -= dy;
1522 			x0 += sx;
1523 		}
1524 		if ((e2 << 1) <= dy) {		/* y step */
1525 			e2 = dx-e2;
1526 			while (e2 < ed * width &&
1527 			    (x1 != (uint32_t)x2 || dx < dy)) {
1528 				x2 += sx;
1529 				gfx_fb_setpixel(x2, y0);
1530 				e2 += dy;
1531 			}
1532 			if (y0 == y1)
1533 				break;
1534 			err += dx;
1535 			y0 += sy;
1536 		}
1537 	}
1538 }
1539 
1540 /*
1541  * quadratic Bézier curve limited to gradients without sign change.
1542  */
1543 void
1544 gfx_fb_bezier(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t x2,
1545     uint32_t y2, uint32_t wd)
1546 {
1547 	int sx, sy, xx, yy, xy, width;
1548 	int dx, dy, err, curvature;
1549 	int i;
1550 
1551 	if (gfx_state.tg_fb_type == FB_TEXT)
1552 		return;
1553 
1554 	width = wd;
1555 	sx = x2 - x1;
1556 	sy = y2 - y1;
1557 	xx = x0 - x1;
1558 	yy = y0 - y1;
1559 	curvature = xx*sy - yy*sx;
1560 
1561 	if (sx*sx + sy*sy > xx*xx+yy*yy) {
1562 		x2 = x0;
1563 		x0 = sx + x1;
1564 		y2 = y0;
1565 		y0 = sy + y1;
1566 		curvature = -curvature;
1567 	}
1568 	if (curvature != 0) {
1569 		xx += sx;
1570 		sx = x0 < x2? 1 : -1;
1571 		xx *= sx;
1572 		yy += sy;
1573 		sy = y0 < y2? 1 : -1;
1574 		yy *= sy;
1575 		xy = (xx*yy) << 1;
1576 		xx *= xx;
1577 		yy *= yy;
1578 		if (curvature * sx * sy < 0) {
1579 			xx = -xx;
1580 			yy = -yy;
1581 			xy = -xy;
1582 			curvature = -curvature;
1583 		}
1584 		dx = 4 * sy * curvature * (x1 - x0) + xx - xy;
1585 		dy = 4 * sx * curvature * (y0 - y1) + yy - xy;
1586 		xx += xx;
1587 		yy += yy;
1588 		err = dx + dy + xy;
1589 		do {
1590 			for (i = 0; i <= width; i++)
1591 				gfx_fb_setpixel(x0 + i, y0);
1592 			if (x0 == x2 && y0 == y2)
1593 				return;  /* last pixel -> curve finished */
1594 			y1 = 2 * err < dx;
1595 			if (2 * err > dy) {
1596 				x0 += sx;
1597 				dx -= xy;
1598 				dy += yy;
1599 				err += dy;
1600 			}
1601 			if (y1 != 0) {
1602 				y0 += sy;
1603 				dy -= xy;
1604 				dx += xx;
1605 				err += dx;
1606 			}
1607 		} while (dy < dx); /* gradient negates -> algorithm fails */
1608 	}
1609 	gfx_fb_line(x0, y0, x2, y2, width);
1610 }
1611 
1612 /*
1613  * draw rectangle using terminal coordinates and current foreground color.
1614  */
1615 void
1616 gfx_term_drawrect(uint32_t ux1, uint32_t uy1, uint32_t ux2, uint32_t uy2)
1617 {
1618 	int x1, y1, x2, y2;
1619 	int xshift, yshift;
1620 	int width, i;
1621 	uint32_t vf_width, vf_height;
1622 	teken_rect_t r;
1623 
1624 	if (gfx_state.tg_fb_type == FB_TEXT)
1625 		return;
1626 
1627 	vf_width = gfx_state.tg_font.vf_width;
1628 	vf_height = gfx_state.tg_font.vf_height;
1629 	width = vf_width / 4;			/* line width */
1630 	xshift = (vf_width - width) / 2;
1631 	yshift = (vf_height - width) / 2;
1632 
1633 	/* Shift coordinates */
1634 	if (ux1 != 0)
1635 		ux1--;
1636 	if (uy1 != 0)
1637 		uy1--;
1638 	ux2--;
1639 	uy2--;
1640 
1641 	/* mark area used in terminal */
1642 	r.tr_begin.tp_col = ux1;
1643 	r.tr_begin.tp_row = uy1;
1644 	r.tr_end.tp_col = ux2 + 1;
1645 	r.tr_end.tp_row = uy2 + 1;
1646 
1647 	term_image_display(&gfx_state, &r);
1648 
1649 	/*
1650 	 * Draw horizontal lines width points thick, shifted from outer edge.
1651 	 */
1652 	x1 = (ux1 + 1) * vf_width + gfx_state.tg_origin.tp_col;
1653 	y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row + yshift;
1654 	x2 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
1655 	gfx_fb_drawrect(x1, y1, x2, y1 + width, 1);
1656 	y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
1657 	y2 += vf_height - yshift - width;
1658 	gfx_fb_drawrect(x1, y2, x2, y2 + width, 1);
1659 
1660 	/*
1661 	 * Draw vertical lines width points thick, shifted from outer edge.
1662 	 */
1663 	x1 = ux1 * vf_width + gfx_state.tg_origin.tp_col + xshift;
1664 	y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row;
1665 	y1 += vf_height;
1666 	y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
1667 	gfx_fb_drawrect(x1, y1, x1 + width, y2, 1);
1668 	x1 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
1669 	x1 += vf_width - xshift - width;
1670 	gfx_fb_drawrect(x1, y1, x1 + width, y2, 1);
1671 
1672 	/* Draw upper left corner. */
1673 	x1 = ux1 * vf_width + gfx_state.tg_origin.tp_col + xshift;
1674 	y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row;
1675 	y1 += vf_height;
1676 
1677 	x2 = ux1 * vf_width + gfx_state.tg_origin.tp_col;
1678 	x2 += vf_width;
1679 	y2 = uy1 * vf_height + gfx_state.tg_origin.tp_row + yshift;
1680 	for (i = 0; i <= width; i++)
1681 		gfx_fb_bezier(x1 + i, y1, x1 + i, y2 + i, x2, y2 + i, width-i);
1682 
1683 	/* Draw lower left corner. */
1684 	x1 = ux1 * vf_width + gfx_state.tg_origin.tp_col;
1685 	x1 += vf_width;
1686 	y1 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
1687 	y1 += vf_height - yshift;
1688 	x2 = ux1 * vf_width + gfx_state.tg_origin.tp_col + xshift;
1689 	y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
1690 	for (i = 0; i <= width; i++)
1691 		gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i);
1692 
1693 	/* Draw upper right corner. */
1694 	x1 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
1695 	y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row + yshift;
1696 	x2 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
1697 	x2 += vf_width - xshift - width;
1698 	y2 = uy1 * vf_height + gfx_state.tg_origin.tp_row;
1699 	y2 += vf_height;
1700 	for (i = 0; i <= width; i++)
1701 		gfx_fb_bezier(x1, y1 + i, x2 + i, y1 + i, x2 + i, y2, width-i);
1702 
1703 	/* Draw lower right corner. */
1704 	x1 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
1705 	y1 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
1706 	y1 += vf_height - yshift;
1707 	x2 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
1708 	x2 += vf_width - xshift - width;
1709 	y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
1710 	for (i = 0; i <= width; i++)
1711 		gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i);
1712 }
1713 
1714 int
1715 gfx_fb_putimage(png_t *png, uint32_t ux1, uint32_t uy1, uint32_t ux2,
1716     uint32_t uy2, uint32_t flags)
1717 {
1718 #if defined(EFI)
1719 	EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
1720 #else
1721 	struct paletteentry *p;
1722 #endif
1723 	uint8_t *data;
1724 	uint32_t i, j, x, y, fheight, fwidth;
1725 	int rs, gs, bs;
1726 	uint8_t r, g, b, a;
1727 	bool scale = false;
1728 	bool trace = false;
1729 	teken_rect_t rect;
1730 
1731 	trace = (flags & FL_PUTIMAGE_DEBUG) != 0;
1732 
1733 	if (gfx_state.tg_fb_type == FB_TEXT) {
1734 		if (trace)
1735 			printf("Framebuffer not active.\n");
1736 		return (1);
1737 	}
1738 
1739 	if (png->color_type != PNG_TRUECOLOR_ALPHA) {
1740 		if (trace)
1741 			printf("Not truecolor image.\n");
1742 		return (1);
1743 	}
1744 
1745 	if (ux1 > gfx_state.tg_fb.fb_width ||
1746 	    uy1 > gfx_state.tg_fb.fb_height) {
1747 		if (trace)
1748 			printf("Top left coordinate off screen.\n");
1749 		return (1);
1750 	}
1751 
1752 	if (png->width > UINT16_MAX || png->height > UINT16_MAX) {
1753 		if (trace)
1754 			printf("Image too large.\n");
1755 		return (1);
1756 	}
1757 
1758 	if (png->width < 1 || png->height < 1) {
1759 		if (trace)
1760 			printf("Image too small.\n");
1761 		return (1);
1762 	}
1763 
1764 	/*
1765 	 * If 0 was passed for either ux2 or uy2, then calculate the missing
1766 	 * part of the bottom right coordinate.
1767 	 */
1768 	scale = true;
1769 	if (ux2 == 0 && uy2 == 0) {
1770 		/* Both 0, use the native resolution of the image */
1771 		ux2 = ux1 + png->width;
1772 		uy2 = uy1 + png->height;
1773 		scale = false;
1774 	} else if (ux2 == 0) {
1775 		/* Set ux2 from uy2/uy1 to maintain aspect ratio */
1776 		ux2 = ux1 + (png->width * (uy2 - uy1)) / png->height;
1777 	} else if (uy2 == 0) {
1778 		/* Set uy2 from ux2/ux1 to maintain aspect ratio */
1779 		uy2 = uy1 + (png->height * (ux2 - ux1)) / png->width;
1780 	}
1781 
1782 	if (ux2 > gfx_state.tg_fb.fb_width ||
1783 	    uy2 > gfx_state.tg_fb.fb_height) {
1784 		if (trace)
1785 			printf("Bottom right coordinate off screen.\n");
1786 		return (1);
1787 	}
1788 
1789 	fwidth = ux2 - ux1;
1790 	fheight = uy2 - uy1;
1791 
1792 	/*
1793 	 * If the original image dimensions have been passed explicitly,
1794 	 * disable scaling.
1795 	 */
1796 	if (fwidth == png->width && fheight == png->height)
1797 		scale = false;
1798 
1799 	if (ux1 == 0) {
1800 		/*
1801 		 * No top left X co-ordinate (real coordinates start at 1),
1802 		 * place as far right as it will fit.
1803 		 */
1804 		ux2 = gfx_state.tg_fb.fb_width - gfx_state.tg_origin.tp_col;
1805 		ux1 = ux2 - fwidth;
1806 	}
1807 
1808 	if (uy1 == 0) {
1809 		/*
1810 		 * No top left Y co-ordinate (real coordinates start at 1),
1811 		 * place as far down as it will fit.
1812 		 */
1813 		uy2 = gfx_state.tg_fb.fb_height - gfx_state.tg_origin.tp_row;
1814 		uy1 = uy2 - fheight;
1815 	}
1816 
1817 	if (ux1 >= ux2 || uy1 >= uy2) {
1818 		if (trace)
1819 			printf("Image dimensions reversed.\n");
1820 		return (1);
1821 	}
1822 
1823 	if (fwidth < 2 || fheight < 2) {
1824 		if (trace)
1825 			printf("Target area too small\n");
1826 		return (1);
1827 	}
1828 
1829 	if (trace)
1830 		printf("Image %ux%u -> %ux%u @%ux%u\n",
1831 		    png->width, png->height, fwidth, fheight, ux1, uy1);
1832 
1833 	rect.tr_begin.tp_col = ux1 / gfx_state.tg_font.vf_width;
1834 	rect.tr_begin.tp_row = uy1 / gfx_state.tg_font.vf_height;
1835 	rect.tr_end.tp_col = (ux1 + fwidth) / gfx_state.tg_font.vf_width;
1836 	rect.tr_end.tp_row = (uy1 + fheight) / gfx_state.tg_font.vf_height;
1837 
1838 	/*
1839 	 * mark area used in terminal
1840 	 */
1841 	if (!(flags & FL_PUTIMAGE_NOSCROLL))
1842 		term_image_display(&gfx_state, &rect);
1843 
1844 	if ((flags & FL_PUTIMAGE_BORDER))
1845 		gfx_fb_drawrect(ux1, uy1, ux2, uy2, 0);
1846 
1847 	data = malloc(fwidth * fheight * sizeof(*p));
1848 	p = (void *)data;
1849 	if (data == NULL) {
1850 		if (trace)
1851 			printf("Out of memory.\n");
1852 		return (1);
1853 	}
1854 
1855 	/*
1856 	 * Build image for our framebuffer.
1857 	 */
1858 
1859 	/* Helper to calculate the pixel index from the source png */
1860 #define	GETPIXEL(xx, yy)	(((yy) * png->width + (xx)) * png->bpp)
1861 
1862 	/*
1863 	 * For each of the x and y directions, calculate the number of pixels
1864 	 * in the source image that correspond to a single pixel in the target.
1865 	 * Use fixed-point arithmetic with 16-bits for each of the integer and
1866 	 * fractional parts.
1867 	 */
1868 	const uint32_t wcstep = ((png->width - 1) << 16) / (fwidth - 1);
1869 	const uint32_t hcstep = ((png->height - 1) << 16) / (fheight - 1);
1870 
1871 	rs = 8 - (fls(gfx_state.tg_fb.fb_mask_red) -
1872 	    ffs(gfx_state.tg_fb.fb_mask_red) + 1);
1873 	gs = 8 - (fls(gfx_state.tg_fb.fb_mask_green) -
1874 	    ffs(gfx_state.tg_fb.fb_mask_green) + 1);
1875 	bs = 8 - (fls(gfx_state.tg_fb.fb_mask_blue) -
1876 	    ffs(gfx_state.tg_fb.fb_mask_blue) + 1);
1877 
1878 	uint32_t hc = 0;
1879 	for (y = 0; y < fheight; y++) {
1880 		uint32_t hc2 = (hc >> 9) & 0x7f;
1881 		uint32_t hc1 = 0x80 - hc2;
1882 
1883 		uint32_t offset_y = hc >> 16;
1884 		uint32_t offset_y1 = offset_y + 1;
1885 
1886 		uint32_t wc = 0;
1887 		for (x = 0; x < fwidth; x++) {
1888 			uint32_t wc2 = (wc >> 9) & 0x7f;
1889 			uint32_t wc1 = 0x80 - wc2;
1890 
1891 			uint32_t offset_x = wc >> 16;
1892 			uint32_t offset_x1 = offset_x + 1;
1893 
1894 			/* Target pixel index */
1895 			j = y * fwidth + x;
1896 
1897 			if (!scale) {
1898 				i = GETPIXEL(x, y);
1899 				r = png->image[i];
1900 				g = png->image[i + 1];
1901 				b = png->image[i + 2];
1902 				a = png->image[i + 3];
1903 			} else {
1904 				uint8_t pixel[4];
1905 
1906 				uint32_t p00 = GETPIXEL(offset_x, offset_y);
1907 				uint32_t p01 = GETPIXEL(offset_x, offset_y1);
1908 				uint32_t p10 = GETPIXEL(offset_x1, offset_y);
1909 				uint32_t p11 = GETPIXEL(offset_x1, offset_y1);
1910 
1911 				/*
1912 				 * Given a 2x2 array of pixels in the source
1913 				 * image, combine them to produce a single
1914 				 * value for the pixel in the target image.
1915 				 * Each column of pixels is combined using
1916 				 * a weighted average where the top and bottom
1917 				 * pixels contribute hc1 and hc2 respectively.
1918 				 * The calculation for bottom pixel pB and
1919 				 * top pixel pT is:
1920 				 *   (pT * hc1 + pB * hc2) / (hc1 + hc2)
1921 				 * Once the values are determined for the two
1922 				 * columns of pixels, then the columns are
1923 				 * averaged together in the same way but using
1924 				 * wc1 and wc2 for the weightings.
1925 				 *
1926 				 * Since hc1 and hc2 are chosen so that
1927 				 * hc1 + hc2 == 128 (and same for wc1 + wc2),
1928 				 * the >> 14 below is a quick way to divide by
1929 				 * (hc1 + hc2) * (wc1 + wc2)
1930 				 */
1931 				for (i = 0; i < 4; i++)
1932 					pixel[i] = (
1933 					    (png->image[p00 + i] * hc1 +
1934 					    png->image[p01 + i] * hc2) * wc1 +
1935 					    (png->image[p10 + i] * hc1 +
1936 					    png->image[p11 + i] * hc2) * wc2)
1937 					    >> 14;
1938 
1939 				r = pixel[0];
1940 				g = pixel[1];
1941 				b = pixel[2];
1942 				a = pixel[3];
1943 			}
1944 
1945 			if (trace)
1946 				printf("r/g/b: %x/%x/%x\n", r, g, b);
1947 			/*
1948 			 * Rough colorspace reduction for 15/16 bit colors.
1949 			 */
1950 			p[j].Red = r >> rs;
1951                         p[j].Green = g >> gs;
1952                         p[j].Blue = b >> bs;
1953                         p[j].Reserved = a;
1954 
1955 			wc += wcstep;
1956 		}
1957 		hc += hcstep;
1958 	}
1959 
1960 	gfx_fb_cons_display(ux1, uy1, fwidth, fheight, data);
1961 	free(data);
1962 	return (0);
1963 }
1964 
1965 /*
1966  * Reset font flags to FONT_AUTO.
1967  */
1968 void
1969 reset_font_flags(void)
1970 {
1971 	struct fontlist *fl;
1972 
1973 	STAILQ_FOREACH(fl, &fonts, font_next) {
1974 		fl->font_flags = FONT_AUTO;
1975 	}
1976 }
1977 
1978 /* Return  w^2 + h^2 or 0, if the dimensions are unknown */
1979 static unsigned
1980 edid_diagonal_squared(void)
1981 {
1982 	unsigned w, h;
1983 
1984 	if (edid_info == NULL)
1985 		return (0);
1986 
1987 	w = edid_info->display.max_horizontal_image_size;
1988 	h = edid_info->display.max_vertical_image_size;
1989 
1990 	/* If either one is 0, we have aspect ratio, not size */
1991 	if (w == 0 || h == 0)
1992 		return (0);
1993 
1994 	/*
1995 	 * some monitors encode the aspect ratio instead of the physical size.
1996 	 */
1997 	if ((w == 16 && h == 9) || (w == 16 && h == 10) ||
1998 	    (w == 4 && h == 3) || (w == 5 && h == 4))
1999 		return (0);
2000 
2001 	/*
2002 	 * translate cm to inch, note we scale by 100 here.
2003 	 */
2004 	w = w * 100 / 254;
2005 	h = h * 100 / 254;
2006 
2007 	/* Return w^2 + h^2 */
2008 	return (w * w + h * h);
2009 }
2010 
2011 /*
2012  * calculate pixels per inch.
2013  */
2014 static unsigned
2015 gfx_get_ppi(void)
2016 {
2017 	unsigned dp, di;
2018 
2019 	di = edid_diagonal_squared();
2020 	if (di == 0)
2021 		return (0);
2022 
2023 	dp = gfx_state.tg_fb.fb_width *
2024 	    gfx_state.tg_fb.fb_width +
2025 	    gfx_state.tg_fb.fb_height *
2026 	    gfx_state.tg_fb.fb_height;
2027 
2028 	return (isqrt(dp / di));
2029 }
2030 
2031 /*
2032  * Calculate font size from density independent pixels (dp):
2033  * ((16dp * ppi) / 160) * display_factor.
2034  * Here we are using fixed constants: 1dp == 160 ppi and
2035  * display_factor 2.
2036  *
2037  * We are rounding font size up and are searching for font which is
2038  * not smaller than calculated size value.
2039  */
2040 static vt_font_bitmap_data_t *
2041 gfx_get_font(void)
2042 {
2043 	unsigned ppi, size;
2044 	vt_font_bitmap_data_t *font = NULL;
2045 	struct fontlist *fl, *next;
2046 
2047 	/* Text mode is not supported here. */
2048 	if (gfx_state.tg_fb_type == FB_TEXT)
2049 		return (NULL);
2050 
2051 	ppi = gfx_get_ppi();
2052 	if (ppi == 0)
2053 		return (NULL);
2054 
2055 	/*
2056 	 * We will search for 16dp font.
2057 	 * We are using scale up by 10 for roundup.
2058 	 */
2059 	size = (16 * ppi * 10) / 160;
2060 	/* Apply display factor 2.  */
2061 	size = roundup(size * 2, 10) / 10;
2062 
2063 	STAILQ_FOREACH(fl, &fonts, font_next) {
2064 		next = STAILQ_NEXT(fl, font_next);
2065 
2066 		/*
2067 		 * If this is last font or, if next font is smaller,
2068 		 * we have our font. Make sure, it actually is loaded.
2069 		 */
2070 		if (next == NULL || next->font_data->vfbd_height < size) {
2071 			font = fl->font_data;
2072 			if (font->vfbd_font == NULL ||
2073 			    fl->font_flags == FONT_RELOAD) {
2074 				if (fl->font_load != NULL &&
2075 				    fl->font_name != NULL)
2076 					font = fl->font_load(fl->font_name);
2077 			}
2078 			break;
2079 		}
2080 	}
2081 
2082 	return (font);
2083 }
2084 
2085 static vt_font_bitmap_data_t *
2086 set_font(teken_unit_t *rows, teken_unit_t *cols, teken_unit_t h, teken_unit_t w)
2087 {
2088 	vt_font_bitmap_data_t *font = NULL;
2089 	struct fontlist *fl;
2090 	unsigned height = h;
2091 	unsigned width = w;
2092 
2093 	/*
2094 	 * First check for manually loaded font.
2095 	 */
2096 	STAILQ_FOREACH(fl, &fonts, font_next) {
2097 		if (fl->font_flags == FONT_MANUAL) {
2098 			font = fl->font_data;
2099 			if (font->vfbd_font == NULL && fl->font_load != NULL &&
2100 			    fl->font_name != NULL) {
2101 				font = fl->font_load(fl->font_name);
2102 			}
2103 			if (font == NULL || font->vfbd_font == NULL)
2104 				font = NULL;
2105 			break;
2106 		}
2107 	}
2108 
2109 	if (font == NULL)
2110 		font = gfx_get_font();
2111 
2112 	if (font != NULL) {
2113 		*rows = height / font->vfbd_height;
2114 		*cols = width / font->vfbd_width;
2115 		return (font);
2116 	}
2117 
2118 	/*
2119 	 * Find best font for these dimensions, or use default.
2120 	 * If height >= VT_FB_MAX_HEIGHT and width >= VT_FB_MAX_WIDTH,
2121 	 * do not use smaller font than our DEFAULT_FONT_DATA.
2122 	 */
2123 	STAILQ_FOREACH(fl, &fonts, font_next) {
2124 		font = fl->font_data;
2125 		if ((*rows * font->vfbd_height <= height &&
2126 		    *cols * font->vfbd_width <= width) ||
2127 		    (height >= VT_FB_MAX_HEIGHT &&
2128 		    width >= VT_FB_MAX_WIDTH &&
2129 		    font->vfbd_height == DEFAULT_FONT_DATA.vfbd_height &&
2130 		    font->vfbd_width == DEFAULT_FONT_DATA.vfbd_width)) {
2131 			if (font->vfbd_font == NULL ||
2132 			    fl->font_flags == FONT_RELOAD) {
2133 				if (fl->font_load != NULL &&
2134 				    fl->font_name != NULL) {
2135 					font = fl->font_load(fl->font_name);
2136 				}
2137 				if (font == NULL)
2138 					continue;
2139 			}
2140 			*rows = height / font->vfbd_height;
2141 			*cols = width / font->vfbd_width;
2142 			break;
2143 		}
2144 		font = NULL;
2145 	}
2146 
2147 	if (font == NULL) {
2148 		/*
2149 		 * We have fonts sorted smallest last, try it before
2150 		 * falling back to builtin.
2151 		 */
2152 		fl = STAILQ_LAST(&fonts, fontlist, font_next);
2153 		if (fl != NULL && fl->font_load != NULL &&
2154 		    fl->font_name != NULL) {
2155 			font = fl->font_load(fl->font_name);
2156 		}
2157 		if (font == NULL)
2158 			font = &DEFAULT_FONT_DATA;
2159 
2160 		*rows = height / font->vfbd_height;
2161 		*cols = width / font->vfbd_width;
2162 	}
2163 
2164 	return (font);
2165 }
2166 
2167 static void
2168 cons_clear(void)
2169 {
2170 	char clear[] = { '\033', 'c' };
2171 
2172 	/* Reset terminal */
2173 	teken_input(&gfx_state.tg_teken, clear, sizeof(clear));
2174 	gfx_state.tg_functions->tf_param(&gfx_state, TP_SHOWCURSOR, 0);
2175 }
2176 
2177 void
2178 setup_font(teken_gfx_t *state, teken_unit_t height, teken_unit_t width)
2179 {
2180 	vt_font_bitmap_data_t *font_data;
2181 	teken_pos_t *tp = &state->tg_tp;
2182 	char env[8];
2183 	int i;
2184 
2185 	/*
2186 	 * set_font() will select a appropriate sized font for
2187 	 * the number of rows and columns selected.  If we don't
2188 	 * have a font that will fit, then it will use the
2189 	 * default builtin font and adjust the rows and columns
2190 	 * to fit on the screen.
2191 	 */
2192 	font_data = set_font(&tp->tp_row, &tp->tp_col, height, width);
2193 
2194         if (font_data == NULL)
2195 		panic("out of memory");
2196 
2197 	for (i = 0; i < VFNT_MAPS; i++) {
2198 		state->tg_font.vf_map[i] =
2199 		    font_data->vfbd_font->vf_map[i];
2200 		state->tg_font.vf_map_count[i] =
2201 		    font_data->vfbd_font->vf_map_count[i];
2202 	}
2203 
2204 	state->tg_font.vf_bytes = font_data->vfbd_font->vf_bytes;
2205 	state->tg_font.vf_height = font_data->vfbd_font->vf_height;
2206 	state->tg_font.vf_width = font_data->vfbd_font->vf_width;
2207 
2208 	snprintf(env, sizeof (env), "%ux%u",
2209 	    state->tg_font.vf_width, state->tg_font.vf_height);
2210 	env_setenv("screen.font", EV_VOLATILE | EV_NOHOOK,
2211 	    env, font_set, env_nounset);
2212 }
2213 
2214 /* Binary search for the glyph. Return 0 if not found. */
2215 static uint16_t
2216 font_bisearch(const vfnt_map_t *map, uint32_t len, teken_char_t src)
2217 {
2218 	unsigned min, mid, max;
2219 
2220 	min = 0;
2221 	max = len - 1;
2222 
2223 	/* Empty font map. */
2224 	if (len == 0)
2225 		return (0);
2226 	/* Character below minimal entry. */
2227 	if (src < map[0].vfm_src)
2228 		return (0);
2229 	/* Optimization: ASCII characters occur very often. */
2230 	if (src <= map[0].vfm_src + map[0].vfm_len)
2231 		return (src - map[0].vfm_src + map[0].vfm_dst);
2232 	/* Character above maximum entry. */
2233 	if (src > map[max].vfm_src + map[max].vfm_len)
2234 		return (0);
2235 
2236 	/* Binary search. */
2237 	while (max >= min) {
2238 		mid = (min + max) / 2;
2239 		if (src < map[mid].vfm_src)
2240 			max = mid - 1;
2241 		else if (src > map[mid].vfm_src + map[mid].vfm_len)
2242 			min = mid + 1;
2243 		else
2244 			return (src - map[mid].vfm_src + map[mid].vfm_dst);
2245 	}
2246 
2247 	return (0);
2248 }
2249 
2250 /*
2251  * Return glyph bitmap. If glyph is not found, we will return bitmap
2252  * for the first (offset 0) glyph.
2253  */
2254 uint8_t *
2255 font_lookup(const struct vt_font *vf, teken_char_t c, const teken_attr_t *a)
2256 {
2257 	uint16_t dst;
2258 	size_t stride;
2259 
2260 	/* Substitute bold with normal if not found. */
2261 	if (a->ta_format & TF_BOLD) {
2262 		dst = font_bisearch(vf->vf_map[VFNT_MAP_BOLD],
2263 		    vf->vf_map_count[VFNT_MAP_BOLD], c);
2264 		if (dst != 0)
2265 			goto found;
2266 	}
2267 	dst = font_bisearch(vf->vf_map[VFNT_MAP_NORMAL],
2268 	    vf->vf_map_count[VFNT_MAP_NORMAL], c);
2269 
2270 found:
2271 	stride = howmany(vf->vf_width, 8) * vf->vf_height;
2272 	return (&vf->vf_bytes[dst * stride]);
2273 }
2274 
2275 static int
2276 load_mapping(int fd, struct vt_font *fp, int n)
2277 {
2278 	size_t i, size;
2279 	ssize_t rv;
2280 	vfnt_map_t *mp;
2281 
2282 	if (fp->vf_map_count[n] == 0)
2283 		return (0);
2284 
2285 	size = fp->vf_map_count[n] * sizeof(*mp);
2286 	mp = malloc(size);
2287 	if (mp == NULL)
2288 		return (ENOMEM);
2289 	fp->vf_map[n] = mp;
2290 
2291 	rv = read(fd, mp, size);
2292 	if (rv < 0 || (size_t)rv != size) {
2293 		free(fp->vf_map[n]);
2294 		fp->vf_map[n] = NULL;
2295 		return (EIO);
2296 	}
2297 
2298 	for (i = 0; i < fp->vf_map_count[n]; i++) {
2299 		mp[i].vfm_src = be32toh(mp[i].vfm_src);
2300 		mp[i].vfm_dst = be16toh(mp[i].vfm_dst);
2301 		mp[i].vfm_len = be16toh(mp[i].vfm_len);
2302 	}
2303 	return (0);
2304 }
2305 
2306 static int
2307 builtin_mapping(struct vt_font *fp, int n)
2308 {
2309 	size_t size;
2310 	struct vfnt_map *mp;
2311 
2312 	if (n >= VFNT_MAPS)
2313 		return (EINVAL);
2314 
2315 	if (fp->vf_map_count[n] == 0)
2316 		return (0);
2317 
2318 	size = fp->vf_map_count[n] * sizeof(*mp);
2319 	mp = malloc(size);
2320 	if (mp == NULL)
2321 		return (ENOMEM);
2322 	fp->vf_map[n] = mp;
2323 
2324 	memcpy(mp, DEFAULT_FONT_DATA.vfbd_font->vf_map[n], size);
2325 	return (0);
2326 }
2327 
2328 /*
2329  * Load font from builtin or from file.
2330  * We do need special case for builtin because the builtin font glyphs
2331  * are compressed and we do need to uncompress them.
2332  * Having single load_font() for both cases will help us to simplify
2333  * font switch handling.
2334  */
2335 static vt_font_bitmap_data_t *
2336 load_font(char *path)
2337 {
2338 	int fd, i;
2339 	uint32_t glyphs;
2340 	struct font_header fh;
2341 	struct fontlist *fl;
2342 	vt_font_bitmap_data_t *bp;
2343 	struct vt_font *fp;
2344 	size_t size;
2345 	ssize_t rv;
2346 
2347 	/* Get our entry from the font list. */
2348 	STAILQ_FOREACH(fl, &fonts, font_next) {
2349 		if (strcmp(fl->font_name, path) == 0)
2350 			break;
2351 	}
2352 	if (fl == NULL)
2353 		return (NULL);	/* Should not happen. */
2354 
2355 	bp = fl->font_data;
2356 	if (bp->vfbd_font != NULL && fl->font_flags != FONT_RELOAD)
2357 		return (bp);
2358 
2359 	fd = -1;
2360 	/*
2361 	 * Special case for builtin font.
2362 	 * Builtin font is the very first font we load, we do not have
2363 	 * previous loads to be released.
2364 	 */
2365 	if (fl->font_flags == FONT_BUILTIN) {
2366 		if ((fp = calloc(1, sizeof(struct vt_font))) == NULL)
2367 			return (NULL);
2368 
2369 		fp->vf_width = DEFAULT_FONT_DATA.vfbd_width;
2370 		fp->vf_height = DEFAULT_FONT_DATA.vfbd_height;
2371 
2372 		fp->vf_bytes = malloc(DEFAULT_FONT_DATA.vfbd_uncompressed_size);
2373 		if (fp->vf_bytes == NULL) {
2374 			free(fp);
2375 			return (NULL);
2376 		}
2377 
2378 		bp->vfbd_uncompressed_size =
2379 		    DEFAULT_FONT_DATA.vfbd_uncompressed_size;
2380 		bp->vfbd_compressed_size =
2381 		    DEFAULT_FONT_DATA.vfbd_compressed_size;
2382 
2383 		if (lz4_decompress(DEFAULT_FONT_DATA.vfbd_compressed_data,
2384 		    fp->vf_bytes,
2385 		    DEFAULT_FONT_DATA.vfbd_compressed_size,
2386 		    DEFAULT_FONT_DATA.vfbd_uncompressed_size, 0) != 0) {
2387 			free(fp->vf_bytes);
2388 			free(fp);
2389 			return (NULL);
2390 		}
2391 
2392 		for (i = 0; i < VFNT_MAPS; i++) {
2393 			fp->vf_map_count[i] =
2394 			    DEFAULT_FONT_DATA.vfbd_font->vf_map_count[i];
2395 			if (builtin_mapping(fp, i) != 0)
2396 				goto free_done;
2397 		}
2398 
2399 		bp->vfbd_font = fp;
2400 		return (bp);
2401 	}
2402 
2403 	fd = open(path, O_RDONLY);
2404 	if (fd < 0)
2405 		return (NULL);
2406 
2407 	size = sizeof(fh);
2408 	rv = read(fd, &fh, size);
2409 	if (rv < 0 || (size_t)rv != size) {
2410 		bp = NULL;
2411 		goto done;
2412 	}
2413 	if (memcmp(fh.fh_magic, FONT_HEADER_MAGIC, sizeof(fh.fh_magic)) != 0) {
2414 		bp = NULL;
2415 		goto done;
2416 	}
2417 	if ((fp = calloc(1, sizeof(struct vt_font))) == NULL) {
2418 		bp = NULL;
2419 		goto done;
2420 	}
2421 	for (i = 0; i < VFNT_MAPS; i++)
2422 		fp->vf_map_count[i] = be32toh(fh.fh_map_count[i]);
2423 
2424 	glyphs = be32toh(fh.fh_glyph_count);
2425 	fp->vf_width = fh.fh_width;
2426 	fp->vf_height = fh.fh_height;
2427 
2428 	size = howmany(fp->vf_width, 8) * fp->vf_height * glyphs;
2429 	bp->vfbd_uncompressed_size = size;
2430 	if ((fp->vf_bytes = malloc(size)) == NULL)
2431 		goto free_done;
2432 
2433 	rv = read(fd, fp->vf_bytes, size);
2434 	if (rv < 0 || (size_t)rv != size)
2435 		goto free_done;
2436 	for (i = 0; i < VFNT_MAPS; i++) {
2437 		if (load_mapping(fd, fp, i) != 0)
2438 			goto free_done;
2439 	}
2440 
2441 	/*
2442 	 * Reset builtin flag now as we have full font loaded.
2443 	 */
2444 	if (fl->font_flags == FONT_BUILTIN)
2445 		fl->font_flags = FONT_AUTO;
2446 
2447 	/*
2448 	 * Release previously loaded entries. We can do this now, as
2449 	 * the new font is loaded. Note, there can be no console
2450 	 * output till the new font is in place and teken is notified.
2451 	 * We do need to keep fl->font_data for glyph dimensions.
2452 	 */
2453 	STAILQ_FOREACH(fl, &fonts, font_next) {
2454 		if (fl->font_data->vfbd_font == NULL)
2455 			continue;
2456 
2457 		for (i = 0; i < VFNT_MAPS; i++)
2458 			free(fl->font_data->vfbd_font->vf_map[i]);
2459 		free(fl->font_data->vfbd_font->vf_bytes);
2460 		free(fl->font_data->vfbd_font);
2461 		fl->font_data->vfbd_font = NULL;
2462 	}
2463 
2464 	bp->vfbd_font = fp;
2465 	bp->vfbd_compressed_size = 0;
2466 
2467 done:
2468 	if (fd != -1)
2469 		close(fd);
2470 	return (bp);
2471 
2472 free_done:
2473 	for (i = 0; i < VFNT_MAPS; i++)
2474 		free(fp->vf_map[i]);
2475 	free(fp->vf_bytes);
2476 	free(fp);
2477 	bp = NULL;
2478 	goto done;
2479 }
2480 
2481 struct name_entry {
2482 	char			*n_name;
2483 	SLIST_ENTRY(name_entry)	n_entry;
2484 };
2485 
2486 SLIST_HEAD(name_list, name_entry);
2487 
2488 /* Read font names from index file. */
2489 static struct name_list *
2490 read_list(char *fonts)
2491 {
2492 	struct name_list *nl;
2493 	struct name_entry *np;
2494 	char *dir, *ptr;
2495 	char buf[PATH_MAX];
2496 	int fd, len;
2497 
2498 	TSENTER();
2499 
2500 	dir = strdup(fonts);
2501 	if (dir == NULL)
2502 		return (NULL);
2503 
2504 	ptr = strrchr(dir, '/');
2505 	*ptr = '\0';
2506 
2507 	fd = open(fonts, O_RDONLY);
2508 	if (fd < 0)
2509 		return (NULL);
2510 
2511 	nl = malloc(sizeof(*nl));
2512 	if (nl == NULL) {
2513 		close(fd);
2514 		return (nl);
2515 	}
2516 
2517 	SLIST_INIT(nl);
2518 	while ((len = fgetstr(buf, sizeof (buf), fd)) >= 0) {
2519 		if (*buf == '#' || *buf == '\0')
2520 			continue;
2521 
2522 		if (bcmp(buf, "MENU", 4) == 0)
2523 			continue;
2524 
2525 		if (bcmp(buf, "FONT", 4) == 0)
2526 			continue;
2527 
2528 		ptr = strchr(buf, ':');
2529 		if (ptr == NULL)
2530 			continue;
2531 		else
2532 			*ptr = '\0';
2533 
2534 		np = malloc(sizeof(*np));
2535 		if (np == NULL) {
2536 			close(fd);
2537 			return (nl);	/* return what we have */
2538 		}
2539 		if (asprintf(&np->n_name, "%s/%s", dir, buf) < 0) {
2540 			free(np);
2541 			close(fd);
2542 			return (nl);    /* return what we have */
2543 		}
2544 		SLIST_INSERT_HEAD(nl, np, n_entry);
2545 	}
2546 	close(fd);
2547 	TSEXIT();
2548 	return (nl);
2549 }
2550 
2551 /*
2552  * Read the font properties and insert new entry into the list.
2553  * The font list is built in descending order.
2554  */
2555 static bool
2556 insert_font(char *name, FONT_FLAGS flags)
2557 {
2558 	struct font_header fh;
2559 	struct fontlist *fp, *previous, *entry, *next;
2560 	size_t size;
2561 	ssize_t rv;
2562 	int fd;
2563 	char *font_name;
2564 
2565 	TSENTER();
2566 
2567 	font_name = NULL;
2568 	if (flags == FONT_BUILTIN) {
2569 		/*
2570 		 * We only install builtin font once, while setting up
2571 		 * initial console. Since this will happen very early,
2572 		 * we assume asprintf will not fail. Once we have access to
2573 		 * files, the builtin font will be replaced by font loaded
2574 		 * from file.
2575 		 */
2576 		if (!STAILQ_EMPTY(&fonts))
2577 			return (false);
2578 
2579 		fh.fh_width = DEFAULT_FONT_DATA.vfbd_width;
2580 		fh.fh_height = DEFAULT_FONT_DATA.vfbd_height;
2581 
2582 		(void) asprintf(&font_name, "%dx%d",
2583 		    DEFAULT_FONT_DATA.vfbd_width,
2584 		    DEFAULT_FONT_DATA.vfbd_height);
2585 	} else {
2586 		fd = open(name, O_RDONLY);
2587 		if (fd < 0)
2588 			return (false);
2589 		rv = read(fd, &fh, sizeof(fh));
2590 		close(fd);
2591 		if (rv < 0 || (size_t)rv != sizeof(fh))
2592 			return (false);
2593 
2594 		if (memcmp(fh.fh_magic, FONT_HEADER_MAGIC,
2595 		    sizeof(fh.fh_magic)) != 0)
2596 			return (false);
2597 		font_name = strdup(name);
2598 	}
2599 
2600 	if (font_name == NULL)
2601 		return (false);
2602 
2603 	/*
2604 	 * If we have an entry with the same glyph dimensions, replace
2605 	 * the file name and mark us. We only support unique dimensions.
2606 	 */
2607 	STAILQ_FOREACH(entry, &fonts, font_next) {
2608 		if (fh.fh_width == entry->font_data->vfbd_width &&
2609 		    fh.fh_height == entry->font_data->vfbd_height) {
2610 			free(entry->font_name);
2611 			entry->font_name = font_name;
2612 			entry->font_flags = FONT_RELOAD;
2613 			TSEXIT();
2614 			return (true);
2615 		}
2616 	}
2617 
2618 	fp = calloc(sizeof(*fp), 1);
2619 	if (fp == NULL) {
2620 		free(font_name);
2621 		return (false);
2622 	}
2623 	fp->font_data = calloc(sizeof(*fp->font_data), 1);
2624 	if (fp->font_data == NULL) {
2625 		free(font_name);
2626 		free(fp);
2627 		return (false);
2628 	}
2629 	fp->font_name = font_name;
2630 	fp->font_flags = flags;
2631 	fp->font_load = load_font;
2632 	fp->font_data->vfbd_width = fh.fh_width;
2633 	fp->font_data->vfbd_height = fh.fh_height;
2634 
2635 	if (STAILQ_EMPTY(&fonts)) {
2636 		STAILQ_INSERT_HEAD(&fonts, fp, font_next);
2637 		TSEXIT();
2638 		return (true);
2639 	}
2640 
2641 	previous = NULL;
2642 	size = fp->font_data->vfbd_width * fp->font_data->vfbd_height;
2643 
2644 	STAILQ_FOREACH(entry, &fonts, font_next) {
2645 		vt_font_bitmap_data_t *bd;
2646 
2647 		bd = entry->font_data;
2648 		/* Should fp be inserted before the entry? */
2649 		if (size > bd->vfbd_width * bd->vfbd_height) {
2650 			if (previous == NULL) {
2651 				STAILQ_INSERT_HEAD(&fonts, fp, font_next);
2652 			} else {
2653 				STAILQ_INSERT_AFTER(&fonts, previous, fp,
2654 				    font_next);
2655 			}
2656 			TSEXIT();
2657 			return (true);
2658 		}
2659 		next = STAILQ_NEXT(entry, font_next);
2660 		if (next == NULL ||
2661 		    size > next->font_data->vfbd_width *
2662 		    next->font_data->vfbd_height) {
2663 			STAILQ_INSERT_AFTER(&fonts, entry, fp, font_next);
2664 			TSEXIT();
2665 			return (true);
2666 		}
2667 		previous = entry;
2668 	}
2669 	TSEXIT();
2670 	return (true);
2671 }
2672 
2673 static int
2674 font_set(struct env_var *ev __unused, int flags __unused, const void *value)
2675 {
2676 	struct fontlist *fl;
2677 	char *eptr;
2678 	unsigned long x = 0, y = 0;
2679 
2680 	/*
2681 	 * Attempt to extract values from "XxY" string. In case of error,
2682 	 * we have unmaching glyph dimensions and will just output the
2683 	 * available values.
2684 	 */
2685 	if (value != NULL) {
2686 		x = strtoul(value, &eptr, 10);
2687 		if (*eptr == 'x')
2688 			y = strtoul(eptr + 1, &eptr, 10);
2689 	}
2690 	STAILQ_FOREACH(fl, &fonts, font_next) {
2691 		if (fl->font_data->vfbd_width == x &&
2692 		    fl->font_data->vfbd_height == y)
2693 			break;
2694 	}
2695 	if (fl != NULL) {
2696 		/* Reset any FONT_MANUAL flag. */
2697 		reset_font_flags();
2698 
2699 		/* Mark this font manually loaded */
2700 		fl->font_flags = FONT_MANUAL;
2701 		cons_update_mode(gfx_state.tg_fb_type != FB_TEXT);
2702 		return (CMD_OK);
2703 	}
2704 
2705 	printf("Available fonts:\n");
2706 	STAILQ_FOREACH(fl, &fonts, font_next) {
2707 		printf("    %dx%d\n", fl->font_data->vfbd_width,
2708 		    fl->font_data->vfbd_height);
2709 	}
2710 	return (CMD_OK);
2711 }
2712 
2713 void
2714 bios_text_font(bool use_vga_font)
2715 {
2716 	if (use_vga_font)
2717 		(void) insert_font(VGA_8X16_FONT, FONT_MANUAL);
2718 	else
2719 		(void) insert_font(DEFAULT_8X16_FONT, FONT_MANUAL);
2720 }
2721 
2722 void
2723 autoload_font(bool bios)
2724 {
2725 	struct name_list *nl;
2726 	struct name_entry *np;
2727 
2728 	TSENTER();
2729 
2730 	nl = read_list("/boot/fonts/INDEX.fonts");
2731 	if (nl == NULL)
2732 		return;
2733 
2734 	while (!SLIST_EMPTY(nl)) {
2735 		np = SLIST_FIRST(nl);
2736 		SLIST_REMOVE_HEAD(nl, n_entry);
2737 		if (insert_font(np->n_name, FONT_AUTO) == false)
2738 			printf("failed to add font: %s\n", np->n_name);
2739 		free(np->n_name);
2740 		free(np);
2741 	}
2742 
2743 	/*
2744 	 * If vga text mode was requested, load vga.font (8x16 bold) font.
2745 	 */
2746 	if (bios) {
2747 		bios_text_font(true);
2748 	}
2749 
2750 	(void) cons_update_mode(gfx_state.tg_fb_type != FB_TEXT);
2751 
2752 	TSEXIT();
2753 }
2754 
2755 COMMAND_SET(load_font, "loadfont", "load console font from file", command_font);
2756 
2757 static int
2758 command_font(int argc, char *argv[])
2759 {
2760 	int i, c, rc;
2761 	struct fontlist *fl;
2762 	vt_font_bitmap_data_t *bd;
2763 	bool list;
2764 
2765 	list = false;
2766 	optind = 1;
2767 	optreset = 1;
2768 	rc = CMD_OK;
2769 
2770 	while ((c = getopt(argc, argv, "l")) != -1) {
2771 		switch (c) {
2772 		case 'l':
2773 			list = true;
2774 			break;
2775 		case '?':
2776 		default:
2777 			return (CMD_ERROR);
2778 		}
2779 	}
2780 
2781 	argc -= optind;
2782 	argv += optind;
2783 
2784 	if (argc > 1 || (list && argc != 0)) {
2785 		printf("Usage: loadfont [-l] | [file.fnt]\n");
2786 		return (CMD_ERROR);
2787 	}
2788 
2789 	if (list) {
2790 		STAILQ_FOREACH(fl, &fonts, font_next) {
2791 			printf("font %s: %dx%d%s\n", fl->font_name,
2792 			    fl->font_data->vfbd_width,
2793 			    fl->font_data->vfbd_height,
2794 			    fl->font_data->vfbd_font == NULL? "" : " loaded");
2795 		}
2796 		return (CMD_OK);
2797 	}
2798 
2799 	/* Clear scren */
2800 	cons_clear();
2801 
2802 	if (argc == 1) {
2803 		char *name = argv[0];
2804 
2805 		if (insert_font(name, FONT_MANUAL) == false) {
2806 			printf("loadfont error: failed to load: %s\n", name);
2807 			return (CMD_ERROR);
2808 		}
2809 
2810 		(void) cons_update_mode(gfx_state.tg_fb_type != FB_TEXT);
2811 		return (CMD_OK);
2812 	}
2813 
2814 	if (argc == 0) {
2815 		/*
2816 		 * Walk entire font list, release any loaded font, and set
2817 		 * autoload flag. The font list does have at least the builtin
2818 		 * default font.
2819 		 */
2820 		STAILQ_FOREACH(fl, &fonts, font_next) {
2821 			if (fl->font_data->vfbd_font != NULL) {
2822 
2823 				bd = fl->font_data;
2824 				/*
2825 				 * Note the setup_font() is releasing
2826 				 * font bytes.
2827 				 */
2828 				for (i = 0; i < VFNT_MAPS; i++)
2829 					free(bd->vfbd_font->vf_map[i]);
2830 				free(fl->font_data->vfbd_font);
2831 				fl->font_data->vfbd_font = NULL;
2832 				fl->font_data->vfbd_uncompressed_size = 0;
2833 				fl->font_flags = FONT_AUTO;
2834 			}
2835 		}
2836 		(void) cons_update_mode(gfx_state.tg_fb_type != FB_TEXT);
2837 	}
2838 	return (rc);
2839 }
2840 
2841 bool
2842 gfx_get_edid_resolution(struct vesa_edid_info *edid, edid_res_list_t *res)
2843 {
2844 	struct resolution *rp, *p;
2845 
2846 	/*
2847 	 * Walk detailed timings tables (4).
2848 	 */
2849 	if ((edid->display.supported_features
2850 	    & EDID_FEATURE_PREFERRED_TIMING_MODE) != 0) {
2851 		/* Walk detailed timing descriptors (4) */
2852 		for (int i = 0; i < DET_TIMINGS; i++) {
2853 			/*
2854 			 * Reserved value 0 is not used for display descriptor.
2855 			 */
2856 			if (edid->detailed_timings[i].pixel_clock == 0)
2857 				continue;
2858 			if ((rp = malloc(sizeof(*rp))) == NULL)
2859 				continue;
2860 			rp->width = GET_EDID_INFO_WIDTH(edid, i);
2861 			rp->height = GET_EDID_INFO_HEIGHT(edid, i);
2862 			if (rp->width > 0 && rp->width <= EDID_MAX_PIXELS &&
2863 			    rp->height > 0 && rp->height <= EDID_MAX_LINES)
2864 				TAILQ_INSERT_TAIL(res, rp, next);
2865 			else
2866 				free(rp);
2867 		}
2868 	}
2869 
2870 	/*
2871 	 * Walk standard timings list (8).
2872 	 */
2873 	for (int i = 0; i < STD_TIMINGS; i++) {
2874 		/* Is this field unused? */
2875 		if (edid->standard_timings[i] == 0x0101)
2876 			continue;
2877 
2878 		if ((rp = malloc(sizeof(*rp))) == NULL)
2879 			continue;
2880 
2881 		rp->width = HSIZE(edid->standard_timings[i]);
2882 		switch (RATIO(edid->standard_timings[i])) {
2883 		case RATIO1_1:
2884 			rp->height = HSIZE(edid->standard_timings[i]);
2885 			if (edid->header.version > 1 ||
2886 			    edid->header.revision > 2) {
2887 				rp->height = rp->height * 10 / 16;
2888 			}
2889 			break;
2890 		case RATIO4_3:
2891 			rp->height = HSIZE(edid->standard_timings[i]) * 3 / 4;
2892 			break;
2893 		case RATIO5_4:
2894 			rp->height = HSIZE(edid->standard_timings[i]) * 4 / 5;
2895 			break;
2896 		case RATIO16_9:
2897 			rp->height = HSIZE(edid->standard_timings[i]) * 9 / 16;
2898 			break;
2899 		}
2900 
2901 		/*
2902 		 * Create resolution list in decreasing order, except keep
2903 		 * first entry (preferred timing mode).
2904 		 */
2905 		TAILQ_FOREACH(p, res, next) {
2906 			if (p->width * p->height < rp->width * rp->height) {
2907 				/* Keep preferred mode first */
2908 				if (TAILQ_FIRST(res) == p)
2909 					TAILQ_INSERT_AFTER(res, p, rp, next);
2910 				else
2911 					TAILQ_INSERT_BEFORE(p, rp, next);
2912 				break;
2913 			}
2914 			if (TAILQ_NEXT(p, next) == NULL) {
2915 				TAILQ_INSERT_TAIL(res, rp, next);
2916 				break;
2917 			}
2918 		}
2919 	}
2920 	return (!TAILQ_EMPTY(res));
2921 }
2922 
2923 vm_offset_t
2924 build_font_module(vm_offset_t addr)
2925 {
2926 	vt_font_bitmap_data_t *bd;
2927 	struct vt_font *fd;
2928 	struct preloaded_file *fp;
2929 	size_t size;
2930 	uint32_t checksum;
2931 	int i;
2932 	struct font_info fi;
2933 	struct fontlist *fl;
2934 	uint64_t fontp;
2935 
2936 	if (STAILQ_EMPTY(&fonts))
2937 		return (addr);
2938 
2939 	/* We can't load first */
2940 	if ((file_findfile(NULL, NULL)) == NULL) {
2941 		printf("Can not load font module: %s\n",
2942 		    "the kernel is not loaded");
2943 		return (addr);
2944 	}
2945 
2946 	/* helper pointers */
2947 	bd = NULL;
2948 	STAILQ_FOREACH(fl, &fonts, font_next) {
2949 		if (gfx_state.tg_font.vf_width == fl->font_data->vfbd_width &&
2950 		    gfx_state.tg_font.vf_height == fl->font_data->vfbd_height) {
2951 			/*
2952 			 * Kernel does have better built in font.
2953 			 */
2954 			if (fl->font_flags == FONT_BUILTIN)
2955 				return (addr);
2956 
2957 			bd = fl->font_data;
2958 			break;
2959 		}
2960 	}
2961 	if (bd == NULL)
2962 		return (addr);
2963 	fd = bd->vfbd_font;
2964 
2965 	fi.fi_width = fd->vf_width;
2966 	checksum = fi.fi_width;
2967 	fi.fi_height = fd->vf_height;
2968 	checksum += fi.fi_height;
2969 	fi.fi_bitmap_size = bd->vfbd_uncompressed_size;
2970 	checksum += fi.fi_bitmap_size;
2971 
2972 	size = roundup2(sizeof (struct font_info), 8);
2973 	for (i = 0; i < VFNT_MAPS; i++) {
2974 		fi.fi_map_count[i] = fd->vf_map_count[i];
2975 		checksum += fi.fi_map_count[i];
2976 		size += fd->vf_map_count[i] * sizeof (struct vfnt_map);
2977 		size += roundup2(size, 8);
2978 	}
2979 	size += bd->vfbd_uncompressed_size;
2980 
2981 	fi.fi_checksum = -checksum;
2982 
2983 	fp = file_findfile(NULL, "elf kernel");
2984 	if (fp == NULL)
2985 		fp = file_findfile(NULL, "elf64 kernel");
2986 	if (fp == NULL)
2987 		panic("can't find kernel file");
2988 
2989 	fontp = addr;
2990 	addr += archsw.arch_copyin(&fi, addr, sizeof (struct font_info));
2991 	addr = roundup2(addr, 8);
2992 
2993 	/* Copy maps. */
2994 	for (i = 0; i < VFNT_MAPS; i++) {
2995 		if (fd->vf_map_count[i] != 0) {
2996 			addr += archsw.arch_copyin(fd->vf_map[i], addr,
2997 			    fd->vf_map_count[i] * sizeof (struct vfnt_map));
2998 			addr = roundup2(addr, 8);
2999 		}
3000 	}
3001 
3002 	/* Copy the bitmap. */
3003 	addr += archsw.arch_copyin(fd->vf_bytes, addr, fi.fi_bitmap_size);
3004 
3005 	/* Looks OK so far; populate control structure */
3006 	file_addmetadata(fp, MODINFOMD_FONT, sizeof(fontp), &fontp);
3007 	return (addr);
3008 }
3009