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