1/*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source.  A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12/*
13 * Copyright 2016 Toomas Soome <tsoome@me.com>
14 * Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
15 */
16
17/*
18 * Common functions to implement graphical framebuffer support for console.
19 */
20
21#include <sys/cdefs.h>
22#include <sys/param.h>
23#include <stand.h>
24#if	defined(EFI)
25#include <efi.h>
26#include <efilib.h>
27#else
28#include <btxv86.h>
29#endif
30#include <sys/tem_impl.h>
31#include <sys/consplat.h>
32#include <sys/visual_io.h>
33#include <sys/multiboot2.h>
34#include <sys/font.h>
35#include <sys/rgb.h>
36#include <sys/endian.h>
37#include <gfx_fb.h>
38#include <pnglite.h>
39#include <bootstrap.h>
40#include <lz4.h>
41
42/* VGA text mode does use bold font. */
43#if !defined(VGA_8X16_FONT)
44#define	VGA_8X16_FONT		"/boot/fonts/8x16b.fnt"
45#endif
46#if !defined(DEFAULT_8X16_FONT)
47#define	DEFAULT_8X16_FONT	"/boot/fonts/8x16.fnt"
48#endif
49
50/*
51 * Global framebuffer struct, to be updated with mode changes.
52 */
53multiboot_tag_framebuffer_t gfx_fb;
54
55/* To support setenv, keep track of inverses and colors. */
56static int gfx_inverse = 0;
57static int gfx_inverse_screen = 0;
58static uint8_t gfx_fg = DEFAULT_ANSI_FOREGROUND;
59static uint8_t gfx_bg = DEFAULT_ANSI_BACKGROUND;
60#if	defined(EFI)
61static EFI_GRAPHICS_OUTPUT_BLT_PIXEL *GlyphBuffer;
62static size_t GlyphBufferSize;
63#endif
64
65static int gfx_fb_cons_clear(struct vis_consclear *);
66static void gfx_fb_cons_copy(struct vis_conscopy *);
67static void gfx_fb_cons_display(struct vis_consdisplay *);
68
69#if	defined(EFI)
70static int gfx_gop_cons_clear(uint32_t data, uint32_t width, uint32_t height);
71static void gfx_gop_cons_copy(struct vis_conscopy *);
72static void gfx_gop_cons_display(struct vis_consdisplay *);
73static void gfx_gop_display_cursor(struct vis_conscursor *);
74#endif
75static int gfx_bm_cons_clear(uint32_t data, uint32_t width, uint32_t height);
76static void gfx_bm_cons_copy(struct vis_conscopy *);
77static void gfx_bm_cons_display(struct vis_consdisplay *);
78static void gfx_bm_display_cursor(struct vis_conscursor *);
79
80static bool insert_font(char *, FONT_FLAGS);
81
82/*
83 * Set default operations to use bitmap based implementation.
84 * In case of UEFI, if GOP is available, we will switch to GOP based
85 * implementation.
86 *
87 * Also note, for UEFI we do attempt to boost the execution by setting
88 * Task Priority Level (TPL) to TPL_NOTIFY, which is highest priority
89 * usable in application.
90 */
91struct gfx_fb_ops {
92	int (*gfx_cons_clear)(uint32_t, uint32_t, uint32_t);
93	void (*gfx_cons_copy)(struct vis_conscopy *);
94	void (*gfx_cons_display)(struct vis_consdisplay *);
95	void (*gfx_cons_display_cursor)(struct vis_conscursor *);
96} gfx_fb_ops = {
97	.gfx_cons_clear = gfx_bm_cons_clear,
98	.gfx_cons_copy = gfx_bm_cons_copy,
99	.gfx_cons_display = gfx_bm_cons_display,
100	.gfx_cons_display_cursor = gfx_bm_display_cursor
101};
102
103/*
104 * Translate platform specific FB address.
105 */
106static uint8_t *
107gfx_get_fb_address(void)
108{
109#if	defined(EFI)
110	return ((uint8_t *)(uintptr_t)
111	    gfx_fb.framebuffer_common.framebuffer_addr);
112#else
113	return ((uint8_t *)PTOV((uint32_t)
114	    gfx_fb.framebuffer_common.framebuffer_addr & 0xffffffff));
115#endif
116}
117
118/*
119 * Generic platform callbacks for tem.
120 */
121void
122plat_tem_get_prom_font_size(int *charheight, int *windowtop)
123{
124	*charheight = 0;
125	*windowtop = 0;
126}
127
128void
129plat_tem_get_colors(uint8_t *fg, uint8_t *bg)
130{
131	*fg = gfx_fg;
132	*bg = gfx_bg;
133}
134
135void
136plat_tem_get_inverses(int *inverse, int *inverse_screen)
137{
138	*inverse = gfx_inverse;
139	*inverse_screen = gfx_inverse_screen;
140}
141
142/*
143 * Utility function to parse gfx mode line strings.
144 */
145bool
146gfx_parse_mode_str(char *str, int *x, int *y, int *depth)
147{
148	char *p, *end;
149
150	errno = 0;
151	p = str;
152	*x = strtoul(p, &end, 0);
153	if (*x == 0 || errno != 0)
154		return (false);
155	if (*end != 'x')
156		return (false);
157	p = end + 1;
158	*y = strtoul(p, &end, 0);
159	if (*y == 0 || errno != 0)
160		return (false);
161	if (*end != 'x') {
162		*depth = -1;    /* auto select */
163	} else {
164		p = end + 1;
165		*depth = strtoul(p, &end, 0);
166		if (*depth == 0 || errno != 0 || *end != '\0')
167			return (false);
168	}
169
170	return (true);
171}
172
173/*
174 * Support for color mapping.
175 */
176uint32_t
177gfx_fb_color_map(uint8_t index)
178{
179	rgb_t rgb;
180
181	if (gfx_fb.framebuffer_common.framebuffer_type !=
182	    MULTIBOOT_FRAMEBUFFER_TYPE_RGB) {
183		if (index < nitems(solaris_color_to_pc_color))
184			return (solaris_color_to_pc_color[index]);
185		else
186			return (index);
187	}
188
189	rgb.red.pos = gfx_fb.u.fb2.framebuffer_red_field_position;
190	rgb.red.size = gfx_fb.u.fb2.framebuffer_red_mask_size;
191
192	rgb.green.pos = gfx_fb.u.fb2.framebuffer_green_field_position;
193	rgb.green.size = gfx_fb.u.fb2.framebuffer_green_mask_size;
194
195	rgb.blue.pos = gfx_fb.u.fb2.framebuffer_blue_field_position;
196	rgb.blue.size = gfx_fb.u.fb2.framebuffer_blue_mask_size;
197
198	return (rgb_color_map(&rgb, index));
199}
200
201static bool
202color_name_to_ansi(const char *name, int *val)
203{
204	if (strcasecmp(name, "black") == 0) {
205		*val = ANSI_COLOR_BLACK;
206		return (true);
207	}
208	if (strcasecmp(name, "red") == 0) {
209		*val = ANSI_COLOR_RED;
210		return (true);
211	}
212	if (strcasecmp(name, "green") == 0) {
213		*val = ANSI_COLOR_GREEN;
214		return (true);
215	}
216	if (strcasecmp(name, "yellow") == 0) {
217		*val = ANSI_COLOR_YELLOW;
218		return (true);
219	}
220	if (strcasecmp(name, "blue") == 0) {
221		*val = ANSI_COLOR_BLUE;
222		return (true);
223	}
224	if (strcasecmp(name, "magenta") == 0) {
225		*val = ANSI_COLOR_MAGENTA;
226		return (true);
227	}
228	if (strcasecmp(name, "cyan") == 0) {
229		*val = ANSI_COLOR_CYAN;
230		return (true);
231	}
232	if (strcasecmp(name, "white") == 0) {
233		*val = ANSI_COLOR_WHITE;
234		return (true);
235	}
236	return (false);
237}
238
239/* Callback to check and set colors */
240static int
241gfx_set_colors(struct env_var *ev, int flags, const void *value)
242{
243	int val = 0, limit;
244	char buf[2];
245	const void *evalue;
246
247	if (value == NULL)
248		return (CMD_OK);
249
250	if (gfx_fb.framebuffer_common.framebuffer_bpp < 24)
251		limit = 7;
252	else
253		limit = 255;
254
255	if (color_name_to_ansi(value, &val)) {
256		snprintf(buf, sizeof (buf), "%d", val);
257		evalue = buf;
258	} else {
259		char *end;
260
261		errno = 0;
262		val = (int)strtol(value, &end, 0);
263		if (errno != 0 || *end != '\0') {
264			printf("Allowed values are either ansi color name or "
265			    "number from range [0-7]%s.\n",
266			    limit == 7 ? "" : " or [16-255]");
267			return (CMD_OK);
268		}
269		evalue = value;
270	}
271
272	/* invalid value? */
273	if ((val < 0 || val > limit) || (val > 7 && val < 16)) {
274		printf("Allowed values are either ansi color name or "
275		    "number from range [0-7]%s.\n",
276		    limit == 7 ? "" : " or [16-255]");
277		return (CMD_OK);
278	}
279
280	if (strcmp(ev->ev_name, "tem.fg_color") == 0) {
281		/* is it already set? */
282		if (gfx_fg == val)
283			return (CMD_OK);
284		gfx_fg = val;
285	}
286	if (strcmp(ev->ev_name, "tem.bg_color") == 0) {
287		/* is it already set? */
288		if (gfx_bg == val)
289			return (CMD_OK);
290		gfx_bg = val;
291	}
292	env_setenv(ev->ev_name, flags | EV_NOHOOK, evalue, NULL, NULL);
293	plat_cons_update_mode(-1);
294	return (CMD_OK);
295}
296
297/* Callback to check and set inverses */
298static int
299gfx_set_inverses(struct env_var *ev, int flags, const void *value)
300{
301	int t, f;
302
303	if (value == NULL)
304		return (CMD_OK);
305
306	t = strcmp(value, "true");
307	f = strcmp(value, "false");
308
309	/* invalid value? */
310	if (t != 0 && f != 0)
311		return (CMD_OK);
312
313	if (strcmp(ev->ev_name, "tem.inverse") == 0) {
314		/* is it already set? */
315		if (gfx_inverse == (t == 0))
316			return (CMD_OK);
317		gfx_inverse = (t == 0);
318	}
319	if (strcmp(ev->ev_name, "tem.inverse-screen") == 0) {
320		/* is it already set? */
321		if (gfx_inverse_screen == (t == 0))
322			return (CMD_OK);
323		gfx_inverse_screen = (t == 0);
324	}
325	env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
326	plat_cons_update_mode(-1);
327	return (CMD_OK);
328}
329
330/*
331 * Initialize gfx framework.
332 */
333void
334gfx_framework_init(struct visual_ops *fb_ops)
335{
336	int rc, limit;
337	char *env, buf[2];
338#if	defined(EFI)
339	extern EFI_GRAPHICS_OUTPUT *gop;
340
341	if (gop != NULL) {
342		gfx_fb_ops.gfx_cons_clear = gfx_gop_cons_clear;
343		gfx_fb_ops.gfx_cons_copy = gfx_gop_cons_copy;
344		gfx_fb_ops.gfx_cons_display = gfx_gop_cons_display;
345		gfx_fb_ops.gfx_cons_display_cursor = gfx_gop_display_cursor;
346	}
347#endif
348
349	if (gfx_fb.framebuffer_common.framebuffer_bpp < 24)
350		limit = 7;
351	else
352		limit = 255;
353
354	/* Add visual io callbacks */
355	fb_ops->cons_clear = gfx_fb_cons_clear;
356	fb_ops->cons_copy = gfx_fb_cons_copy;
357	fb_ops->cons_display = gfx_fb_cons_display;
358
359	/* set up tem inverse controls */
360	env = getenv("tem.inverse");
361	if (env != NULL) {
362		if (strcmp(env, "true") == 0)
363			gfx_inverse = 1;
364		unsetenv("tem.inverse");
365	}
366
367	env = getenv("tem.inverse-screen");
368	if (env != NULL) {
369		if (strcmp(env, "true") == 0)
370			gfx_inverse_screen = 1;
371		unsetenv("tem.inverse-screen");
372	}
373
374	if (gfx_inverse)
375		env = "true";
376	else
377		env = "false";
378
379	env_setenv("tem.inverse", EV_VOLATILE, env, gfx_set_inverses,
380	    env_nounset);
381
382	if (gfx_inverse_screen)
383		env = "true";
384	else
385		env = "false";
386
387	env_setenv("tem.inverse-screen", EV_VOLATILE, env, gfx_set_inverses,
388	    env_nounset);
389
390	/* set up tem color controls */
391	env = getenv("tem.fg_color");
392	if (env != NULL) {
393		rc = (int)strtol(env, NULL, 0);
394		if ((rc >= 0 && rc <= limit) && (rc <= 7 || rc >= 16))
395			gfx_fg = rc;
396		unsetenv("tem.fg_color");
397	}
398
399	env = getenv("tem.bg_color");
400	if (env != NULL) {
401		rc = (int)strtol(env, NULL, 0);
402		if ((rc >= 0 && rc <= limit) && (rc <= 7 || rc >= 16))
403			gfx_bg = rc;
404		unsetenv("tem.bg_color");
405	}
406
407	snprintf(buf, sizeof (buf), "%d", gfx_fg);
408	env_setenv("tem.fg_color", EV_VOLATILE, buf, gfx_set_colors,
409	    env_nounset);
410	snprintf(buf, sizeof (buf), "%d", gfx_bg);
411	env_setenv("tem.bg_color", EV_VOLATILE, buf, gfx_set_colors,
412	    env_nounset);
413
414	/*
415	 * Setup font list to have builtin font.
416	 */
417	(void) insert_font(NULL, FONT_BUILTIN);
418}
419
420/*
421 * visual io callbacks.
422 */
423
424#if	defined(EFI)
425static int
426gfx_gop_cons_clear(uint32_t data, uint32_t width, uint32_t height)
427{
428	EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer;
429	EFI_STATUS status;
430	extern EFI_GRAPHICS_OUTPUT *gop;
431
432	BltBuffer = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)&data;
433
434	status = gop->Blt(gop, BltBuffer, EfiBltVideoFill, 0, 0,
435	    0, 0, width, height, 0);
436
437	if (EFI_ERROR(status))
438		return (1);
439	else
440		return (0);
441}
442#endif
443
444static int
445gfx_bm_cons_clear(uint32_t data, uint32_t width, uint32_t height)
446{
447	uint8_t *fb, *fb8;
448	uint32_t *fb32, pitch;
449	uint16_t *fb16;
450	uint32_t i, j;
451
452	fb = gfx_get_fb_address();
453	pitch = gfx_fb.framebuffer_common.framebuffer_pitch;
454
455	switch (gfx_fb.framebuffer_common.framebuffer_bpp) {
456	case 8:		/* 8 bit */
457		for (i = 0; i < height; i++) {
458			(void) memset(fb + i * pitch, data, pitch);
459		}
460		break;
461	case 15:
462	case 16:		/* 16 bit */
463		for (i = 0; i < height; i++) {
464			fb16 = (uint16_t *)(fb + i * pitch);
465			for (j = 0; j < width; j++)
466				fb16[j] = (uint16_t)(data & 0xffff);
467		}
468		break;
469	case 24:		/* 24 bit */
470		for (i = 0; i < height; i++) {
471			fb8 = fb + i * pitch;
472			for (j = 0; j < pitch; j += 3) {
473				fb8[j] = (data >> 16) & 0xff;
474				fb8[j+1] = (data >> 8) & 0xff;
475				fb8[j+2] = data & 0xff;
476			}
477		}
478		break;
479	case 32:		/* 32 bit */
480		for (i = 0; i < height; i++) {
481			fb32 = (uint32_t *)(fb + i * pitch);
482			for (j = 0; j < width; j++)
483				fb32[j] = data;
484		}
485		break;
486	default:
487		return (1);
488	}
489
490	return (0);
491}
492
493static int
494gfx_fb_cons_clear(struct vis_consclear *ca)
495{
496	uint32_t data, width, height;
497	int ret;
498#if	defined(EFI)
499	EFI_TPL tpl;
500#endif
501
502	data = gfx_fb_color_map(ca->bg_color);
503	width = gfx_fb.framebuffer_common.framebuffer_width;
504	height = gfx_fb.framebuffer_common.framebuffer_height;
505
506#if	defined(EFI)
507	tpl = BS->RaiseTPL(TPL_NOTIFY);
508#endif
509	ret = gfx_fb_ops.gfx_cons_clear(data, width, height);
510#if	defined(EFI)
511	BS->RestoreTPL(tpl);
512#endif
513	return (ret);
514}
515
516#if	defined(EFI)
517static void
518gfx_gop_cons_copy(struct vis_conscopy *ma)
519{
520	UINTN width, height;
521	extern EFI_GRAPHICS_OUTPUT *gop;
522
523	width = ma->e_col - ma->s_col + 1;
524	height = ma->e_row - ma->s_row + 1;
525
526	(void) gop->Blt(gop, NULL, EfiBltVideoToVideo, ma->s_col, ma->s_row,
527	    ma->t_col, ma->t_row, width, height, 0);
528}
529#endif
530
531static void
532gfx_bm_cons_copy(struct vis_conscopy *ma)
533{
534	uint32_t soffset, toffset;
535	uint32_t width, height;
536	uint8_t *src, *dst, *fb;
537	uint32_t bpp, pitch;
538
539	fb = gfx_get_fb_address();
540	bpp = roundup2(gfx_fb.framebuffer_common.framebuffer_bpp, 8) >> 3;
541	pitch = gfx_fb.framebuffer_common.framebuffer_pitch;
542
543	soffset = ma->s_col * bpp + ma->s_row * pitch;
544	toffset = ma->t_col * bpp + ma->t_row * pitch;
545	src = fb + soffset;
546	dst = fb + toffset;
547	width = (ma->e_col - ma->s_col + 1) * bpp;
548	height = ma->e_row - ma->s_row + 1;
549
550	if (toffset <= soffset) {
551		for (uint32_t i = 0; i < height; i++) {
552			uint32_t increment = i * pitch;
553			(void) memmove(dst + increment, src + increment, width);
554		}
555	} else {
556		for (int i = height - 1; i >= 0; i--) {
557			uint32_t increment = i * pitch;
558			(void) memmove(dst + increment, src + increment, width);
559		}
560	}
561}
562
563static void
564gfx_fb_cons_copy(struct vis_conscopy *ma)
565{
566#if	defined(EFI)
567	EFI_TPL tpl;
568
569	tpl = BS->RaiseTPL(TPL_NOTIFY);
570#endif
571
572	gfx_fb_ops.gfx_cons_copy(ma);
573#if	defined(EFI)
574	BS->RestoreTPL(tpl);
575#endif
576}
577
578/*
579 * Implements alpha blending for RGBA data, could use pixels for arguments,
580 * but byte stream seems more generic.
581 * The generic alpha blending is:
582 * blend = alpha * fg + (1.0 - alpha) * bg.
583 * Since our alpha is not from range [0..1], we scale appropriately.
584 */
585static uint8_t
586alpha_blend(uint8_t fg, uint8_t bg, uint8_t alpha)
587{
588	uint16_t blend, h, l;
589
590	/* trivial corner cases */
591	if (alpha == 0)
592		return (bg);
593	if (alpha == 0xFF)
594		return (fg);
595	blend = (alpha * fg + (0xFF - alpha) * bg);
596	/* Division by 0xFF */
597	h = blend >> 8;
598	l = blend & 0xFF;
599	if (h + l >= 0xFF)
600		h++;
601	return (h);
602}
603
604/* Copy memory to framebuffer or to memory. */
605static void
606bitmap_cpy(uint8_t *dst, uint8_t *src, uint32_t len, int bpp)
607{
608	uint32_t i;
609	uint8_t a;
610
611	switch (bpp) {
612	case 4:
613		/*
614		 * we only implement alpha blending for depth 32,
615		 * use memcpy for other cases.
616		 */
617		for (i = 0; i < len; i += bpp) {
618			a = src[i+3];
619			dst[i] = alpha_blend(src[i], dst[i], a);
620			dst[i+1] = alpha_blend(src[i+1], dst[i+1], a);
621			dst[i+2] = alpha_blend(src[i+2], dst[i+2], a);
622			dst[i+3] = a;
623		}
624		break;
625	default:
626		(void) memcpy(dst, src, len);
627		break;
628	}
629}
630
631#if	defined(EFI)
632static void
633gfx_gop_cons_display(struct vis_consdisplay *da)
634{
635	EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer;
636	uint32_t size;
637	int bpp;
638	extern EFI_GRAPHICS_OUTPUT *gop;
639
640	bpp = roundup2(gfx_fb.framebuffer_common.framebuffer_bpp, 8) >> 3;
641	size = sizeof (*BltBuffer) * da->width * da->height;
642
643	/*
644	 * Common data to display is glyph, use preallocated
645	 * glyph buffer.
646	 */
647	if (size == GlyphBufferSize) {
648		BltBuffer = GlyphBuffer;
649	} else {
650		BltBuffer = malloc(size);
651	}
652	if (BltBuffer == NULL) {
653		if (gfx_get_fb_address() != NULL) {
654			/* Fall back to bitmap implementation */
655			gfx_bm_cons_display(da);
656		}
657		/*
658		 * We can not use Blt() and we have no address
659		 * to write data to FB.
660		 */
661		return;
662	}
663
664	(void) gop->Blt(gop, BltBuffer, EfiBltVideoToBltBuffer,
665	    da->col, da->row, 0, 0, da->width, da->height, 0);
666	bitmap_cpy((void *)BltBuffer, da->data, size, bpp);
667	(void) gop->Blt(gop, BltBuffer, EfiBltBufferToVideo,
668	    0, 0, da->col, da->row, da->width, da->height, 0);
669
670	if (BltBuffer != GlyphBuffer)
671		free(BltBuffer);
672}
673#endif
674
675static void
676gfx_bm_cons_display(struct vis_consdisplay *da)
677{
678	uint32_t size;		/* write size per scanline */
679	uint8_t *fbp;		/* fb + calculated offset */
680	int i, bpp, pitch;
681
682	bpp = roundup2(gfx_fb.framebuffer_common.framebuffer_bpp, 8) >> 3;
683	pitch = gfx_fb.framebuffer_common.framebuffer_pitch;
684
685	size = da->width * bpp;
686	fbp = gfx_get_fb_address();
687	fbp += da->col * bpp + da->row * pitch;
688
689	/* write all scanlines in rectangle */
690	for (i = 0; i < da->height; i++) {
691		uint8_t *dest = fbp + i * pitch;
692		uint8_t *src = da->data + i * size;
693		bitmap_cpy(dest, src, size, bpp);
694	}
695}
696
697static void
698gfx_fb_cons_display(struct vis_consdisplay *da)
699{
700#if	defined(EFI)
701	EFI_TPL tpl;
702#endif
703
704	/* make sure we will not write past FB */
705	if ((uint32_t)da->col >= gfx_fb.framebuffer_common.framebuffer_width ||
706	    (uint32_t)da->row >= gfx_fb.framebuffer_common.framebuffer_height ||
707	    (uint32_t)da->col + da->width >
708	    gfx_fb.framebuffer_common.framebuffer_width ||
709	    (uint32_t)da->row + da->height >
710	    gfx_fb.framebuffer_common.framebuffer_height)
711		return;
712
713#if	defined(EFI)
714	tpl = BS->RaiseTPL(TPL_NOTIFY);
715#endif
716	gfx_fb_ops.gfx_cons_display(da);
717#if	defined(EFI)
718	BS->RestoreTPL(tpl);
719#endif
720}
721
722void
723gfx_fb_display_cursor(struct vis_conscursor *ca)
724{
725#if	defined(EFI)
726	EFI_TPL tpl;
727
728	tpl = BS->RaiseTPL(TPL_NOTIFY);
729#endif
730	gfx_fb_ops.gfx_cons_display_cursor(ca);
731#if	defined(EFI)
732	BS->RestoreTPL(tpl);
733#endif
734}
735
736#if	defined(EFI)
737static void
738gfx_gop_display_cursor(struct vis_conscursor *ca)
739{
740	union pixel {
741		EFI_GRAPHICS_OUTPUT_BLT_PIXEL p;
742		uint32_t p32;
743	} *row, fg, bg;
744	size_t size;
745	extern EFI_GRAPHICS_OUTPUT *gop;
746
747	size = sizeof (*GlyphBuffer) * ca->width * ca->height;
748	if (size != GlyphBufferSize) {
749		free(GlyphBuffer);
750		GlyphBuffer = malloc(size);
751		if (GlyphBuffer == NULL) {
752			if (gfx_get_fb_address() != NULL) {
753				/* Fall back to bitmap implementation */
754				gfx_bm_display_cursor(ca);
755			}
756			/*
757			 * We can not use Blt() and we have no address
758			 * to write data to FB.
759			 */
760			return;
761		}
762		GlyphBufferSize = size;
763	}
764
765	/*
766	 * Since EfiBltVideoToBltBuffer is valid, Blt() can fail only
767	 * due to device error.
768	 */
769	if (gop->Blt(gop, GlyphBuffer, EfiBltVideoToBltBuffer,
770	    ca->col, ca->row, 0, 0, ca->width, ca->height, 0) != EFI_SUCCESS)
771		return;
772
773	fg.p.Reserved = 0;
774	fg.p.Red = ca->fg_color.twentyfour[0];
775	fg.p.Green = ca->fg_color.twentyfour[1];
776	fg.p.Blue = ca->fg_color.twentyfour[2];
777	bg.p.Reserved = 0;
778	bg.p.Red = ca->bg_color.twentyfour[0];
779	bg.p.Green = ca->bg_color.twentyfour[1];
780	bg.p.Blue = ca->bg_color.twentyfour[2];
781
782	/*
783	 * Build inverse image of the glyph.
784	 * Since xor has self-inverse property, drawing cursor
785	 * second time on the same spot, will restore the original content.
786	 */
787	for (screen_size_t i = 0; i < ca->height; i++) {
788		row = (union pixel *)(GlyphBuffer + i * ca->width);
789		for (screen_size_t j = 0; j < ca->width; j++) {
790			row[j].p32 = (row[j].p32 ^ fg.p32) ^ bg.p32;
791		}
792	}
793
794	(void) gop->Blt(gop, GlyphBuffer, EfiBltBufferToVideo,
795	    0, 0, ca->col, ca->row, ca->width, ca->height, 0);
796}
797#endif
798
799static void
800gfx_bm_display_cursor(struct vis_conscursor *ca)
801{
802	uint32_t fg, bg;
803	uint32_t offset, size, *fb32;
804	uint16_t *fb16;
805	uint8_t *fb8, *fb;
806	uint32_t bpp, pitch;
807
808	fb = gfx_get_fb_address();
809	bpp = roundup2(gfx_fb.framebuffer_common.framebuffer_bpp, 8) >> 3;
810	pitch = gfx_fb.framebuffer_common.framebuffer_pitch;
811
812	size = ca->width * bpp;
813
814	/*
815	 * Build cursor image. We are building mirror image of data on
816	 * frame buffer by (D xor FG) xor BG.
817	 */
818	offset = ca->col * bpp + ca->row * pitch;
819	switch (gfx_fb.framebuffer_common.framebuffer_bpp) {
820	case 8:		/* 8 bit */
821		fg = ca->fg_color.mono;
822		bg = ca->bg_color.mono;
823		for (int i = 0; i < ca->height; i++) {
824			fb8 = fb + offset + i * pitch;
825			for (uint32_t j = 0; j < size; j += 1) {
826				fb8[j] = (fb8[j] ^ (fg & 0xff)) ^ (bg & 0xff);
827			}
828		}
829		break;
830	case 15:
831	case 16:	/* 16 bit */
832		fg = ca->fg_color.sixteen[0] << 8;
833		fg |= ca->fg_color.sixteen[1];
834		bg = ca->bg_color.sixteen[0] << 8;
835		bg |= ca->bg_color.sixteen[1];
836		for (int i = 0; i < ca->height; i++) {
837			fb16 = (uint16_t *)(fb + offset + i * pitch);
838			for (int j = 0; j < ca->width; j++) {
839				fb16[j] = (fb16[j] ^ (fg & 0xffff)) ^
840				    (bg & 0xffff);
841			}
842		}
843		break;
844	case 24:	/* 24 bit */
845		fg = ca->fg_color.twentyfour[0] << 16;
846		fg |= ca->fg_color.twentyfour[1] << 8;
847		fg |= ca->fg_color.twentyfour[2];
848		bg = ca->bg_color.twentyfour[0] << 16;
849		bg |= ca->bg_color.twentyfour[1] << 8;
850		bg |= ca->bg_color.twentyfour[2];
851
852		for (int i = 0; i < ca->height; i++) {
853			fb8 = fb + offset + i * pitch;
854			for (uint32_t j = 0; j < size; j += 3) {
855				fb8[j] = (fb8[j] ^ ((fg >> 16) & 0xff)) ^
856				    ((bg >> 16) & 0xff);
857				fb8[j+1] = (fb8[j+1] ^ ((fg >> 8) & 0xff)) ^
858				    ((bg >> 8) & 0xff);
859				fb8[j+2] = (fb8[j+2] ^ (fg & 0xff)) ^
860				    (bg & 0xff);
861			}
862		}
863		break;
864	case 32:	/* 32 bit */
865		fg = ca->fg_color.twentyfour[0] << 16;
866		fg |= ca->fg_color.twentyfour[1] << 8;
867		fg |= ca->fg_color.twentyfour[2];
868		bg = ca->bg_color.twentyfour[0] << 16;
869		bg |= ca->bg_color.twentyfour[1] << 8;
870		bg |= ca->bg_color.twentyfour[2];
871		for (int i = 0; i < ca->height; i++) {
872			fb32 = (uint32_t *)(fb + offset + i * pitch);
873			for (int j = 0; j < ca->width; j++)
874				fb32[j] = (fb32[j] ^ fg) ^ bg;
875		}
876		break;
877	}
878}
879
880/*
881 * Public graphics primitives.
882 */
883
884static int
885isqrt(int num)
886{
887	int res = 0;
888	int bit = 1 << 30;
889
890	/* "bit" starts at the highest power of four <= the argument. */
891	while (bit > num)
892		bit >>= 2;
893
894	while (bit != 0) {
895		if (num >= res + bit) {
896			num -= res + bit;
897			res = (res >> 1) + bit;
898		} else
899			res >>= 1;
900		bit >>= 2;
901	}
902	return (res);
903}
904
905/* set pixel in framebuffer using gfx coordinates */
906void
907gfx_fb_setpixel(uint32_t x, uint32_t y)
908{
909	uint32_t c, offset, pitch, bpp;
910	uint8_t *fb;
911	text_color_t fg, bg;
912#if	defined(EFI)
913	EFI_GRAPHICS_OUTPUT_BLT_PIXEL p;
914	extern EFI_GRAPHICS_OUTPUT *gop;
915#endif
916
917	if (plat_stdout_is_framebuffer() == 0)
918		return;
919
920	tem_get_colors((tem_vt_state_t)tems.ts_active, &fg, &bg);
921	c = gfx_fb_color_map(fg);
922
923	if (x >= gfx_fb.framebuffer_common.framebuffer_width ||
924	    y >= gfx_fb.framebuffer_common.framebuffer_height)
925		return;
926
927	fb = gfx_get_fb_address();
928	pitch = gfx_fb.framebuffer_common.framebuffer_pitch;
929	bpp = roundup2(gfx_fb.framebuffer_common.framebuffer_bpp, 8) >> 3;
930
931	offset = y * pitch + x * bpp;
932	switch (gfx_fb.framebuffer_common.framebuffer_bpp) {
933	case 8:
934		fb[offset] = c & 0xff;
935		break;
936	case 15:
937	case 16:
938		*(uint16_t *)(fb + offset) = c & 0xffff;
939		break;
940	case 24:
941		fb[offset] = (c >> 16) & 0xff;
942		fb[offset + 1] = (c >> 8) & 0xff;
943		fb[offset + 2] = c & 0xff;
944		break;
945	case 32:
946#if	defined(EFI)
947		if (gop != NULL) {
948			p.Reserved = 0;
949			p.Red = (c >> 16) & 0xff;
950			p.Green = (c >> 8) & 0xff;
951			p.Blue = c & 0xff;
952			gop->Blt(gop, &p, EfiBltBufferToVideo, 0, 0,
953			    x, y, 1, 1, 0);
954		} else
955#endif
956		*(uint32_t *)(fb + offset) = c;
957		break;
958	}
959}
960
961/*
962 * draw rectangle in framebuffer using gfx coordinates.
963 * The function is borrowed from fbsd vt_fb.c
964 */
965void
966gfx_fb_drawrect(uint32_t x1, uint32_t y1, uint32_t x2, uint32_t y2,
967    uint32_t fill)
968{
969	uint32_t x, y;
970
971	if (plat_stdout_is_framebuffer() == 0)
972		return;
973
974	for (y = y1; y <= y2; y++) {
975		if (fill || (y == y1) || (y == y2)) {
976			for (x = x1; x <= x2; x++)
977				gfx_fb_setpixel(x, y);
978		} else {
979			gfx_fb_setpixel(x1, y);
980			gfx_fb_setpixel(x2, y);
981		}
982	}
983}
984
985void
986gfx_fb_line(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t wd)
987{
988	int dx, sx, dy, sy;
989	int err, e2, x2, y2, ed, width;
990
991	if (plat_stdout_is_framebuffer() == 0)
992		return;
993
994	width = wd;
995	sx = x0 < x1? 1 : -1;
996	sy = y0 < y1? 1 : -1;
997	dx = x1 > x0? x1 - x0 : x0 - x1;
998	dy = y1 > y0? y1 - y0 : y0 - y1;
999	err = dx + dy;
1000	ed = dx + dy == 0 ? 1: isqrt(dx * dx + dy * dy);
1001
1002	for (;;) {
1003		gfx_fb_setpixel(x0, y0);
1004		e2 = err;
1005		x2 = x0;
1006		if ((e2 << 1) >= -dx) {		/* x step */
1007			e2 += dy;
1008			y2 = y0;
1009			while (e2 < ed * width &&
1010			    (y1 != (uint32_t)y2 || dx > dy)) {
1011				y2 += sy;
1012				gfx_fb_setpixel(x0, y2);
1013				e2 += dx;
1014			}
1015			if (x0 == x1)
1016				break;
1017			e2 = err;
1018			err -= dy;
1019			x0 += sx;
1020		}
1021		if ((e2 << 1) <= dy) {		/* y step */
1022			e2 = dx-e2;
1023			while (e2 < ed * width &&
1024			    (x1 != (uint32_t)x2 || dx < dy)) {
1025				x2 += sx;
1026				gfx_fb_setpixel(x2, y0);
1027				e2 += dy;
1028			}
1029			if (y0 == y1)
1030				break;
1031			err += dx;
1032			y0 += sy;
1033		}
1034	}
1035}
1036
1037/*
1038 * quadratic B��zier curve limited to gradients without sign change.
1039 */
1040void
1041gfx_fb_bezier(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t x2,
1042    uint32_t y2, uint32_t wd)
1043{
1044	int sx, sy, xx, yy, xy, width;
1045	int dx, dy, err, curvature;
1046	int i;
1047
1048	if (plat_stdout_is_framebuffer() == 0)
1049		return;
1050
1051	width = wd;
1052	sx = x2 - x1;
1053	sy = y2 - y1;
1054	xx = x0 - x1;
1055	yy = y0 - y1;
1056	curvature = xx*sy - yy*sx;
1057
1058	if (sx*sx + sy*sy > xx*xx+yy*yy) {
1059		x2 = x0;
1060		x0 = sx + x1;
1061		y2 = y0;
1062		y0 = sy + y1;
1063		curvature = -curvature;
1064	}
1065	if (curvature != 0) {
1066		xx += sx;
1067		sx = x0 < x2? 1 : -1;
1068		xx *= sx;
1069		yy += sy;
1070		sy = y0 < y2? 1 : -1;
1071		yy *= sy;
1072		xy = (xx*yy) << 1;
1073		xx *= xx;
1074		yy *= yy;
1075		if (curvature * sx * sy < 0) {
1076			xx = -xx;
1077			yy = -yy;
1078			xy = -xy;
1079			curvature = -curvature;
1080		}
1081		dx = 4 * sy * curvature * (x1 - x0) + xx - xy;
1082		dy = 4 * sx * curvature * (y0 - y1) + yy - xy;
1083		xx += xx;
1084		yy += yy;
1085		err = dx + dy + xy;
1086		do {
1087			for (i = 0; i <= width; i++)
1088				gfx_fb_setpixel(x0 + i, y0);
1089			if (x0 == x2 && y0 == y2)
1090				return;  /* last pixel -> curve finished */
1091			y1 = 2 * err < dx;
1092			if (2 * err > dy) {
1093				x0 += sx;
1094				dx -= xy;
1095				dy += yy;
1096				err += dy;
1097			}
1098			if (y1 != 0) {
1099				y0 += sy;
1100				dy -= xy;
1101				dx += xx;
1102				err += dx;
1103			}
1104		} while (dy < dx); /* gradient negates -> algorithm fails */
1105	}
1106	gfx_fb_line(x0, y0, x2, y2, width);
1107}
1108
1109/*
1110 * draw rectangle using terminal coordinates and current foreground color.
1111 */
1112void
1113gfx_term_drawrect(uint32_t ux1, uint32_t uy1, uint32_t ux2, uint32_t uy2)
1114{
1115	int x1, y1, x2, y2;
1116	int xshift, yshift;
1117	int width, i;
1118	uint32_t vf_width, vf_height;
1119
1120	if (plat_stdout_is_framebuffer() == 0)
1121		return;
1122
1123	vf_width = tems.ts_font.vf_width;
1124	vf_height = tems.ts_font.vf_height;
1125	width = vf_width / 4;			/* line width */
1126	xshift = (vf_width - width) / 2;
1127	yshift = (vf_height - width) / 2;
1128	/* Terminal coordinates start from (1,1) */
1129	ux1--;
1130	uy1--;
1131	ux2--;
1132	uy2--;
1133
1134	/* mark area used in tem */
1135	tem_image_display(tems.ts_active, uy1 - 1, ux1 - 1, uy2, ux2);
1136
1137	/*
1138	 * Draw horizontal lines width points thick, shifted from outer edge.
1139	 */
1140	x1 = (ux1 + 1) * vf_width + tems.ts_p_offset.x;
1141	y1 = uy1 * vf_height + tems.ts_p_offset.y + yshift;
1142	x2 = ux2 * vf_width + tems.ts_p_offset.x;
1143	gfx_fb_drawrect(x1, y1, x2, y1 + width, 1);
1144	y2 = uy2 * vf_height + tems.ts_p_offset.y;
1145	y2 += vf_height - yshift - width;
1146	gfx_fb_drawrect(x1, y2, x2, y2 + width, 1);
1147
1148	/*
1149	 * Draw vertical lines width points thick, shifted from outer edge.
1150	 */
1151	x1 = ux1 * vf_width + tems.ts_p_offset.x + xshift;
1152	y1 = uy1 * vf_height + tems.ts_p_offset.y;
1153	y1 += vf_height;
1154	y2 = uy2 * vf_height + tems.ts_p_offset.y;
1155	gfx_fb_drawrect(x1, y1, x1 + width, y2, 1);
1156	x1 = ux2 * vf_width + tems.ts_p_offset.x;
1157	x1 += vf_width - xshift - width;
1158	gfx_fb_drawrect(x1, y1, x1 + width, y2, 1);
1159
1160	/* Draw upper left corner. */
1161	x1 = ux1 * vf_width + tems.ts_p_offset.x + xshift;
1162	y1 = uy1 * vf_height + tems.ts_p_offset.y;
1163	y1 += vf_height;
1164
1165	x2 = ux1 * vf_width + tems.ts_p_offset.x;
1166	x2 += vf_width;
1167	y2 = uy1 * vf_height + tems.ts_p_offset.y + yshift;
1168	for (i = 0; i <= width; i++)
1169		gfx_fb_bezier(x1 + i, y1, x1 + i, y2 + i, x2, y2 + i, width-i);
1170
1171	/* Draw lower left corner. */
1172	x1 = ux1 * vf_width + tems.ts_p_offset.x;
1173	x1 += vf_width;
1174	y1 = uy2 * vf_height + tems.ts_p_offset.y;
1175	y1 += vf_height - yshift;
1176	x2 = ux1 * vf_width + tems.ts_p_offset.x + xshift;
1177	y2 = uy2 * vf_height + tems.ts_p_offset.y;
1178	for (i = 0; i <= width; i++)
1179		gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i);
1180
1181	/* Draw upper right corner. */
1182	x1 = ux2 * vf_width + tems.ts_p_offset.x;
1183	y1 = uy1 * vf_height + tems.ts_p_offset.y + yshift;
1184	x2 = ux2 * vf_width + tems.ts_p_offset.x;
1185	x2 += vf_width - xshift - width;
1186	y2 = uy1 * vf_height + tems.ts_p_offset.y;
1187	y2 += vf_height;
1188	for (i = 0; i <= width; i++)
1189		gfx_fb_bezier(x1, y1 + i, x2 + i, y1 + i, x2 + i, y2, width-i);
1190
1191	/* Draw lower right corner. */
1192	x1 = ux2 * vf_width + tems.ts_p_offset.x;
1193	y1 = uy2 * vf_height + tems.ts_p_offset.y;
1194	y1 += vf_height - yshift;
1195	x2 = ux2 * vf_width + tems.ts_p_offset.x;
1196	x2 += vf_width - xshift - width;
1197	y2 = uy2 * vf_height + tems.ts_p_offset.y;
1198	for (i = 0; i <= width; i++)
1199		gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i);
1200}
1201
1202#define	FL_PUTIMAGE_BORDER	0x1
1203#define	FL_PUTIMAGE_NOSCROLL	0x2
1204#define	FL_PUTIMAGE_DEBUG	0x80
1205
1206int
1207gfx_fb_putimage(png_t *png, uint32_t ux1, uint32_t uy1, uint32_t ux2,
1208    uint32_t uy2, uint32_t flags)
1209{
1210	struct vis_consdisplay da;
1211	uint32_t i, j, x, y, fheight, fwidth, color;
1212	int fbpp;
1213	uint8_t r, g, b, a, *p;
1214	bool scale = false;
1215	bool trace = false;
1216
1217	trace = (flags & FL_PUTIMAGE_DEBUG) != 0;
1218
1219	if (plat_stdout_is_framebuffer() == 0) {
1220		if (trace)
1221			printf("Framebuffer not active.\n");
1222		return (1);
1223	}
1224
1225	if (png->color_type != PNG_TRUECOLOR_ALPHA) {
1226		if (trace)
1227			printf("Not truecolor image.\n");
1228		return (1);
1229	}
1230
1231	if (ux1 > gfx_fb.framebuffer_common.framebuffer_width ||
1232	    uy1 > gfx_fb.framebuffer_common.framebuffer_height) {
1233		if (trace)
1234			printf("Top left coordinate off screen.\n");
1235		return (1);
1236	}
1237
1238	if (png->width > UINT16_MAX || png->height > UINT16_MAX) {
1239		if (trace)
1240			printf("Image too large.\n");
1241		return (1);
1242	}
1243
1244	if (png->width < 1 || png->height < 1) {
1245		if (trace)
1246			printf("Image too small.\n");
1247		return (1);
1248	}
1249
1250	/*
1251	 * If 0 was passed for either ux2 or uy2, then calculate the missing
1252	 * part of the bottom right coordinate.
1253	 */
1254	scale = true;
1255	if (ux2 == 0 && uy2 == 0) {
1256		/* Both 0, use the native resolution of the image */
1257		ux2 = ux1 + png->width;
1258		uy2 = uy1 + png->height;
1259		scale = false;
1260	} else if (ux2 == 0) {
1261		/* Set ux2 from uy2/uy1 to maintain aspect ratio */
1262		ux2 = ux1 + (png->width * (uy2 - uy1)) / png->height;
1263	} else if (uy2 == 0) {
1264		/* Set uy2 from ux2/ux1 to maintain aspect ratio */
1265		uy2 = uy1 + (png->height * (ux2 - ux1)) / png->width;
1266	}
1267
1268	if (ux2 > gfx_fb.framebuffer_common.framebuffer_width ||
1269	    uy2 > gfx_fb.framebuffer_common.framebuffer_height) {
1270		if (trace)
1271			printf("Bottom right coordinate off screen.\n");
1272		return (1);
1273	}
1274
1275	fwidth = ux2 - ux1;
1276	fheight = uy2 - uy1;
1277
1278	/*
1279	 * If the original image dimensions have been passed explicitly,
1280	 * disable scaling.
1281	 */
1282	if (fwidth == png->width && fheight == png->height)
1283		scale = false;
1284
1285	if (ux1 == 0) {
1286		/*
1287		 * No top left X co-ordinate (real coordinates start at 1),
1288		 * place as far right as it will fit.
1289		 */
1290		ux2 = gfx_fb.framebuffer_common.framebuffer_width -
1291		    tems.ts_p_offset.x;
1292		ux1 = ux2 - fwidth;
1293	}
1294
1295	if (uy1 == 0) {
1296		/*
1297		 * No top left Y co-ordinate (real coordinates start at 1),
1298		 * place as far down as it will fit.
1299		 */
1300		uy2 = gfx_fb.framebuffer_common.framebuffer_height -
1301		    tems.ts_p_offset.y;
1302		uy1 = uy2 - fheight;
1303	}
1304
1305	if (ux1 >= ux2 || uy1 >= uy2) {
1306		if (trace)
1307			printf("Image dimensions reversed.\n");
1308		return (1);
1309	}
1310
1311	if (fwidth < 2 || fheight < 2) {
1312		if (trace)
1313			printf("Target area too small\n");
1314		return (1);
1315	}
1316
1317	if (trace)
1318		printf("Image %ux%u -> %ux%u @%ux%u\n",
1319		    png->width, png->height, fwidth, fheight, ux1, uy1);
1320
1321	da.col = ux1;
1322	da.row = uy1;
1323	da.width = fwidth;
1324	da.height = fheight;
1325
1326	/*
1327	 * mark area used in tem
1328	 */
1329	if (!(flags & FL_PUTIMAGE_NOSCROLL)) {
1330		tem_image_display(tems.ts_active,
1331		    da.row / tems.ts_font.vf_height - 1,
1332		    da.col / tems.ts_font.vf_width - 1,
1333		    (da.row + da.height) / tems.ts_font.vf_height - 1,
1334		    (da.col + da.width) / tems.ts_font.vf_width - 1);
1335	}
1336
1337	if ((flags & FL_PUTIMAGE_BORDER))
1338		gfx_fb_drawrect(ux1, uy1, ux2, uy2, 0);
1339
1340	fbpp = roundup2(gfx_fb.framebuffer_common.framebuffer_bpp, 8) >> 3;
1341
1342	da.data = malloc(fwidth * fheight * fbpp);
1343	if (da.data == NULL) {
1344		if (trace)
1345			printf("Out of memory.\n");
1346		return (1);
1347	}
1348
1349	/*
1350	 * Build image for our framebuffer.
1351	 */
1352
1353	/* Helper to calculate the pixel index from the source png */
1354#define	GETPIXEL(xx, yy) (((yy) * png->width + (xx)) * png->bpp)
1355
1356	/*
1357	 * For each of the x and y directions, calculate the number of pixels
1358	 * in the source image that correspond to a single pixel in the target.
1359	 * Use fixed-point arithmetic with 16-bits for each of the integer and
1360	 * fractional parts.
1361	 */
1362	const uint32_t wcstep = ((png->width - 1) << 16) / (fwidth - 1);
1363	const uint32_t hcstep = ((png->height - 1) << 16) / (fheight - 1);
1364
1365	uint32_t hc = 0;
1366	for (y = 0; y < fheight; y++) {
1367		uint32_t hc2 = (hc >> 9) & 0x7f;
1368		uint32_t hc1 = 0x80 - hc2;
1369
1370		uint32_t offset_y = hc >> 16;
1371		uint32_t offset_y1 = offset_y + 1;
1372
1373		uint32_t wc = 0;
1374		for (x = 0; x < fwidth; x++) {
1375			uint32_t wc2 = (wc >> 9) & 0x7f;
1376			uint32_t wc1 = 0x80 - wc2;
1377
1378			uint32_t offset_x = wc >> 16;
1379			uint32_t offset_x1 = offset_x + 1;
1380
1381			/* Target pixel index */
1382			j = (y * fwidth + x) * fbpp;
1383
1384			if (!scale) {
1385				i = GETPIXEL(x, y);
1386				r = png->image[i];
1387				g = png->image[i + 1];
1388				b = png->image[i + 2];
1389				a = png->image[i + 3];
1390			} else {
1391				uint8_t pixel[4];
1392
1393				uint32_t p00 = GETPIXEL(offset_x, offset_y);
1394				uint32_t p01 = GETPIXEL(offset_x, offset_y1);
1395				uint32_t p10 = GETPIXEL(offset_x1, offset_y);
1396				uint32_t p11 = GETPIXEL(offset_x1, offset_y1);
1397
1398				/*
1399				 * Given a 2x2 array of pixels in the source
1400				 * image, combine them to produce a single
1401				 * value for the pixel in the target image.
1402				 * Each column of pixels is combined using
1403				 * a weighted average where the top and bottom
1404				 * pixels contribute hc1 and hc2 respectively.
1405				 * The calculation for bottom pixel pB and
1406				 * top pixel pT is:
1407				 *   (pT * hc1 + pB * hc2) / (hc1 + hc2)
1408				 * Once the values are determined for the two
1409				 * columns of pixels, then the columns are
1410				 * averaged together in the same way but using
1411				 * wc1 and wc2 for the weightings.
1412				 *
1413				 * Since hc1 and hc2 are chosen so that
1414				 * hc1 + hc2 == 128 (and same for wc1 + wc2),
1415				 * the >> 14 below is a quick way to divide by
1416				 * (hc1 + hc2) * (wc1 + wc2)
1417				 */
1418				for (i = 0; i < 4; i++)
1419					pixel[i] = (
1420					    (png->image[p00 + i] * hc1 +
1421					    png->image[p01 + i] * hc2) * wc1 +
1422					    (png->image[p10 + i] * hc1 +
1423					    png->image[p11 + i] * hc2) * wc2)
1424					    >> 14;
1425
1426				r = pixel[0];
1427				g = pixel[1];
1428				b = pixel[2];
1429				a = pixel[3];
1430			}
1431
1432			color =
1433			    r >> (8 - gfx_fb.u.fb2.framebuffer_red_mask_size)
1434			    << gfx_fb.u.fb2.framebuffer_red_field_position |
1435			    g >> (8 - gfx_fb.u.fb2.framebuffer_green_mask_size)
1436			    << gfx_fb.u.fb2.framebuffer_green_field_position |
1437			    b >> (8 - gfx_fb.u.fb2.framebuffer_blue_mask_size)
1438			    << gfx_fb.u.fb2.framebuffer_blue_field_position;
1439
1440			switch (gfx_fb.framebuffer_common.framebuffer_bpp) {
1441			case 8: {
1442				uint32_t best, dist, k;
1443				int diff;
1444
1445				color = 0;
1446				best = 256 * 256 * 256;
1447				for (k = 0; k < 16; k++) {
1448					diff = r - cmap4_to_24.red[k];
1449					dist = diff * diff;
1450					diff = g - cmap4_to_24.green[k];
1451					dist += diff * diff;
1452					diff = b - cmap4_to_24.blue[k];
1453					dist += diff * diff;
1454
1455					if (dist < best) {
1456						color = k;
1457						best = dist;
1458						if (dist == 0)
1459							break;
1460					}
1461				}
1462				da.data[j] = solaris_color_to_pc_color[color];
1463				break;
1464			}
1465			case 15:
1466			case 16:
1467				*(uint16_t *)(da.data+j) = color;
1468				break;
1469			case 24:
1470				p = (uint8_t *)&color;
1471				da.data[j] = p[0];
1472				da.data[j+1] = p[1];
1473				da.data[j+2] = p[2];
1474				break;
1475			case 32:
1476				color |= a << 24;
1477				*(uint32_t *)(da.data+j) = color;
1478				break;
1479			}
1480			wc += wcstep;
1481		}
1482		hc += hcstep;
1483	}
1484
1485	gfx_fb_cons_display(&da);
1486	free(da.data);
1487	return (0);
1488}
1489
1490static int
1491load_mapping(int fd, struct font *fp, int n)
1492{
1493	size_t i, size;
1494	ssize_t rv;
1495	struct font_map *mp;
1496
1497	if (fp->vf_map_count[n] == 0)
1498		return (0);
1499
1500	size = fp->vf_map_count[n] * sizeof (*mp);
1501	mp = malloc(size);
1502	if (mp == NULL)
1503		return (ENOMEM);
1504	fp->vf_map[n] = mp;
1505
1506	rv = read(fd, mp, size);
1507	if (rv < 0 || (size_t)rv != size) {
1508		free(fp->vf_map[n]);
1509		fp->vf_map[n] = NULL;
1510		return (EIO);
1511	}
1512
1513	for (i = 0; i < fp->vf_map_count[n]; i++) {
1514		mp[i].font_src = be32toh(mp[i].font_src);
1515		mp[i].font_dst = be16toh(mp[i].font_dst);
1516		mp[i].font_len = be16toh(mp[i].font_len);
1517	}
1518	return (0);
1519}
1520
1521static int
1522builtin_mapping(struct font *fp, int n)
1523{
1524	size_t size;
1525	struct font_map *mp;
1526
1527	if (n >= VFNT_MAPS)
1528		return (EINVAL);
1529
1530	if (fp->vf_map_count[n] == 0)
1531		return (0);
1532
1533	size = fp->vf_map_count[n] * sizeof (*mp);
1534	mp = malloc(size);
1535	if (mp == NULL)
1536		return (ENOMEM);
1537	fp->vf_map[n] = mp;
1538
1539	memcpy(mp, DEFAULT_FONT_DATA.font->vf_map[n], size);
1540	return (0);
1541}
1542
1543/*
1544 * Load font from builtin or from file.
1545 * We do need special case for builtin because the builtin font glyphs
1546 * are compressed and we do need to uncompress them.
1547 * Having single load_font() for both cases will help us to simplify
1548 * font switch handling.
1549 */
1550static bitmap_data_t *
1551load_font(char *path)
1552{
1553	int fd, i;
1554	uint32_t glyphs;
1555	struct font_header fh;
1556	struct fontlist *fl;
1557	bitmap_data_t *bp;
1558	struct font *fp;
1559	size_t size;
1560	ssize_t rv;
1561
1562	/* Get our entry from the font list. */
1563	STAILQ_FOREACH(fl, &fonts, font_next) {
1564		if (strcmp(fl->font_name, path) == 0)
1565			break;
1566	}
1567	if (fl == NULL)
1568		return (NULL);	/* Should not happen. */
1569
1570	bp = fl->font_data;
1571	if (bp->font != NULL && fl->font_flags != FONT_RELOAD)
1572		return (bp);
1573
1574	fd = -1;
1575	/*
1576	 * Special case for builtin font.
1577	 * Builtin font is the very first font we load, we do not have
1578	 * previous loads to be released.
1579	 */
1580	if (fl->font_flags == FONT_BUILTIN) {
1581		if ((fp = calloc(1, sizeof (struct font))) == NULL)
1582			return (NULL);
1583
1584		fp->vf_width = DEFAULT_FONT_DATA.width;
1585		fp->vf_height = DEFAULT_FONT_DATA.height;
1586
1587		fp->vf_bytes = malloc(DEFAULT_FONT_DATA.uncompressed_size);
1588		if (fp->vf_bytes == NULL) {
1589			free(fp);
1590			return (NULL);
1591		}
1592
1593		bp->uncompressed_size = DEFAULT_FONT_DATA.uncompressed_size;
1594		bp->compressed_size = DEFAULT_FONT_DATA.compressed_size;
1595
1596		if (lz4_decompress(DEFAULT_FONT_DATA.compressed_data,
1597		    fp->vf_bytes,
1598		    DEFAULT_FONT_DATA.compressed_size,
1599		    DEFAULT_FONT_DATA.uncompressed_size, 0) != 0) {
1600			free(fp->vf_bytes);
1601			free(fp);
1602			return (NULL);
1603		}
1604
1605		for (i = 0; i < VFNT_MAPS; i++) {
1606			fp->vf_map_count[i] =
1607			    DEFAULT_FONT_DATA.font->vf_map_count[i];
1608			if (builtin_mapping(fp, i) != 0)
1609				goto free_done;
1610		}
1611
1612		bp->font = fp;
1613		return (bp);
1614	}
1615
1616	fd = open(path, O_RDONLY);
1617	if (fd < 0) {
1618		return (NULL);
1619	}
1620
1621	size = sizeof (fh);
1622	rv = read(fd, &fh, size);
1623	if (rv < 0 || (size_t)rv != size) {
1624		bp = NULL;
1625		goto done;
1626	}
1627	if (memcmp(fh.fh_magic, FONT_HEADER_MAGIC, sizeof (fh.fh_magic)) != 0) {
1628		bp = NULL;
1629		goto done;
1630	}
1631	if ((fp = calloc(1, sizeof (struct font))) == NULL) {
1632		bp = NULL;
1633		goto done;
1634	}
1635	for (i = 0; i < VFNT_MAPS; i++)
1636		fp->vf_map_count[i] = be32toh(fh.fh_map_count[i]);
1637
1638	glyphs = be32toh(fh.fh_glyph_count);
1639	fp->vf_width = fh.fh_width;
1640	fp->vf_height = fh.fh_height;
1641
1642	size = howmany(fp->vf_width, 8) * fp->vf_height * glyphs;
1643	bp->uncompressed_size = size;
1644	if ((fp->vf_bytes = malloc(size)) == NULL)
1645		goto free_done;
1646
1647	rv = read(fd, fp->vf_bytes, size);
1648	if (rv < 0 || (size_t)rv != size)
1649		goto free_done;
1650	for (i = 0; i < VFNT_MAPS; i++) {
1651		if (load_mapping(fd, fp, i) != 0)
1652			goto free_done;
1653	}
1654
1655	/*
1656	 * Reset builtin flag now as we have full font loaded.
1657	 */
1658	if (fl->font_flags == FONT_BUILTIN)
1659		fl->font_flags = FONT_AUTO;
1660
1661	/*
1662	 * Release previously loaded entries. We can do this now, as
1663	 * the new font is loaded. Note, there can be no console
1664	 * output till the new font is in place and tem is notified.
1665	 * We do need to keep fl->font_data for glyph dimensions.
1666	 */
1667	STAILQ_FOREACH(fl, &fonts, font_next) {
1668		if (fl->font_data->font == NULL)
1669			continue;
1670
1671		for (i = 0; i < VFNT_MAPS; i++)
1672			free(fl->font_data->font->vf_map[i]);
1673		free(fl->font_data->font->vf_bytes);
1674		free(fl->font_data->font);
1675		fl->font_data->font = NULL;
1676	}
1677
1678	bp->font = fp;
1679	bp->compressed_size = 0;
1680
1681done:
1682	if (fd != -1)
1683		close(fd);
1684	return (bp);
1685
1686free_done:
1687	for (i = 0; i < VFNT_MAPS; i++)
1688		free(fp->vf_map[i]);
1689	free(fp->vf_bytes);
1690	free(fp);
1691	bp = NULL;
1692	goto done;
1693}
1694
1695
1696struct name_entry {
1697	char			*n_name;
1698	SLIST_ENTRY(name_entry)	n_entry;
1699};
1700
1701SLIST_HEAD(name_list, name_entry);
1702
1703/* Read font names from index file. */
1704static struct name_list *
1705read_list(char *fonts)
1706{
1707	struct name_list *nl;
1708	struct name_entry *np;
1709	char buf[PATH_MAX];
1710	int fd, len;
1711
1712	fd = open(fonts, O_RDONLY);
1713	if (fd < 0)
1714		return (NULL);
1715
1716	nl = malloc(sizeof (*nl));
1717	if (nl == NULL) {
1718		close(fd);
1719		return (nl);
1720	}
1721
1722	SLIST_INIT(nl);
1723	while ((len = fgetstr(buf, sizeof (buf), fd)) > 0) {
1724		np = malloc(sizeof (*np));
1725		if (np == NULL) {
1726			close(fd);
1727			return (nl);    /* return what we have */
1728		}
1729		np->n_name = strdup(buf);
1730		if (np->n_name == NULL) {
1731			free(np);
1732			close(fd);
1733			return (nl);    /* return what we have */
1734		}
1735		SLIST_INSERT_HEAD(nl, np, n_entry);
1736	}
1737	close(fd);
1738	return (nl);
1739}
1740
1741/*
1742 * Read the font properties and insert new entry into the list.
1743 * The font list is built in descending order.
1744 */
1745static bool
1746insert_font(char *name, FONT_FLAGS flags)
1747{
1748	struct font_header fh;
1749	struct fontlist *fp, *previous, *entry, *next;
1750	size_t size;
1751	ssize_t rv;
1752	int fd;
1753	char *font_name;
1754
1755	font_name = NULL;
1756	if (flags == FONT_BUILTIN) {
1757		/*
1758		 * We only install builtin font once, while setting up
1759		 * initial console. Since this will happen very early,
1760		 * we assume asprintf will not fail. Once we have access to
1761		 * files, the builtin font will be replaced by font loaded
1762		 * from file.
1763		 */
1764		if (!STAILQ_EMPTY(&fonts))
1765			return (false);
1766
1767		fh.fh_width = DEFAULT_FONT_DATA.width;
1768		fh.fh_height = DEFAULT_FONT_DATA.height;
1769
1770		(void) asprintf(&font_name, "%dx%d",
1771		    DEFAULT_FONT_DATA.width, DEFAULT_FONT_DATA.height);
1772	} else {
1773		fd = open(name, O_RDONLY);
1774		if (fd < 0)
1775			return (false);
1776		rv = read(fd, &fh, sizeof (fh));
1777		close(fd);
1778		if (rv < 0 || (size_t)rv != sizeof (fh))
1779			return (false);
1780
1781		if (memcmp(fh.fh_magic, FONT_HEADER_MAGIC,
1782		    sizeof (fh.fh_magic)) != 0)
1783			return (false);
1784		font_name = strdup(name);
1785	}
1786
1787	if (font_name == NULL)
1788		return (false);
1789
1790	/*
1791	 * If we have an entry with the same glyph dimensions, replace
1792	 * the file name and mark us. We only support unique dimensions.
1793	 */
1794	STAILQ_FOREACH(entry, &fonts, font_next) {
1795		if (fh.fh_width == entry->font_data->width &&
1796		    fh.fh_height == entry->font_data->height) {
1797			free(entry->font_name);
1798			entry->font_name = font_name;
1799			entry->font_flags = FONT_RELOAD;
1800			return (true);
1801		}
1802	}
1803
1804	fp = calloc(sizeof (*fp), 1);
1805	if (fp == NULL) {
1806		free(font_name);
1807		return (false);
1808	}
1809	fp->font_data = calloc(sizeof (*fp->font_data), 1);
1810	if (fp->font_data == NULL) {
1811		free(font_name);
1812		free(fp);
1813		return (false);
1814	}
1815	fp->font_name = font_name;
1816	fp->font_flags = flags;
1817	fp->font_load = load_font;
1818	fp->font_data->width = fh.fh_width;
1819	fp->font_data->height = fh.fh_height;
1820
1821	if (STAILQ_EMPTY(&fonts)) {
1822		STAILQ_INSERT_HEAD(&fonts, fp, font_next);
1823		return (true);
1824	}
1825
1826	previous = NULL;
1827	size = fp->font_data->width * fp->font_data->height;
1828
1829	STAILQ_FOREACH(entry, &fonts, font_next) {
1830		/* Should fp be inserted before the entry? */
1831		if (size > entry->font_data->width * entry->font_data->height) {
1832			if (previous == NULL) {
1833				STAILQ_INSERT_HEAD(&fonts, fp, font_next);
1834			} else {
1835				STAILQ_INSERT_AFTER(&fonts, previous, fp,
1836				    font_next);
1837			}
1838			return (true);
1839		}
1840		next = STAILQ_NEXT(entry, font_next);
1841		if (next == NULL ||
1842		    size > next->font_data->width * next->font_data->height) {
1843			STAILQ_INSERT_AFTER(&fonts, entry, fp, font_next);
1844			return (true);
1845		}
1846		previous = entry;
1847	}
1848	return (true);
1849}
1850
1851static int
1852font_set(struct env_var *ev __unused, int flags __unused, const void *value)
1853{
1854	struct fontlist *fl;
1855	char *eptr;
1856	unsigned long x = 0, y = 0;
1857
1858	/*
1859	 * Attempt to extract values from "XxY" string. In case of error,
1860	 * we have unmaching glyph dimensions and will just output the
1861	 * available values.
1862	 */
1863	if (value != NULL) {
1864		x = strtoul(value, &eptr, 10);
1865		if (*eptr == 'x')
1866			y = strtoul(eptr + 1, &eptr, 10);
1867	}
1868	STAILQ_FOREACH(fl, &fonts, font_next) {
1869		if (fl->font_data->width == x && fl->font_data->height == y)
1870			break;
1871	}
1872	if (fl != NULL) {
1873		/* Reset any FONT_MANUAL flag. */
1874		reset_font_flags();
1875
1876		/* Mark this font manually loaded */
1877		fl->font_flags = FONT_MANUAL;
1878		/* Trigger tem update. */
1879		tems.update_font = true;
1880		plat_cons_update_mode(-1);
1881		return (CMD_OK);
1882	}
1883
1884	printf("Available fonts:\n");
1885	STAILQ_FOREACH(fl, &fonts, font_next) {
1886		printf("    %dx%d\n", fl->font_data->width,
1887		    fl->font_data->height);
1888	}
1889	return (CMD_OK);
1890}
1891
1892void
1893bios_text_font(bool use_vga_font)
1894{
1895	if (use_vga_font)
1896		(void) insert_font(VGA_8X16_FONT, FONT_MANUAL);
1897	else
1898		(void) insert_font(DEFAULT_8X16_FONT, FONT_MANUAL);
1899	tems.update_font = true;
1900}
1901
1902void
1903autoload_font(bool bios)
1904{
1905	struct name_list *nl;
1906	struct name_entry *np;
1907
1908	nl = read_list("/boot/fonts/fonts.dir");
1909	if (nl == NULL)
1910		return;
1911
1912	while (!SLIST_EMPTY(nl)) {
1913		np = SLIST_FIRST(nl);
1914		SLIST_REMOVE_HEAD(nl, n_entry);
1915		if (insert_font(np->n_name, FONT_AUTO) == false)
1916			printf("failed to add font: %s\n", np->n_name);
1917		free(np->n_name);
1918		free(np);
1919	}
1920
1921	unsetenv("screen-font");
1922	env_setenv("screen-font", EV_VOLATILE, NULL, font_set, env_nounset);
1923
1924	/*
1925	 * If vga text mode was requested, load vga.font (8x16 bold) font.
1926	 */
1927	if (bios) {
1928		bios_text_font(true);
1929	}
1930
1931	/* Trigger tem update. */
1932	tems.update_font = true;
1933	plat_cons_update_mode(-1);
1934}
1935
1936COMMAND_SET(load_font, "loadfont", "load console font from file", command_font);
1937
1938static int
1939command_font(int argc, char *argv[])
1940{
1941	int i, c, rc = CMD_OK;
1942	struct fontlist *fl;
1943	bool list;
1944
1945	list = false;
1946	optind = 1;
1947	optreset = 1;
1948	rc = CMD_OK;
1949
1950	while ((c = getopt(argc, argv, "l")) != -1) {
1951		switch (c) {
1952		case 'l':
1953			list = true;
1954			break;
1955		case '?':
1956		default:
1957			return (CMD_ERROR);
1958		}
1959	}
1960
1961	argc -= optind;
1962	argv += optind;
1963
1964	if (argc > 1 || (list && argc != 0)) {
1965		printf("Usage: loadfont [-l] | [file.fnt]\n");
1966		return (CMD_ERROR);
1967	}
1968
1969	if (list) {
1970		STAILQ_FOREACH(fl, &fonts, font_next) {
1971			printf("font %s: %dx%d%s\n", fl->font_name,
1972			    fl->font_data->width,
1973			    fl->font_data->height,
1974			    fl->font_data->font == NULL? "" : " loaded");
1975		}
1976		return (CMD_OK);
1977	}
1978
1979	if (argc == 1) {
1980		char *name = argv[0];
1981
1982		if (insert_font(name, FONT_MANUAL) == false) {
1983			printf("loadfont error: failed to load: %s\n", name);
1984			return (CMD_ERROR);
1985		}
1986
1987		tems.update_font = true;
1988		plat_cons_update_mode(-1);
1989		return (CMD_OK);
1990	}
1991
1992	if (argc == 0) {
1993		/*
1994		 * Walk entire font list, release any loaded font, and set
1995		 * autoload flag. The font list does have at least the builtin
1996		 * default font.
1997		 */
1998		STAILQ_FOREACH(fl, &fonts, font_next) {
1999			if (fl->font_data->font != NULL) {
2000				/* Note the tem is releasing font bytes */
2001				for (i = 0; i < VFNT_MAPS; i++)
2002					free(fl->font_data->font->vf_map[i]);
2003				free(fl->font_data->font);
2004				fl->font_data->font = NULL;
2005				fl->font_data->uncompressed_size = 0;
2006				fl->font_flags = FONT_AUTO;
2007			}
2008		}
2009		tems.update_font = true;
2010		plat_cons_update_mode(-1);
2011	}
2012	return (rc);
2013}
2014
2015bool
2016gfx_get_edid_resolution(struct vesa_edid_info *edid, edid_res_list_t *res)
2017{
2018	struct resolution *rp, *p;
2019
2020	/*
2021	 * Walk detailed timings tables (4).
2022	 */
2023	if ((edid->display.supported_features
2024	    & EDID_FEATURE_PREFERRED_TIMING_MODE) != 0) {
2025		/* Walk detailed timing descriptors (4) */
2026		for (int i = 0; i < DET_TIMINGS; i++) {
2027			/*
2028			 * Reserved value 0 is not used for display decriptor.
2029			 */
2030			if (edid->detailed_timings[i].pixel_clock == 0)
2031				continue;
2032			if ((rp = malloc(sizeof (*rp))) == NULL)
2033				continue;
2034			rp->width = GET_EDID_INFO_WIDTH(edid, i);
2035			rp->height = GET_EDID_INFO_HEIGHT(edid, i);
2036			if (rp->width > 0 && rp->width <= EDID_MAX_PIXELS &&
2037			    rp->height > 0 && rp->height <= EDID_MAX_LINES)
2038				TAILQ_INSERT_TAIL(res, rp, next);
2039			else
2040				free(rp);
2041		}
2042	}
2043
2044	/*
2045	 * Walk standard timings list (8).
2046	 */
2047	for (int i = 0; i < STD_TIMINGS; i++) {
2048		/* Is this field unused? */
2049		if (edid->standard_timings[i] == 0x0101)
2050			continue;
2051
2052		if ((rp = malloc(sizeof (*rp))) == NULL)
2053			continue;
2054
2055		rp->width = HSIZE(edid->standard_timings[i]);
2056		switch (RATIO(edid->standard_timings[i])) {
2057		case RATIO1_1:
2058			rp->height = HSIZE(edid->standard_timings[i]);
2059			if (edid->header.version > 1 ||
2060			    edid->header.revision > 2) {
2061				rp->height = rp->height * 10 / 16;
2062			}
2063			break;
2064		case RATIO4_3:
2065			rp->height = HSIZE(edid->standard_timings[i]) * 3 / 4;
2066			break;
2067		case RATIO5_4:
2068			rp->height = HSIZE(edid->standard_timings[i]) * 4 / 5;
2069			break;
2070		case RATIO16_9:
2071			rp->height = HSIZE(edid->standard_timings[i]) * 9 / 16;
2072			break;
2073		}
2074
2075		/*
2076		 * Create resolution list in decreasing order, except keep
2077		 * first entry (preferred timing mode).
2078		 */
2079		TAILQ_FOREACH(p, res, next) {
2080			if (p->width * p->height < rp->width * rp->height) {
2081				/* Keep preferred mode first */
2082				if (TAILQ_FIRST(res) == p)
2083					TAILQ_INSERT_AFTER(res, p, rp, next);
2084				else
2085					TAILQ_INSERT_BEFORE(p, rp, next);
2086				break;
2087			}
2088			if (TAILQ_NEXT(p, next) == NULL) {
2089				TAILQ_INSERT_TAIL(res, rp, next);
2090				break;
2091			}
2092		}
2093	}
2094	return (!TAILQ_EMPTY(res));
2095}
2096