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