xref: /freebsd/stand/common/gfx_fb.c (revision 21b5b8b38b1c606fb8f6b1b0e3549072b3c1d44d)
13630506bSToomas Soome /*-
23630506bSToomas Soome  * SPDX-License-Identifier: BSD-2-Clause
33630506bSToomas Soome  *
43630506bSToomas Soome  * Copyright 2020 Toomas Soome
53630506bSToomas Soome  * Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
63630506bSToomas Soome  * Copyright 2020 RackTop Systems, Inc.
73630506bSToomas Soome  *
83630506bSToomas Soome  * Redistribution and use in source and binary forms, with or without
93630506bSToomas Soome  * modification, are permitted provided that the following conditions
103630506bSToomas Soome  * are met:
113630506bSToomas Soome  * 1. Redistributions of source code must retain the above copyright
123630506bSToomas Soome  *    notice, this list of conditions and the following disclaimer.
133630506bSToomas Soome  * 2. Redistributions in binary form must reproduce the above copyright
143630506bSToomas Soome  *    notice, this list of conditions and the following disclaimer in the
153630506bSToomas Soome  *    documentation and/or other materials provided with the distribution.
163630506bSToomas Soome  *
173630506bSToomas Soome  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
183630506bSToomas Soome  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
193630506bSToomas Soome  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
203630506bSToomas Soome  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
213630506bSToomas Soome  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
223630506bSToomas Soome  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
233630506bSToomas Soome  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
243630506bSToomas Soome  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
253630506bSToomas Soome  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
263630506bSToomas Soome  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
273630506bSToomas Soome  * SUCH DAMAGE.
283630506bSToomas Soome  */
293630506bSToomas Soome 
301caed70cSToomas Soome /*
311caed70cSToomas Soome  * The workhorse here is gfxfb_blt(). It is implemented to mimic UEFI
321caed70cSToomas Soome  * GOP Blt, and allows us to fill the rectangle on screen, copy
331caed70cSToomas Soome  * rectangle from video to buffer and buffer to video and video to video.
341caed70cSToomas Soome  * Such implementation does allow us to have almost identical implementation
351caed70cSToomas Soome  * for both BIOS VBE and UEFI.
361caed70cSToomas Soome  *
371caed70cSToomas Soome  * ALL pixel data is assumed to be 32-bit BGRA (byte order Blue, Green, Red,
381caed70cSToomas Soome  * Alpha) format, this allows us to only handle RGB data and not to worry
391caed70cSToomas Soome  * about mixing RGB with indexed colors.
401caed70cSToomas Soome  * Data exchange between memory buffer and video will translate BGRA
411caed70cSToomas Soome  * and native format as following:
421caed70cSToomas Soome  *
431caed70cSToomas Soome  * 32-bit to/from 32-bit is trivial case.
441caed70cSToomas Soome  * 32-bit to/from 24-bit is also simple - we just drop the alpha channel.
451caed70cSToomas Soome  * 32-bit to/from 16-bit is more complicated, because we nee to handle
461caed70cSToomas Soome  * data loss from 32-bit to 16-bit. While reading/writing from/to video, we
471caed70cSToomas Soome  * need to apply masks of 16-bit color components. This will preserve
481caed70cSToomas Soome  * colors for terminal text. For 32-bit truecolor PMG images, we need to
491caed70cSToomas Soome  * translate 32-bit colors to 15/16 bit colors and this means data loss.
501caed70cSToomas Soome  * There are different algorithms how to perform such color space reduction,
511caed70cSToomas Soome  * we are currently using bitwise right shift to reduce color space and so far
521caed70cSToomas Soome  * this technique seems to be sufficient (see also gfx_fb_putimage(), the
531caed70cSToomas Soome  * end of for loop).
541caed70cSToomas Soome  * 32-bit to/from 8-bit is the most troublesome because 8-bit colors are
551caed70cSToomas Soome  * indexed. From video, we do get color indexes, and we do translate
561caed70cSToomas Soome  * color index values to RGB. To write to video, we again need to translate
571caed70cSToomas Soome  * RGB to color index. Additionally, we need to translate between VGA and
581caed70cSToomas Soome  * console colors.
591caed70cSToomas Soome  *
601caed70cSToomas Soome  * Our internal color data is represented using BGRA format. But the hardware
611caed70cSToomas Soome  * used indexed colors for 8-bit colors (0-255) and for this mode we do
621caed70cSToomas Soome  * need to perform translation to/from BGRA and index values.
631caed70cSToomas Soome  *
641caed70cSToomas Soome  *                   - paletteentry RGB <-> index -
651caed70cSToomas Soome  * BGRA BUFFER <----/                              \ - VIDEO
661caed70cSToomas Soome  *                  \                              /
671caed70cSToomas Soome  *                   -  RGB (16/24/32)            -
681caed70cSToomas Soome  *
691caed70cSToomas Soome  * To perform index to RGB translation, we use palette table generated
701caed70cSToomas Soome  * from when we set up 8-bit mode video. We cannot read palette data from
711caed70cSToomas Soome  * the hardware, because not all hardware supports reading it.
721caed70cSToomas Soome  *
731caed70cSToomas Soome  * BGRA to index is implemented in rgb_to_color_index() by searching
741caed70cSToomas Soome  * palette array for closest match of RBG values.
751caed70cSToomas Soome  *
761caed70cSToomas Soome  * Note: In 8-bit mode, We do store first 16 colors to palette registers
771caed70cSToomas Soome  * in VGA color order, this serves two purposes; firstly,
781caed70cSToomas Soome  * if palette update is not supported, we still have correct 16 colors.
791caed70cSToomas Soome  * Secondly, the kernel does get correct 16 colors when some other boot
801caed70cSToomas Soome  * loader is used. However, the palette map for 8-bit colors is using
811caed70cSToomas Soome  * console color ordering - this does allow us to skip translation
821caed70cSToomas Soome  * from VGA colors to console colors, while we are reading RGB data.
831caed70cSToomas Soome  */
841caed70cSToomas Soome 
853630506bSToomas Soome #include <sys/param.h>
863630506bSToomas Soome #include <stand.h>
873630506bSToomas Soome #include <teken.h>
883630506bSToomas Soome #include <gfx_fb.h>
893630506bSToomas Soome #include <sys/font.h>
9000460cc8SEmmanuel Vadot #include <sys/splash.h>
9118968b82SWarner Losh #include <sys/linker.h>
9218968b82SWarner Losh #include <sys/module.h>
933630506bSToomas Soome #include <sys/stdint.h>
943630506bSToomas Soome #include <sys/endian.h>
953630506bSToomas Soome #include <pnglite.h>
963630506bSToomas Soome #include <bootstrap.h>
973630506bSToomas Soome #include <lz4.h>
983630506bSToomas Soome #if defined(EFI)
993630506bSToomas Soome #include <efi.h>
1003630506bSToomas Soome #include <efilib.h>
1013630506bSToomas Soome #else
1023630506bSToomas Soome #include <vbe.h>
1033630506bSToomas Soome #endif
1043630506bSToomas Soome 
10586077f4fSAhmad Khalifa #include "modinfo.h"
10686077f4fSAhmad Khalifa 
1073630506bSToomas Soome /* VGA text mode does use bold font. */
1083630506bSToomas Soome #if !defined(VGA_8X16_FONT)
10920fb2ea2SToomas Soome #define	VGA_8X16_FONT		"/boot/fonts/8x16b.fnt"
1103630506bSToomas Soome #endif
1113630506bSToomas Soome #if !defined(DEFAULT_8X16_FONT)
1123630506bSToomas Soome #define	DEFAULT_8X16_FONT	"/boot/fonts/8x16.fnt"
1133630506bSToomas Soome #endif
1143630506bSToomas Soome 
1153630506bSToomas Soome /*
1163630506bSToomas Soome  * Must be sorted by font size in descending order
1173630506bSToomas Soome  */
1183630506bSToomas Soome font_list_t fonts = STAILQ_HEAD_INITIALIZER(fonts);
1193630506bSToomas Soome 
1203630506bSToomas Soome #define	DEFAULT_FONT_DATA	font_data_8x16
1213630506bSToomas Soome extern vt_font_bitmap_data_t	font_data_8x16;
1223630506bSToomas Soome teken_gfx_t gfx_state = { 0 };
1233630506bSToomas Soome 
1243630506bSToomas Soome static struct {
1253630506bSToomas Soome 	unsigned char r;	/* Red percentage value. */
1263630506bSToomas Soome 	unsigned char g;	/* Green percentage value. */
1273630506bSToomas Soome 	unsigned char b;	/* Blue percentage value. */
1283630506bSToomas Soome } color_def[NCOLORS] = {
1293630506bSToomas Soome 	{0,	0,	0},	/* black */
1303630506bSToomas Soome 	{50,	0,	0},	/* dark red */
1313630506bSToomas Soome 	{0,	50,	0},	/* dark green */
1323630506bSToomas Soome 	{77,	63,	0},	/* dark yellow */
1333630506bSToomas Soome 	{20,	40,	64},	/* dark blue */
1343630506bSToomas Soome 	{50,	0,	50},	/* dark magenta */
1353630506bSToomas Soome 	{0,	50,	50},	/* dark cyan */
1363630506bSToomas Soome 	{75,	75,	75},	/* light gray */
1373630506bSToomas Soome 
1383630506bSToomas Soome 	{18,	20,	21},	/* dark gray */
1393630506bSToomas Soome 	{100,	0,	0},	/* light red */
1403630506bSToomas Soome 	{0,	100,	0},	/* light green */
1413630506bSToomas Soome 	{100,	100,	0},	/* light yellow */
1423630506bSToomas Soome 	{45,	62,	81},	/* light blue */
1433630506bSToomas Soome 	{100,	0,	100},	/* light magenta */
1443630506bSToomas Soome 	{0,	100,	100},	/* light cyan */
1453630506bSToomas Soome 	{100,	100,	100},	/* white */
1463630506bSToomas Soome };
1473630506bSToomas Soome uint32_t cmap[NCMAP];
1483630506bSToomas Soome 
1493630506bSToomas Soome /*
1503630506bSToomas Soome  * Between console's palette and VGA's one:
1513630506bSToomas Soome  *  - blue and red are swapped (1 <-> 4)
1523630506bSToomas Soome  *  - yellow and cyan are swapped (3 <-> 6)
1533630506bSToomas Soome  */
1543630506bSToomas Soome const int cons_to_vga_colors[NCOLORS] = {
1553630506bSToomas Soome 	0,  4,  2,  6,  1,  5,  3,  7,
1563630506bSToomas Soome 	8, 12, 10, 14,  9, 13, 11, 15
1573630506bSToomas Soome };
1583630506bSToomas Soome 
1593630506bSToomas Soome static const int vga_to_cons_colors[NCOLORS] = {
1603630506bSToomas Soome 	0,  1,  2,  3,  4,  5,  6,  7,
1613630506bSToomas Soome 	8,  9, 10, 11,  12, 13, 14, 15
1623630506bSToomas Soome };
1633630506bSToomas Soome 
164*21b5b8b3SToomas Soome /*
165*21b5b8b3SToomas Soome  * It is reported very slow console draw in some systems.
166*21b5b8b3SToomas Soome  * in order to exclude buggy gop->Blt(), we want option
167*21b5b8b3SToomas Soome  * to use direct draw to framebuffer and avoid gop->Blt.
168*21b5b8b3SToomas Soome  * Can be toggled with "gop" command.
169*21b5b8b3SToomas Soome  */
170*21b5b8b3SToomas Soome bool ignore_gop_blt = false;
171*21b5b8b3SToomas Soome 
1723630506bSToomas Soome struct text_pixel *screen_buffer;
1733630506bSToomas Soome #if defined(EFI)
1743630506bSToomas Soome static EFI_GRAPHICS_OUTPUT_BLT_PIXEL *GlyphBuffer;
1753630506bSToomas Soome #else
1763630506bSToomas Soome static struct paletteentry *GlyphBuffer;
1773630506bSToomas Soome #endif
1783630506bSToomas Soome static size_t GlyphBufferSize;
1793630506bSToomas Soome 
1803630506bSToomas Soome static bool insert_font(char *, FONT_FLAGS);
1813630506bSToomas Soome static int font_set(struct env_var *, int, const void *);
1823630506bSToomas Soome static void * allocate_glyphbuffer(uint32_t, uint32_t);
1833630506bSToomas Soome static void gfx_fb_cursor_draw(teken_gfx_t *, const teken_pos_t *, bool);
1843630506bSToomas Soome 
1853630506bSToomas Soome /*
1863630506bSToomas Soome  * Initialize gfx framework.
1873630506bSToomas Soome  */
1883630506bSToomas Soome void
gfx_framework_init(void)1893630506bSToomas Soome gfx_framework_init(void)
1903630506bSToomas Soome {
1913630506bSToomas Soome 	/*
1923630506bSToomas Soome 	 * Setup font list to have builtin font.
1933630506bSToomas Soome 	 */
1943630506bSToomas Soome 	(void) insert_font(NULL, FONT_BUILTIN);
1956faf55c8SWarner Losh 	gfx_interp_ref();	/* Draw in the gfx interpreter for this thing */
1963630506bSToomas Soome }
1973630506bSToomas Soome 
1983630506bSToomas Soome static uint8_t *
gfx_get_fb_address(void)1993630506bSToomas Soome gfx_get_fb_address(void)
2003630506bSToomas Soome {
2013630506bSToomas Soome 	return (ptov((uint32_t)gfx_state.tg_fb.fb_addr));
2023630506bSToomas Soome }
2033630506bSToomas Soome 
2043630506bSToomas Soome /*
2053630506bSToomas Soome  * Utility function to parse gfx mode line strings.
2063630506bSToomas Soome  */
2073630506bSToomas Soome bool
gfx_parse_mode_str(char * str,int * x,int * y,int * depth)2083630506bSToomas Soome gfx_parse_mode_str(char *str, int *x, int *y, int *depth)
2093630506bSToomas Soome {
2103630506bSToomas Soome 	char *p, *end;
2113630506bSToomas Soome 
2123630506bSToomas Soome 	errno = 0;
2133630506bSToomas Soome 	p = str;
2143630506bSToomas Soome 	*x = strtoul(p, &end, 0);
2153630506bSToomas Soome 	if (*x == 0 || errno != 0)
2163630506bSToomas Soome 		return (false);
2173630506bSToomas Soome 	if (*end != 'x')
2183630506bSToomas Soome 		return (false);
2193630506bSToomas Soome 	p = end + 1;
2203630506bSToomas Soome 	*y = strtoul(p, &end, 0);
2213630506bSToomas Soome 	if (*y == 0 || errno != 0)
2223630506bSToomas Soome 		return (false);
2233630506bSToomas Soome 	if (*end != 'x') {
2243630506bSToomas Soome 		*depth = -1;    /* auto select */
2253630506bSToomas Soome 	} else {
2263630506bSToomas Soome 		p = end + 1;
2273630506bSToomas Soome 		*depth = strtoul(p, &end, 0);
2283630506bSToomas Soome 		if (*depth == 0 || errno != 0 || *end != '\0')
2293630506bSToomas Soome 			return (false);
2303630506bSToomas Soome 	}
2313630506bSToomas Soome 
2323630506bSToomas Soome 	return (true);
2333630506bSToomas Soome }
2343630506bSToomas Soome 
2353630506bSToomas Soome static uint32_t
rgb_color_map(uint8_t index,uint32_t rmax,int roffset,uint32_t gmax,int goffset,uint32_t bmax,int boffset)2363630506bSToomas Soome rgb_color_map(uint8_t index, uint32_t rmax, int roffset,
2373630506bSToomas Soome     uint32_t gmax, int goffset, uint32_t bmax, int boffset)
2383630506bSToomas Soome {
2393630506bSToomas Soome 	uint32_t color, code, gray, level;
2403630506bSToomas Soome 
2413630506bSToomas Soome 	if (index < NCOLORS) {
2423630506bSToomas Soome #define	CF(_f, _i) ((_f ## max * color_def[(_i)]._f / 100) << _f ## offset)
2433630506bSToomas Soome 		return (CF(r, index) | CF(g, index) | CF(b, index));
2443630506bSToomas Soome #undef  CF
2453630506bSToomas Soome         }
2463630506bSToomas Soome 
2473630506bSToomas Soome #define	CF(_f, _c) ((_f ## max & _c) << _f ## offset)
2483630506bSToomas Soome         /* 6x6x6 color cube */
2493630506bSToomas Soome         if (index > 15 && index < 232) {
2503630506bSToomas Soome                 uint32_t red, green, blue;
2513630506bSToomas Soome 
2523630506bSToomas Soome                 for (red = 0; red < 6; red++) {
2533630506bSToomas Soome                         for (green = 0; green < 6; green++) {
2543630506bSToomas Soome                                 for (blue = 0; blue < 6; blue++) {
2553630506bSToomas Soome                                         code = 16 + (red * 36) +
2563630506bSToomas Soome                                             (green * 6) + blue;
2573630506bSToomas Soome                                         if (code != index)
2583630506bSToomas Soome                                                 continue;
2593630506bSToomas Soome                                         red = red ? (red * 40 + 55) : 0;
2603630506bSToomas Soome                                         green = green ? (green * 40 + 55) : 0;
2613630506bSToomas Soome                                         blue = blue ? (blue * 40 + 55) : 0;
2623630506bSToomas Soome                                         color = CF(r, red);
2633630506bSToomas Soome 					color |= CF(g, green);
2643630506bSToomas Soome 					color |= CF(b, blue);
2653630506bSToomas Soome 					return (color);
2663630506bSToomas Soome                                 }
2673630506bSToomas Soome                         }
2683630506bSToomas Soome                 }
2693630506bSToomas Soome         }
2703630506bSToomas Soome 
2713630506bSToomas Soome         /* colors 232-255 are a grayscale ramp */
2723630506bSToomas Soome         for (gray = 0; gray < 24; gray++) {
2733630506bSToomas Soome                 level = (gray * 10) + 8;
2743630506bSToomas Soome                 code = 232 + gray;
2753630506bSToomas Soome                 if (code == index)
2763630506bSToomas Soome                         break;
2773630506bSToomas Soome         }
2783630506bSToomas Soome         return (CF(r, level) | CF(g, level) | CF(b, level));
2793630506bSToomas Soome #undef  CF
2803630506bSToomas Soome }
2813630506bSToomas Soome 
2823630506bSToomas Soome /*
2833630506bSToomas Soome  * Support for color mapping.
2843630506bSToomas Soome  * For 8, 24 and 32 bit depth, use mask size 8.
2853630506bSToomas Soome  * 15/16 bit depth needs to use mask size from mode,
2863630506bSToomas Soome  * or we will lose color information from 32-bit to 15/16 bit translation.
2873630506bSToomas Soome  */
2883630506bSToomas Soome uint32_t
gfx_fb_color_map(uint8_t index)2893630506bSToomas Soome gfx_fb_color_map(uint8_t index)
2903630506bSToomas Soome {
2913630506bSToomas Soome 	int rmask, gmask, bmask;
2923630506bSToomas Soome 	int roff, goff, boff, bpp;
2933630506bSToomas Soome 
2943630506bSToomas Soome 	roff = ffs(gfx_state.tg_fb.fb_mask_red) - 1;
2953630506bSToomas Soome         goff = ffs(gfx_state.tg_fb.fb_mask_green) - 1;
2963630506bSToomas Soome         boff = ffs(gfx_state.tg_fb.fb_mask_blue) - 1;
2973630506bSToomas Soome 	bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3;
2983630506bSToomas Soome 
2993630506bSToomas Soome 	if (bpp == 2)
3003630506bSToomas Soome 		rmask = gfx_state.tg_fb.fb_mask_red >> roff;
3013630506bSToomas Soome 	else
3023630506bSToomas Soome 		rmask = 0xff;
3033630506bSToomas Soome 
3043630506bSToomas Soome 	if (bpp == 2)
3053630506bSToomas Soome 		gmask = gfx_state.tg_fb.fb_mask_green >> goff;
3063630506bSToomas Soome 	else
3073630506bSToomas Soome 		gmask = 0xff;
3083630506bSToomas Soome 
3093630506bSToomas Soome 	if (bpp == 2)
3103630506bSToomas Soome 		bmask = gfx_state.tg_fb.fb_mask_blue >> boff;
3113630506bSToomas Soome 	else
3123630506bSToomas Soome 		bmask = 0xff;
3133630506bSToomas Soome 
3143630506bSToomas Soome 	return (rgb_color_map(index, rmask, 16, gmask, 8, bmask, 0));
3153630506bSToomas Soome }
3163630506bSToomas Soome 
3174bbfe4bfSToomas Soome /*
3184bbfe4bfSToomas Soome  * Get indexed color from RGB. This function is used to write data to video
3194bbfe4bfSToomas Soome  * memory when the adapter is set to use indexed colors.
3204bbfe4bfSToomas Soome  * Since UEFI does only support 32-bit colors, we do not implement it for
3214bbfe4bfSToomas Soome  * UEFI because there is no need for it and we do not have palette array
3224bbfe4bfSToomas Soome  * for UEFI.
3234bbfe4bfSToomas Soome  */
3243630506bSToomas Soome static uint8_t
rgb_to_color_index(uint8_t r,uint8_t g,uint8_t b)3253630506bSToomas Soome rgb_to_color_index(uint8_t r, uint8_t g, uint8_t b)
3263630506bSToomas Soome {
3273630506bSToomas Soome #if !defined(EFI)
3283630506bSToomas Soome 	uint32_t color, best, dist, k;
3293630506bSToomas Soome 	int diff;
3303630506bSToomas Soome 
3313630506bSToomas Soome 	color = 0;
3321caed70cSToomas Soome 	best = 255 * 255 * 255;
3333630506bSToomas Soome 	for (k = 0; k < NCMAP; k++) {
3343630506bSToomas Soome 		diff = r - pe8[k].Red;
3353630506bSToomas Soome 		dist = diff * diff;
3363630506bSToomas Soome 		diff = g - pe8[k].Green;
3373630506bSToomas Soome 		dist += diff * diff;
3383630506bSToomas Soome 		diff = b - pe8[k].Blue;
3393630506bSToomas Soome 		dist += diff * diff;
3403630506bSToomas Soome 
3414bbfe4bfSToomas Soome 		/* Exact match, exit the loop */
3423630506bSToomas Soome 		if (dist == 0)
3433630506bSToomas Soome 			break;
3444bbfe4bfSToomas Soome 
3453630506bSToomas Soome 		if (dist < best) {
3463630506bSToomas Soome 			color = k;
3473630506bSToomas Soome 			best = dist;
3483630506bSToomas Soome 		}
3493630506bSToomas Soome 	}
3503630506bSToomas Soome 	if (k == NCMAP)
3513630506bSToomas Soome 		k = color;
3523630506bSToomas Soome 	return (k);
3533630506bSToomas Soome #else
3543630506bSToomas Soome 	(void) r;
3553630506bSToomas Soome 	(void) g;
3563630506bSToomas Soome 	(void) b;
3573630506bSToomas Soome 	return (0);
3583630506bSToomas Soome #endif
3593630506bSToomas Soome }
3603630506bSToomas Soome 
3613630506bSToomas Soome int
generate_cons_palette(uint32_t * palette,int format,uint32_t rmax,int roffset,uint32_t gmax,int goffset,uint32_t bmax,int boffset)3623630506bSToomas Soome generate_cons_palette(uint32_t *palette, int format,
3633630506bSToomas Soome     uint32_t rmax, int roffset, uint32_t gmax, int goffset,
3643630506bSToomas Soome     uint32_t bmax, int boffset)
3653630506bSToomas Soome {
3663630506bSToomas Soome 	int i;
3673630506bSToomas Soome 
3683630506bSToomas Soome 	switch (format) {
3693630506bSToomas Soome 	case COLOR_FORMAT_VGA:
3703630506bSToomas Soome 		for (i = 0; i < NCOLORS; i++)
3713630506bSToomas Soome 			palette[i] = cons_to_vga_colors[i];
3723630506bSToomas Soome 		for (; i < NCMAP; i++)
3733630506bSToomas Soome 			palette[i] = i;
3743630506bSToomas Soome 		break;
3753630506bSToomas Soome 	case COLOR_FORMAT_RGB:
3763630506bSToomas Soome 		for (i = 0; i < NCMAP; i++)
3773630506bSToomas Soome 			palette[i] = rgb_color_map(i, rmax, roffset,
3783630506bSToomas Soome 			    gmax, goffset, bmax, boffset);
3793630506bSToomas Soome 		break;
3803630506bSToomas Soome 	default:
3813630506bSToomas Soome 		return (ENODEV);
3823630506bSToomas Soome 	}
3833630506bSToomas Soome 
3843630506bSToomas Soome 	return (0);
3853630506bSToomas Soome }
3863630506bSToomas Soome 
3873630506bSToomas Soome static void
gfx_mem_wr1(uint8_t * base,size_t size,uint32_t o,uint8_t v)3883630506bSToomas Soome gfx_mem_wr1(uint8_t *base, size_t size, uint32_t o, uint8_t v)
3893630506bSToomas Soome {
3903630506bSToomas Soome 
3913630506bSToomas Soome 	if (o >= size)
3923630506bSToomas Soome 		return;
3933630506bSToomas Soome 	*(uint8_t *)(base + o) = v;
3943630506bSToomas Soome }
3953630506bSToomas Soome 
3963630506bSToomas Soome static void
gfx_mem_wr2(uint8_t * base,size_t size,uint32_t o,uint16_t v)3973630506bSToomas Soome gfx_mem_wr2(uint8_t *base, size_t size, uint32_t o, uint16_t v)
3983630506bSToomas Soome {
3993630506bSToomas Soome 
4003630506bSToomas Soome 	if (o >= size)
4013630506bSToomas Soome 		return;
4023630506bSToomas Soome 	*(uint16_t *)(base + o) = v;
4033630506bSToomas Soome }
4043630506bSToomas Soome 
4053630506bSToomas Soome static void
gfx_mem_wr4(uint8_t * base,size_t size,uint32_t o,uint32_t v)4063630506bSToomas Soome gfx_mem_wr4(uint8_t *base, size_t size, uint32_t o, uint32_t v)
4073630506bSToomas Soome {
4083630506bSToomas Soome 
4093630506bSToomas Soome 	if (o >= size)
4103630506bSToomas Soome 		return;
4113630506bSToomas Soome 	*(uint32_t *)(base + o) = v;
4123630506bSToomas Soome }
4133630506bSToomas Soome 
gfxfb_blt_fill(void * BltBuffer,uint32_t DestinationX,uint32_t DestinationY,uint32_t Width,uint32_t Height)4143630506bSToomas Soome static int gfxfb_blt_fill(void *BltBuffer,
4153630506bSToomas Soome     uint32_t DestinationX, uint32_t DestinationY,
4163630506bSToomas Soome     uint32_t Width, uint32_t Height)
4173630506bSToomas Soome {
4183630506bSToomas Soome #if defined(EFI)
4193630506bSToomas Soome 	EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
4203630506bSToomas Soome #else
4213630506bSToomas Soome 	struct paletteentry *p;
4223630506bSToomas Soome #endif
4233630506bSToomas Soome 	uint32_t data, bpp, pitch, y, x;
4243630506bSToomas Soome 	int roff, goff, boff;
4253630506bSToomas Soome 	size_t size;
4263630506bSToomas Soome 	off_t off;
4273630506bSToomas Soome 	uint8_t *destination;
4283630506bSToomas Soome 
4293630506bSToomas Soome 	if (BltBuffer == NULL)
4303630506bSToomas Soome 		return (EINVAL);
4313630506bSToomas Soome 
4323630506bSToomas Soome 	if (DestinationY + Height > gfx_state.tg_fb.fb_height)
4333630506bSToomas Soome 		return (EINVAL);
4343630506bSToomas Soome 
4353630506bSToomas Soome 	if (DestinationX + Width > gfx_state.tg_fb.fb_width)
4363630506bSToomas Soome 		return (EINVAL);
4373630506bSToomas Soome 
4383630506bSToomas Soome 	if (Width == 0 || Height == 0)
4393630506bSToomas Soome 		return (EINVAL);
4403630506bSToomas Soome 
4413630506bSToomas Soome 	p = BltBuffer;
4423630506bSToomas Soome 	roff = ffs(gfx_state.tg_fb.fb_mask_red) - 1;
4433630506bSToomas Soome 	goff = ffs(gfx_state.tg_fb.fb_mask_green) - 1;
4443630506bSToomas Soome 	boff = ffs(gfx_state.tg_fb.fb_mask_blue) - 1;
4453630506bSToomas Soome 
4463630506bSToomas Soome 	if (gfx_state.tg_fb.fb_bpp == 8) {
4473630506bSToomas Soome 		data = rgb_to_color_index(p->Red, p->Green, p->Blue);
4483630506bSToomas Soome 	} else {
4493630506bSToomas Soome 		data = (p->Red &
4503630506bSToomas Soome 		    (gfx_state.tg_fb.fb_mask_red >> roff)) << roff;
4513630506bSToomas Soome 		data |= (p->Green &
4523630506bSToomas Soome 		    (gfx_state.tg_fb.fb_mask_green >> goff)) << goff;
4533630506bSToomas Soome 		data |= (p->Blue &
4543630506bSToomas Soome 		    (gfx_state.tg_fb.fb_mask_blue >> boff)) << boff;
4553630506bSToomas Soome 	}
4563630506bSToomas Soome 
4573630506bSToomas Soome 	bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3;
4583630506bSToomas Soome 	pitch = gfx_state.tg_fb.fb_stride * bpp;
4593630506bSToomas Soome 	destination = gfx_get_fb_address();
4603630506bSToomas Soome 	size = gfx_state.tg_fb.fb_size;
4613630506bSToomas Soome 
4623630506bSToomas Soome 	for (y = DestinationY; y < Height + DestinationY; y++) {
4633630506bSToomas Soome 		off = y * pitch + DestinationX * bpp;
4643630506bSToomas Soome 		for (x = 0; x < Width; x++) {
4653630506bSToomas Soome 			switch (bpp) {
4663630506bSToomas Soome 			case 1:
4673630506bSToomas Soome 				gfx_mem_wr1(destination, size, off,
4683630506bSToomas Soome 				    (data < NCOLORS) ?
4693630506bSToomas Soome 				    cons_to_vga_colors[data] : data);
4703630506bSToomas Soome 				break;
4713630506bSToomas Soome 			case 2:
4723630506bSToomas Soome 				gfx_mem_wr2(destination, size, off, data);
4733630506bSToomas Soome 				break;
4743630506bSToomas Soome 			case 3:
4753630506bSToomas Soome 				gfx_mem_wr1(destination, size, off,
4763630506bSToomas Soome 				    (data >> 16) & 0xff);
4773630506bSToomas Soome 				gfx_mem_wr1(destination, size, off + 1,
4783630506bSToomas Soome 				    (data >> 8) & 0xff);
4793630506bSToomas Soome 				gfx_mem_wr1(destination, size, off + 2,
4803630506bSToomas Soome 				    data & 0xff);
4813630506bSToomas Soome 				break;
4823630506bSToomas Soome 			case 4:
4833630506bSToomas Soome 				gfx_mem_wr4(destination, size, off, data);
4843630506bSToomas Soome 				break;
4851caed70cSToomas Soome 			default:
4861caed70cSToomas Soome 				return (EINVAL);
4873630506bSToomas Soome 			}
4883630506bSToomas Soome 			off += bpp;
4893630506bSToomas Soome 		}
4903630506bSToomas Soome 	}
4913630506bSToomas Soome 
4923630506bSToomas Soome 	return (0);
4933630506bSToomas Soome }
4943630506bSToomas Soome 
4953630506bSToomas Soome 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)4963630506bSToomas Soome gfxfb_blt_video_to_buffer(void *BltBuffer, uint32_t SourceX, uint32_t SourceY,
4973630506bSToomas Soome     uint32_t DestinationX, uint32_t DestinationY,
4983630506bSToomas Soome     uint32_t Width, uint32_t Height, uint32_t Delta)
4993630506bSToomas Soome {
5003630506bSToomas Soome #if defined(EFI)
5013630506bSToomas Soome 	EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
5023630506bSToomas Soome #else
5033630506bSToomas Soome 	struct paletteentry *p;
5043630506bSToomas Soome #endif
5053630506bSToomas Soome 	uint32_t x, sy, dy;
5063630506bSToomas Soome 	uint32_t bpp, pitch, copybytes;
5073630506bSToomas Soome 	off_t off;
5081caed70cSToomas Soome 	uint8_t *source, *destination, *sb;
5093630506bSToomas Soome 	uint8_t rm, rp, gm, gp, bm, bp;
5103630506bSToomas Soome 	bool bgra;
5113630506bSToomas Soome 
5123630506bSToomas Soome 	if (BltBuffer == NULL)
5133630506bSToomas Soome 		return (EINVAL);
5143630506bSToomas Soome 
5153630506bSToomas Soome 	if (SourceY + Height >
5163630506bSToomas Soome 	    gfx_state.tg_fb.fb_height)
5173630506bSToomas Soome 		return (EINVAL);
5183630506bSToomas Soome 
5193630506bSToomas Soome 	if (SourceX + Width > gfx_state.tg_fb.fb_width)
5203630506bSToomas Soome 		return (EINVAL);
5213630506bSToomas Soome 
5223630506bSToomas Soome 	if (Width == 0 || Height == 0)
5233630506bSToomas Soome 		return (EINVAL);
5243630506bSToomas Soome 
5253630506bSToomas Soome 	if (Delta == 0)
5263630506bSToomas Soome 		Delta = Width * sizeof (*p);
5273630506bSToomas Soome 
5283630506bSToomas Soome 	bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3;
5293630506bSToomas Soome 	pitch = gfx_state.tg_fb.fb_stride * bpp;
5303630506bSToomas Soome 
5313630506bSToomas Soome 	copybytes = Width * bpp;
5323630506bSToomas Soome 
5333630506bSToomas Soome 	rp = ffs(gfx_state.tg_fb.fb_mask_red) - 1;
5343630506bSToomas Soome 	gp = ffs(gfx_state.tg_fb.fb_mask_green) - 1;
5353630506bSToomas Soome 	bp = ffs(gfx_state.tg_fb.fb_mask_blue) - 1;
5363630506bSToomas Soome 	rm = gfx_state.tg_fb.fb_mask_red >> rp;
5373630506bSToomas Soome 	gm = gfx_state.tg_fb.fb_mask_green >> gp;
5383630506bSToomas Soome 	bm = gfx_state.tg_fb.fb_mask_blue >> bp;
5393630506bSToomas Soome 
5403630506bSToomas Soome 	/* If FB pixel format is BGRA, we can use direct copy. */
5413630506bSToomas Soome 	bgra = bpp == 4 &&
5423630506bSToomas Soome 	    ffs(rm) - 1 == 8 && rp == 16 &&
5433630506bSToomas Soome 	    ffs(gm) - 1 == 8 && gp == 8 &&
5443630506bSToomas Soome 	    ffs(bm) - 1 == 8 && bp == 0;
5453630506bSToomas Soome 
5463630506bSToomas Soome 	for (sy = SourceY, dy = DestinationY; dy < Height + DestinationY;
5473630506bSToomas Soome 	    sy++, dy++) {
5483630506bSToomas Soome 		off = sy * pitch + SourceX * bpp;
5493630506bSToomas Soome 		source = gfx_get_fb_address() + off;
5503630506bSToomas Soome 		destination = (uint8_t *)BltBuffer + dy * Delta +
5513630506bSToomas Soome 		    DestinationX * sizeof (*p);
5523630506bSToomas Soome 
5531caed70cSToomas Soome 		if (bgra) {
5543630506bSToomas Soome 			bcopy(source, destination, copybytes);
5551caed70cSToomas Soome 		} else {
5563630506bSToomas Soome 			for (x = 0; x < Width; x++) {
5573630506bSToomas Soome 				uint32_t c = 0;
5583630506bSToomas Soome 
5591caed70cSToomas Soome 				p = (void *)(destination + x * sizeof (*p));
5601caed70cSToomas Soome 				sb = source + x * bpp;
5613630506bSToomas Soome 				switch (bpp) {
5623630506bSToomas Soome 				case 1:
5633630506bSToomas Soome 					c = *sb;
5643630506bSToomas Soome 					break;
5653630506bSToomas Soome 				case 2:
5663630506bSToomas Soome 					c = *(uint16_t *)sb;
5673630506bSToomas Soome 					break;
5683630506bSToomas Soome 				case 3:
5693630506bSToomas Soome 					c = sb[0] << 16 | sb[1] << 8 | sb[2];
5703630506bSToomas Soome 					break;
5713630506bSToomas Soome 				case 4:
5723630506bSToomas Soome 					c = *(uint32_t *)sb;
5733630506bSToomas Soome 					break;
5741caed70cSToomas Soome 				default:
5751caed70cSToomas Soome 					return (EINVAL);
5763630506bSToomas Soome 				}
5773630506bSToomas Soome 
5783630506bSToomas Soome 				if (bpp == 1) {
5793630506bSToomas Soome 					*(uint32_t *)p = gfx_fb_color_map(
5803630506bSToomas Soome 					    (c < 16) ?
5813630506bSToomas Soome 					    vga_to_cons_colors[c] : c);
5823630506bSToomas Soome 				} else {
5833630506bSToomas Soome 					p->Red = (c >> rp) & rm;
5843630506bSToomas Soome 					p->Green = (c >> gp) & gm;
5853630506bSToomas Soome 					p->Blue = (c >> bp) & bm;
5863630506bSToomas Soome 					p->Reserved = 0;
5873630506bSToomas Soome 				}
5883630506bSToomas Soome 			}
5893630506bSToomas Soome 		}
5903630506bSToomas Soome 	}
5913630506bSToomas Soome 
5923630506bSToomas Soome 	return (0);
5933630506bSToomas Soome }
5943630506bSToomas Soome 
5953630506bSToomas Soome 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)5963630506bSToomas Soome gfxfb_blt_buffer_to_video(void *BltBuffer, uint32_t SourceX, uint32_t SourceY,
5973630506bSToomas Soome     uint32_t DestinationX, uint32_t DestinationY,
5983630506bSToomas Soome     uint32_t Width, uint32_t Height, uint32_t Delta)
5993630506bSToomas Soome {
6003630506bSToomas Soome #if defined(EFI)
6013630506bSToomas Soome 	EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
6023630506bSToomas Soome #else
6033630506bSToomas Soome 	struct paletteentry *p;
6043630506bSToomas Soome #endif
6053630506bSToomas Soome 	uint32_t x, sy, dy;
6063630506bSToomas Soome 	uint32_t bpp, pitch, copybytes;
6073630506bSToomas Soome 	off_t off;
6081caed70cSToomas Soome 	uint8_t *source, *destination;
6093630506bSToomas Soome 	uint8_t rm, rp, gm, gp, bm, bp;
6103630506bSToomas Soome 	bool bgra;
6113630506bSToomas Soome 
6123630506bSToomas Soome 	if (BltBuffer == NULL)
6133630506bSToomas Soome 		return (EINVAL);
6143630506bSToomas Soome 
6153630506bSToomas Soome 	if (DestinationY + Height >
6163630506bSToomas Soome 	    gfx_state.tg_fb.fb_height)
6173630506bSToomas Soome 		return (EINVAL);
6183630506bSToomas Soome 
6193630506bSToomas Soome 	if (DestinationX + Width > gfx_state.tg_fb.fb_width)
6203630506bSToomas Soome 		return (EINVAL);
6213630506bSToomas Soome 
6223630506bSToomas Soome 	if (Width == 0 || Height == 0)
6233630506bSToomas Soome 		return (EINVAL);
6243630506bSToomas Soome 
6253630506bSToomas Soome 	if (Delta == 0)
6263630506bSToomas Soome 		Delta = Width * sizeof (*p);
6273630506bSToomas Soome 
6283630506bSToomas Soome 	bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3;
6293630506bSToomas Soome 	pitch = gfx_state.tg_fb.fb_stride * bpp;
6303630506bSToomas Soome 
6313630506bSToomas Soome 	copybytes = Width * bpp;
6323630506bSToomas Soome 
6333630506bSToomas Soome 	rp = ffs(gfx_state.tg_fb.fb_mask_red) - 1;
6343630506bSToomas Soome 	gp = ffs(gfx_state.tg_fb.fb_mask_green) - 1;
6353630506bSToomas Soome 	bp = ffs(gfx_state.tg_fb.fb_mask_blue) - 1;
6363630506bSToomas Soome 	rm = gfx_state.tg_fb.fb_mask_red >> rp;
6373630506bSToomas Soome 	gm = gfx_state.tg_fb.fb_mask_green >> gp;
6383630506bSToomas Soome 	bm = gfx_state.tg_fb.fb_mask_blue >> bp;
6393630506bSToomas Soome 
6403630506bSToomas Soome 	/* If FB pixel format is BGRA, we can use direct copy. */
6413630506bSToomas Soome 	bgra = bpp == 4 &&
6423630506bSToomas Soome 	    ffs(rm) - 1 == 8 && rp == 16 &&
6433630506bSToomas Soome 	    ffs(gm) - 1 == 8 && gp == 8 &&
6443630506bSToomas Soome 	    ffs(bm) - 1 == 8 && bp == 0;
6453630506bSToomas Soome 
6463630506bSToomas Soome 	for (sy = SourceY, dy = DestinationY; sy < Height + SourceY;
6473630506bSToomas Soome 	    sy++, dy++) {
6483630506bSToomas Soome 		off = dy * pitch + DestinationX * bpp;
6493630506bSToomas Soome 		destination = gfx_get_fb_address() + off;
6503630506bSToomas Soome 
6513630506bSToomas Soome 		if (bgra) {
6523630506bSToomas Soome 			source = (uint8_t *)BltBuffer + sy * Delta +
6533630506bSToomas Soome 			    SourceX * sizeof (*p);
6541caed70cSToomas Soome 			bcopy(source, destination, copybytes);
6553630506bSToomas Soome 		} else {
6563630506bSToomas Soome 			for (x = 0; x < Width; x++) {
6573630506bSToomas Soome 				uint32_t c;
6583630506bSToomas Soome 
6593630506bSToomas Soome 				p = (void *)((uint8_t *)BltBuffer +
6603630506bSToomas Soome 				    sy * Delta +
6613630506bSToomas Soome 				    (SourceX + x) * sizeof (*p));
6623630506bSToomas Soome 				if (bpp == 1) {
6633630506bSToomas Soome 					c = rgb_to_color_index(p->Red,
6643630506bSToomas Soome 					    p->Green, p->Blue);
6653630506bSToomas Soome 				} else {
6663630506bSToomas Soome 					c = (p->Red & rm) << rp |
6673630506bSToomas Soome 					    (p->Green & gm) << gp |
6683630506bSToomas Soome 					    (p->Blue & bm) << bp;
6693630506bSToomas Soome 				}
6703630506bSToomas Soome 				off = x * bpp;
6713630506bSToomas Soome 				switch (bpp) {
6723630506bSToomas Soome 				case 1:
6731caed70cSToomas Soome 					gfx_mem_wr1(destination, copybytes,
6743630506bSToomas Soome 					    off, (c < 16) ?
6753630506bSToomas Soome 					    cons_to_vga_colors[c] : c);
6763630506bSToomas Soome 					break;
6773630506bSToomas Soome 				case 2:
6781caed70cSToomas Soome 					gfx_mem_wr2(destination, copybytes,
6793630506bSToomas Soome 					    off, c);
6803630506bSToomas Soome 					break;
6813630506bSToomas Soome 				case 3:
6821caed70cSToomas Soome 					gfx_mem_wr1(destination, copybytes,
6833630506bSToomas Soome 					    off, (c >> 16) & 0xff);
6841caed70cSToomas Soome 					gfx_mem_wr1(destination, copybytes,
6853630506bSToomas Soome 					    off + 1, (c >> 8) & 0xff);
6861caed70cSToomas Soome 					gfx_mem_wr1(destination, copybytes,
6873630506bSToomas Soome 					    off + 2, c & 0xff);
6883630506bSToomas Soome 					break;
6893630506bSToomas Soome 				case 4:
6901caed70cSToomas Soome 					gfx_mem_wr4(destination, copybytes,
6913630506bSToomas Soome 					    x * bpp, c);
6923630506bSToomas Soome 					break;
6931caed70cSToomas Soome 				default:
6941caed70cSToomas Soome 					return (EINVAL);
6953630506bSToomas Soome 				}
6963630506bSToomas Soome 			}
6971caed70cSToomas Soome 		}
6983630506bSToomas Soome 	}
6993630506bSToomas Soome 
7003630506bSToomas Soome 	return (0);
7013630506bSToomas Soome }
7023630506bSToomas Soome 
7033630506bSToomas Soome 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)7043630506bSToomas Soome gfxfb_blt_video_to_video(uint32_t SourceX, uint32_t SourceY,
7053630506bSToomas Soome     uint32_t DestinationX, uint32_t DestinationY,
7063630506bSToomas Soome     uint32_t Width, uint32_t Height)
7073630506bSToomas Soome {
7083630506bSToomas Soome 	uint32_t bpp, copybytes;
7093630506bSToomas Soome 	int pitch;
7103630506bSToomas Soome 	uint8_t *source, *destination;
7113630506bSToomas Soome 	off_t off;
7123630506bSToomas Soome 
7133630506bSToomas Soome 	if (SourceY + Height >
7143630506bSToomas Soome 	    gfx_state.tg_fb.fb_height)
7153630506bSToomas Soome 		return (EINVAL);
7163630506bSToomas Soome 
7173630506bSToomas Soome 	if (SourceX + Width > gfx_state.tg_fb.fb_width)
7183630506bSToomas Soome 		return (EINVAL);
7193630506bSToomas Soome 
7203630506bSToomas Soome 	if (DestinationY + Height >
7213630506bSToomas Soome 	    gfx_state.tg_fb.fb_height)
7223630506bSToomas Soome 		return (EINVAL);
7233630506bSToomas Soome 
7243630506bSToomas Soome 	if (DestinationX + Width > gfx_state.tg_fb.fb_width)
7253630506bSToomas Soome 		return (EINVAL);
7263630506bSToomas Soome 
7273630506bSToomas Soome 	if (Width == 0 || Height == 0)
7283630506bSToomas Soome 		return (EINVAL);
7293630506bSToomas Soome 
7303630506bSToomas Soome 	bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3;
7313630506bSToomas Soome 	pitch = gfx_state.tg_fb.fb_stride * bpp;
7323630506bSToomas Soome 
7333630506bSToomas Soome 	copybytes = Width * bpp;
7343630506bSToomas Soome 
7353630506bSToomas Soome 	off = SourceY * pitch + SourceX * bpp;
7363630506bSToomas Soome 	source = gfx_get_fb_address() + off;
7373630506bSToomas Soome 	off = DestinationY * pitch + DestinationX * bpp;
7383630506bSToomas Soome 	destination = gfx_get_fb_address() + off;
7393630506bSToomas Soome 
7403630506bSToomas Soome 	if ((uintptr_t)destination > (uintptr_t)source) {
7413630506bSToomas Soome 		source += Height * pitch;
7423630506bSToomas Soome 		destination += Height * pitch;
7433630506bSToomas Soome 		pitch = -pitch;
7443630506bSToomas Soome 	}
7453630506bSToomas Soome 
7463630506bSToomas Soome 	while (Height-- > 0) {
7473630506bSToomas Soome 		bcopy(source, destination, copybytes);
7483630506bSToomas Soome 		source += pitch;
7493630506bSToomas Soome 		destination += pitch;
7503630506bSToomas Soome 	}
7513630506bSToomas Soome 
7523630506bSToomas Soome 	return (0);
7533630506bSToomas Soome }
7543630506bSToomas Soome 
7556102f43cSToomas Soome static void
gfxfb_shadow_fill(uint32_t * BltBuffer,uint32_t DestinationX,uint32_t DestinationY,uint32_t Width,uint32_t Height)7566102f43cSToomas Soome gfxfb_shadow_fill(uint32_t *BltBuffer,
7576102f43cSToomas Soome     uint32_t DestinationX, uint32_t DestinationY,
7586102f43cSToomas Soome     uint32_t Width, uint32_t Height)
7596102f43cSToomas Soome {
7606102f43cSToomas Soome 	uint32_t fbX, fbY;
7616102f43cSToomas Soome 
7626102f43cSToomas Soome 	if (gfx_state.tg_shadow_fb == NULL)
7636102f43cSToomas Soome 		return;
7646102f43cSToomas Soome 
7656102f43cSToomas Soome 	fbX = gfx_state.tg_fb.fb_width;
7666102f43cSToomas Soome 	fbY = gfx_state.tg_fb.fb_height;
7676102f43cSToomas Soome 
7686102f43cSToomas Soome 	if (BltBuffer == NULL)
7696102f43cSToomas Soome 		return;
7706102f43cSToomas Soome 
7716102f43cSToomas Soome 	if (DestinationX + Width > fbX)
7726102f43cSToomas Soome 		Width = fbX - DestinationX;
7736102f43cSToomas Soome 
7746102f43cSToomas Soome 	if (DestinationY + Height > fbY)
7756102f43cSToomas Soome 		Height = fbY - DestinationY;
7766102f43cSToomas Soome 
7776102f43cSToomas Soome 	uint32_t y2 = Height + DestinationY;
7786102f43cSToomas Soome 	for (uint32_t y1 = DestinationY; y1 < y2; y1++) {
7796102f43cSToomas Soome 		uint32_t off = y1 * fbX + DestinationX;
7806102f43cSToomas Soome 
7816102f43cSToomas Soome 		for (uint32_t x = 0; x < Width; x++) {
7826102f43cSToomas Soome 			gfx_state.tg_shadow_fb[off + x] = *BltBuffer;
7836102f43cSToomas Soome 		}
7846102f43cSToomas Soome 	}
7856102f43cSToomas Soome }
7866102f43cSToomas Soome 
7873630506bSToomas Soome 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)7883630506bSToomas Soome gfxfb_blt(void *BltBuffer, GFXFB_BLT_OPERATION BltOperation,
7893630506bSToomas Soome     uint32_t SourceX, uint32_t SourceY,
7903630506bSToomas Soome     uint32_t DestinationX, uint32_t DestinationY,
7913630506bSToomas Soome     uint32_t Width, uint32_t Height, uint32_t Delta)
7923630506bSToomas Soome {
7933630506bSToomas Soome 	int rv;
7943630506bSToomas Soome #if defined(EFI)
7953630506bSToomas Soome 	EFI_STATUS status;
7963630506bSToomas Soome 	EFI_GRAPHICS_OUTPUT *gop = gfx_state.tg_private;
7974c7a3a70SToomas Soome 	EFI_TPL tpl;
7983630506bSToomas Soome 
7993ddf7eadSToomas Soome 	/*
800305ef653SWarner Losh 	 * We assume Blt() does work, if not, we will need to build exception
801305ef653SWarner Losh 	 * list case by case. We only have boot services during part of our
802305ef653SWarner Losh 	 * exectution. Once terminate boot services, these operations cannot be
803305ef653SWarner Losh 	 * done as they are provided by protocols that disappear when exit
804305ef653SWarner Losh 	 * boot services.
8053ddf7eadSToomas Soome 	 */
806*21b5b8b3SToomas Soome 	if (!ignore_gop_blt && gop != NULL && boot_services_active) {
8074c7a3a70SToomas Soome 		tpl = BS->RaiseTPL(TPL_NOTIFY);
8083630506bSToomas Soome 		switch (BltOperation) {
8093630506bSToomas Soome 		case GfxFbBltVideoFill:
8106102f43cSToomas Soome 			gfxfb_shadow_fill(BltBuffer, DestinationX,
8116102f43cSToomas Soome 			    DestinationY, Width, Height);
8123630506bSToomas Soome 			status = gop->Blt(gop, BltBuffer, EfiBltVideoFill,
8133630506bSToomas Soome 			    SourceX, SourceY, DestinationX, DestinationY,
8143630506bSToomas Soome 			    Width, Height, Delta);
8153630506bSToomas Soome 			break;
8163630506bSToomas Soome 
8173630506bSToomas Soome 		case GfxFbBltVideoToBltBuffer:
8183630506bSToomas Soome 			status = gop->Blt(gop, BltBuffer,
8193630506bSToomas Soome 			    EfiBltVideoToBltBuffer,
8203630506bSToomas Soome 			    SourceX, SourceY, DestinationX, DestinationY,
8213630506bSToomas Soome 			    Width, Height, Delta);
8223630506bSToomas Soome 			break;
8233630506bSToomas Soome 
8243630506bSToomas Soome 		case GfxFbBltBufferToVideo:
8253630506bSToomas Soome 			status = gop->Blt(gop, BltBuffer, EfiBltBufferToVideo,
8263630506bSToomas Soome 			    SourceX, SourceY, DestinationX, DestinationY,
8273630506bSToomas Soome 			    Width, Height, Delta);
8283630506bSToomas Soome 			break;
8293630506bSToomas Soome 
8303630506bSToomas Soome 		case GfxFbBltVideoToVideo:
8313630506bSToomas Soome 			status = gop->Blt(gop, BltBuffer, EfiBltVideoToVideo,
8323630506bSToomas Soome 			    SourceX, SourceY, DestinationX, DestinationY,
8333630506bSToomas Soome 			    Width, Height, Delta);
8343630506bSToomas Soome 			break;
8353630506bSToomas Soome 
8363630506bSToomas Soome 		default:
8373630506bSToomas Soome 			status = EFI_INVALID_PARAMETER;
8383630506bSToomas Soome 			break;
8393630506bSToomas Soome 		}
8403630506bSToomas Soome 
8413630506bSToomas Soome 		switch (status) {
8423630506bSToomas Soome 		case EFI_SUCCESS:
8433630506bSToomas Soome 			rv = 0;
8443630506bSToomas Soome 			break;
8453630506bSToomas Soome 
8463630506bSToomas Soome 		case EFI_INVALID_PARAMETER:
8473630506bSToomas Soome 			rv = EINVAL;
8483630506bSToomas Soome 			break;
8493630506bSToomas Soome 
8503630506bSToomas Soome 		case EFI_DEVICE_ERROR:
8513630506bSToomas Soome 		default:
8523630506bSToomas Soome 			rv = EIO;
8533630506bSToomas Soome 			break;
8543630506bSToomas Soome 		}
8553630506bSToomas Soome 
8564c7a3a70SToomas Soome 		BS->RestoreTPL(tpl);
8573630506bSToomas Soome 		return (rv);
8583630506bSToomas Soome 	}
8593630506bSToomas Soome #endif
8603630506bSToomas Soome 
8613630506bSToomas Soome 	switch (BltOperation) {
8623630506bSToomas Soome 	case GfxFbBltVideoFill:
8636102f43cSToomas Soome 		gfxfb_shadow_fill(BltBuffer, DestinationX, DestinationY,
8646102f43cSToomas Soome 		    Width, Height);
8653630506bSToomas Soome 		rv = gfxfb_blt_fill(BltBuffer, DestinationX, DestinationY,
8663630506bSToomas Soome 		    Width, Height);
8673630506bSToomas Soome 		break;
8683630506bSToomas Soome 
8693630506bSToomas Soome 	case GfxFbBltVideoToBltBuffer:
8703630506bSToomas Soome 		rv = gfxfb_blt_video_to_buffer(BltBuffer, SourceX, SourceY,
8713630506bSToomas Soome 		    DestinationX, DestinationY, Width, Height, Delta);
8723630506bSToomas Soome 		break;
8733630506bSToomas Soome 
8743630506bSToomas Soome 	case GfxFbBltBufferToVideo:
8753630506bSToomas Soome 		rv = gfxfb_blt_buffer_to_video(BltBuffer, SourceX, SourceY,
8763630506bSToomas Soome 		    DestinationX, DestinationY, Width, Height, Delta);
8773630506bSToomas Soome 		break;
8783630506bSToomas Soome 
8793630506bSToomas Soome 	case GfxFbBltVideoToVideo:
8803630506bSToomas Soome 		rv = gfxfb_blt_video_to_video(SourceX, SourceY,
8813630506bSToomas Soome 		    DestinationX, DestinationY, Width, Height);
8823630506bSToomas Soome 		break;
8833630506bSToomas Soome 
8843630506bSToomas Soome 	default:
8853630506bSToomas Soome 		rv = EINVAL;
8863630506bSToomas Soome 		break;
8873630506bSToomas Soome 	}
8883630506bSToomas Soome 	return (rv);
8893630506bSToomas Soome }
8903630506bSToomas Soome 
8913630506bSToomas Soome void
gfx_bitblt_bitmap(teken_gfx_t * state,const uint8_t * glyph,const teken_attr_t * a,uint32_t alpha,bool cursor)8923630506bSToomas Soome gfx_bitblt_bitmap(teken_gfx_t *state, const uint8_t *glyph,
8933630506bSToomas Soome     const teken_attr_t *a, uint32_t alpha, bool cursor)
8943630506bSToomas Soome {
8953630506bSToomas Soome 	uint32_t width, height;
8963630506bSToomas Soome 	uint32_t fgc, bgc, bpl, cc, o;
8973630506bSToomas Soome 	int bpp, bit, byte;
8983630506bSToomas Soome 	bool invert = false;
8993630506bSToomas Soome 
9003630506bSToomas Soome 	bpp = 4;		/* We only generate BGRA */
9013630506bSToomas Soome 	width = state->tg_font.vf_width;
9023630506bSToomas Soome 	height = state->tg_font.vf_height;
9033630506bSToomas Soome 	bpl = (width + 7) / 8;  /* Bytes per source line. */
9043630506bSToomas Soome 
9053630506bSToomas Soome 	fgc = a->ta_fgcolor;
9063630506bSToomas Soome 	bgc = a->ta_bgcolor;
9073630506bSToomas Soome 	if (a->ta_format & TF_BOLD)
9083630506bSToomas Soome 		fgc |= TC_LIGHT;
9093630506bSToomas Soome 	if (a->ta_format & TF_BLINK)
9103630506bSToomas Soome 		bgc |= TC_LIGHT;
9113630506bSToomas Soome 
9123630506bSToomas Soome 	fgc = gfx_fb_color_map(fgc);
9133630506bSToomas Soome 	bgc = gfx_fb_color_map(bgc);
9143630506bSToomas Soome 
9153630506bSToomas Soome 	if (a->ta_format & TF_REVERSE)
9163630506bSToomas Soome 		invert = !invert;
9173630506bSToomas Soome 	if (cursor)
9183630506bSToomas Soome 		invert = !invert;
9193630506bSToomas Soome 	if (invert) {
9203630506bSToomas Soome 		uint32_t tmp;
9213630506bSToomas Soome 
9223630506bSToomas Soome 		tmp = fgc;
9233630506bSToomas Soome 		fgc = bgc;
9243630506bSToomas Soome 		bgc = tmp;
9253630506bSToomas Soome 	}
9263630506bSToomas Soome 
9273630506bSToomas Soome 	alpha = alpha << 24;
9283630506bSToomas Soome 	fgc |= alpha;
9293630506bSToomas Soome 	bgc |= alpha;
9303630506bSToomas Soome 
9313630506bSToomas Soome 	for (uint32_t y = 0; y < height; y++) {
9323630506bSToomas Soome 		for (uint32_t x = 0; x < width; x++) {
9333630506bSToomas Soome 			byte = y * bpl + x / 8;
9343630506bSToomas Soome 			bit = 0x80 >> (x % 8);
9353630506bSToomas Soome 			o = y * width * bpp + x * bpp;
9363630506bSToomas Soome 			cc = glyph[byte] & bit ? fgc : bgc;
9373630506bSToomas Soome 
9383630506bSToomas Soome 			gfx_mem_wr4(state->tg_glyph,
9393630506bSToomas Soome 			    state->tg_glyph_size, o, cc);
9403630506bSToomas Soome 		}
9413630506bSToomas Soome 	}
9423630506bSToomas Soome }
9433630506bSToomas Soome 
9443630506bSToomas Soome /*
9453630506bSToomas Soome  * Draw prepared glyph on terminal point p.
9463630506bSToomas Soome  */
9473630506bSToomas Soome static void
gfx_fb_printchar(teken_gfx_t * state,const teken_pos_t * p)9483630506bSToomas Soome gfx_fb_printchar(teken_gfx_t *state, const teken_pos_t *p)
9493630506bSToomas Soome {
9503630506bSToomas Soome 	unsigned x, y, width, height;
9513630506bSToomas Soome 
9523630506bSToomas Soome 	width = state->tg_font.vf_width;
9533630506bSToomas Soome 	height = state->tg_font.vf_height;
9543630506bSToomas Soome 	x = state->tg_origin.tp_col + p->tp_col * width;
9553630506bSToomas Soome 	y = state->tg_origin.tp_row + p->tp_row * height;
9563630506bSToomas Soome 
9573630506bSToomas Soome 	gfx_fb_cons_display(x, y, width, height, state->tg_glyph);
9583630506bSToomas Soome }
9593630506bSToomas Soome 
9603630506bSToomas Soome /*
9613630506bSToomas Soome  * Store char with its attribute to buffer and put it on screen.
9623630506bSToomas Soome  */
9633630506bSToomas Soome void
gfx_fb_putchar(void * arg,const teken_pos_t * p,teken_char_t c,const teken_attr_t * a)9643630506bSToomas Soome gfx_fb_putchar(void *arg, const teken_pos_t *p, teken_char_t c,
9653630506bSToomas Soome     const teken_attr_t *a)
9663630506bSToomas Soome {
9673630506bSToomas Soome 	teken_gfx_t *state = arg;
9683630506bSToomas Soome 	const uint8_t *glyph;
9693630506bSToomas Soome 	int idx;
9703630506bSToomas Soome 
9713630506bSToomas Soome 	idx = p->tp_col + p->tp_row * state->tg_tp.tp_col;
9723630506bSToomas Soome 	if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row)
9733630506bSToomas Soome 		return;
9743630506bSToomas Soome 
9753630506bSToomas Soome 	/* remove the cursor */
9763630506bSToomas Soome 	if (state->tg_cursor_visible)
9773630506bSToomas Soome 		gfx_fb_cursor_draw(state, &state->tg_cursor, false);
9783630506bSToomas Soome 
9793630506bSToomas Soome 	screen_buffer[idx].c = c;
9803630506bSToomas Soome 	screen_buffer[idx].a = *a;
9813630506bSToomas Soome 
9823630506bSToomas Soome 	glyph = font_lookup(&state->tg_font, c, a);
9833630506bSToomas Soome 	gfx_bitblt_bitmap(state, glyph, a, 0xff, false);
9843630506bSToomas Soome 	gfx_fb_printchar(state, p);
9853630506bSToomas Soome 
9863630506bSToomas Soome 	/* display the cursor */
9873630506bSToomas Soome 	if (state->tg_cursor_visible) {
9883630506bSToomas Soome 		const teken_pos_t *c;
9893630506bSToomas Soome 
9903630506bSToomas Soome 		c = teken_get_cursor(&state->tg_teken);
9913630506bSToomas Soome 		gfx_fb_cursor_draw(state, c, true);
9923630506bSToomas Soome 	}
9933630506bSToomas Soome }
9943630506bSToomas Soome 
9953630506bSToomas Soome void
gfx_fb_fill(void * arg,const teken_rect_t * r,teken_char_t c,const teken_attr_t * a)9963630506bSToomas Soome gfx_fb_fill(void *arg, const teken_rect_t *r, teken_char_t c,
9973630506bSToomas Soome     const teken_attr_t *a)
9983630506bSToomas Soome {
9993630506bSToomas Soome 	teken_gfx_t *state = arg;
10003630506bSToomas Soome 	const uint8_t *glyph;
10013630506bSToomas Soome 	teken_pos_t p;
10023630506bSToomas Soome 	struct text_pixel *row;
10033630506bSToomas Soome 
10043630506bSToomas Soome 	/* remove the cursor */
10053630506bSToomas Soome 	if (state->tg_cursor_visible)
10063630506bSToomas Soome 		gfx_fb_cursor_draw(state, &state->tg_cursor, false);
10073630506bSToomas Soome 
10083630506bSToomas Soome 	glyph = font_lookup(&state->tg_font, c, a);
10093630506bSToomas Soome 	gfx_bitblt_bitmap(state, glyph, a, 0xff, false);
10103630506bSToomas Soome 
10113630506bSToomas Soome 	for (p.tp_row = r->tr_begin.tp_row; p.tp_row < r->tr_end.tp_row;
10123630506bSToomas Soome 	    p.tp_row++) {
10133630506bSToomas Soome 		row = &screen_buffer[p.tp_row * state->tg_tp.tp_col];
10143630506bSToomas Soome 		for (p.tp_col = r->tr_begin.tp_col;
10153630506bSToomas Soome 		    p.tp_col < r->tr_end.tp_col; p.tp_col++) {
10163630506bSToomas Soome 			row[p.tp_col].c = c;
10173630506bSToomas Soome 			row[p.tp_col].a = *a;
10183630506bSToomas Soome 			gfx_fb_printchar(state, &p);
10193630506bSToomas Soome 		}
10203630506bSToomas Soome 	}
10213630506bSToomas Soome 
10223630506bSToomas Soome 	/* display the cursor */
10233630506bSToomas Soome 	if (state->tg_cursor_visible) {
10243630506bSToomas Soome 		const teken_pos_t *c;
10253630506bSToomas Soome 
10263630506bSToomas Soome 		c = teken_get_cursor(&state->tg_teken);
10273630506bSToomas Soome 		gfx_fb_cursor_draw(state, c, true);
10283630506bSToomas Soome 	}
10293630506bSToomas Soome }
10303630506bSToomas Soome 
10313630506bSToomas Soome static void
gfx_fb_cursor_draw(teken_gfx_t * state,const teken_pos_t * pos,bool on)1032e5a50b03SToomas Soome gfx_fb_cursor_draw(teken_gfx_t *state, const teken_pos_t *pos, bool on)
10333630506bSToomas Soome {
10343630506bSToomas Soome 	const uint8_t *glyph;
1035e5a50b03SToomas Soome 	teken_pos_t p;
10363630506bSToomas Soome 	int idx;
10373630506bSToomas Soome 
1038e5a50b03SToomas Soome 	p = *pos;
1039e5a50b03SToomas Soome 	if (p.tp_col >= state->tg_tp.tp_col)
1040e5a50b03SToomas Soome 		p.tp_col = state->tg_tp.tp_col - 1;
1041e5a50b03SToomas Soome 	if (p.tp_row >= state->tg_tp.tp_row)
1042e5a50b03SToomas Soome 		p.tp_row = state->tg_tp.tp_row - 1;
1043e5a50b03SToomas Soome 	idx = p.tp_col + p.tp_row * state->tg_tp.tp_col;
10443630506bSToomas Soome 	if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row)
10453630506bSToomas Soome 		return;
10463630506bSToomas Soome 
10473630506bSToomas Soome 	glyph = font_lookup(&state->tg_font, screen_buffer[idx].c,
10483630506bSToomas Soome 	    &screen_buffer[idx].a);
10493630506bSToomas Soome 	gfx_bitblt_bitmap(state, glyph, &screen_buffer[idx].a, 0xff, on);
1050e5a50b03SToomas Soome 	gfx_fb_printchar(state, &p);
1051d708f23eSToomas Soome 
1052e5a50b03SToomas Soome 	state->tg_cursor = p;
10533630506bSToomas Soome }
10543630506bSToomas Soome 
10553630506bSToomas Soome void
gfx_fb_cursor(void * arg,const teken_pos_t * p)10563630506bSToomas Soome gfx_fb_cursor(void *arg, const teken_pos_t *p)
10573630506bSToomas Soome {
10583630506bSToomas Soome 	teken_gfx_t *state = arg;
10593630506bSToomas Soome 
10603630506bSToomas Soome 	/* Switch cursor off in old location and back on in new. */
10613630506bSToomas Soome 	if (state->tg_cursor_visible) {
10623630506bSToomas Soome 		gfx_fb_cursor_draw(state, &state->tg_cursor, false);
10633630506bSToomas Soome 		gfx_fb_cursor_draw(state, p, true);
10643630506bSToomas Soome 	}
10653630506bSToomas Soome }
10663630506bSToomas Soome 
10673630506bSToomas Soome void
gfx_fb_param(void * arg,int cmd,unsigned int value)10683630506bSToomas Soome gfx_fb_param(void *arg, int cmd, unsigned int value)
10693630506bSToomas Soome {
10703630506bSToomas Soome 	teken_gfx_t *state = arg;
10713630506bSToomas Soome 	const teken_pos_t *c;
10723630506bSToomas Soome 
10733630506bSToomas Soome 	switch (cmd) {
10743630506bSToomas Soome 	case TP_SETLOCALCURSOR:
10753630506bSToomas Soome 		/*
10763630506bSToomas Soome 		 * 0 means normal (usually block), 1 means hidden, and
10773630506bSToomas Soome 		 * 2 means blinking (always block) for compatibility with
10783630506bSToomas Soome 		 * syscons.  We don't support any changes except hiding,
10793630506bSToomas Soome 		 * so must map 2 to 0.
10803630506bSToomas Soome 		 */
10813630506bSToomas Soome 		value = (value == 1) ? 0 : 1;
10823630506bSToomas Soome 		/* FALLTHROUGH */
10833630506bSToomas Soome 	case TP_SHOWCURSOR:
10843630506bSToomas Soome 		c = teken_get_cursor(&state->tg_teken);
10853630506bSToomas Soome 		gfx_fb_cursor_draw(state, c, true);
10863630506bSToomas Soome 		if (value != 0)
10873630506bSToomas Soome 			state->tg_cursor_visible = true;
10883630506bSToomas Soome 		else
10893630506bSToomas Soome 			state->tg_cursor_visible = false;
10903630506bSToomas Soome 		break;
10913630506bSToomas Soome 	default:
10923630506bSToomas Soome 		/* Not yet implemented */
10933630506bSToomas Soome 		break;
10943630506bSToomas Soome 	}
10953630506bSToomas Soome }
10963630506bSToomas Soome 
10973630506bSToomas Soome bool
is_same_pixel(struct text_pixel * px1,struct text_pixel * px2)10983630506bSToomas Soome is_same_pixel(struct text_pixel *px1, struct text_pixel *px2)
10993630506bSToomas Soome {
11003630506bSToomas Soome 	if (px1->c != px2->c)
11013630506bSToomas Soome 		return (false);
11023630506bSToomas Soome 
11033630506bSToomas Soome 	/* Is there image stored? */
11043630506bSToomas Soome 	if ((px1->a.ta_format & TF_IMAGE) ||
11053630506bSToomas Soome 	    (px2->a.ta_format & TF_IMAGE))
11063630506bSToomas Soome 		return (false);
11073630506bSToomas Soome 
11083630506bSToomas Soome 	if (px1->a.ta_format != px2->a.ta_format)
11093630506bSToomas Soome 		return (false);
11103630506bSToomas Soome 	if (px1->a.ta_fgcolor != px2->a.ta_fgcolor)
11113630506bSToomas Soome 		return (false);
11123630506bSToomas Soome 	if (px1->a.ta_bgcolor != px2->a.ta_bgcolor)
11133630506bSToomas Soome 		return (false);
11143630506bSToomas Soome 
11153630506bSToomas Soome 	return (true);
11163630506bSToomas Soome }
11173630506bSToomas Soome 
11183630506bSToomas Soome static void
gfx_fb_copy_area(teken_gfx_t * state,const teken_rect_t * s,const teken_pos_t * d)11193630506bSToomas Soome gfx_fb_copy_area(teken_gfx_t *state, const teken_rect_t *s,
11203630506bSToomas Soome     const teken_pos_t *d)
11213630506bSToomas Soome {
11223630506bSToomas Soome 	uint32_t sx, sy, dx, dy, width, height;
11236102f43cSToomas Soome 	uint32_t pitch, bytes;
11246102f43cSToomas Soome 	int step;
11253630506bSToomas Soome 
11263630506bSToomas Soome 	width = state->tg_font.vf_width;
11273630506bSToomas Soome 	height = state->tg_font.vf_height;
11283630506bSToomas Soome 
11296102f43cSToomas Soome 	sx = s->tr_begin.tp_col * width;
11306102f43cSToomas Soome 	sy = s->tr_begin.tp_row * height;
11316102f43cSToomas Soome 	dx = d->tp_col * width;
11326102f43cSToomas Soome 	dy = d->tp_row * height;
11333630506bSToomas Soome 
11343630506bSToomas Soome 	width *= (s->tr_end.tp_col - s->tr_begin.tp_col + 1);
11353630506bSToomas Soome 
11366102f43cSToomas Soome 	/*
11376102f43cSToomas Soome 	 * With no shadow fb, use video to video copy.
11386102f43cSToomas Soome 	 */
11396102f43cSToomas Soome 	if (state->tg_shadow_fb == NULL) {
11406102f43cSToomas Soome 		(void) gfxfb_blt(NULL, GfxFbBltVideoToVideo,
11416102f43cSToomas Soome 		    sx + state->tg_origin.tp_col,
11426102f43cSToomas Soome 		    sy + state->tg_origin.tp_row,
11436102f43cSToomas Soome 		    dx + state->tg_origin.tp_col,
11446102f43cSToomas Soome 		    dy + state->tg_origin.tp_row,
11453630506bSToomas Soome 		    width, height, 0);
11466102f43cSToomas Soome 		return;
11476102f43cSToomas Soome 	}
11486102f43cSToomas Soome 
11496102f43cSToomas Soome 	/*
11506102f43cSToomas Soome 	 * With shadow fb, we need to copy data on both shadow and video,
11516102f43cSToomas Soome 	 * to preserve the consistency. We only read data from shadow fb.
11526102f43cSToomas Soome 	 */
11536102f43cSToomas Soome 
11546102f43cSToomas Soome 	step = 1;
11556102f43cSToomas Soome 	pitch = state->tg_fb.fb_width;
11566102f43cSToomas Soome 	bytes = width * sizeof (*state->tg_shadow_fb);
11576102f43cSToomas Soome 
11586102f43cSToomas Soome 	/*
11596102f43cSToomas Soome 	 * To handle overlapping areas, set up reverse copy here.
11606102f43cSToomas Soome 	 */
11616102f43cSToomas Soome 	if (dy * pitch + dx > sy * pitch + sx) {
11626102f43cSToomas Soome 		sy += height;
11636102f43cSToomas Soome 		dy += height;
11646102f43cSToomas Soome 		step = -step;
11656102f43cSToomas Soome 	}
11666102f43cSToomas Soome 
11676102f43cSToomas Soome 	while (height-- > 0) {
11686102f43cSToomas Soome 		uint32_t *source = &state->tg_shadow_fb[sy * pitch + sx];
11696102f43cSToomas Soome 		uint32_t *destination = &state->tg_shadow_fb[dy * pitch + dx];
11706102f43cSToomas Soome 
11716102f43cSToomas Soome 		bcopy(source, destination, bytes);
11726102f43cSToomas Soome 		(void) gfxfb_blt(destination, GfxFbBltBufferToVideo,
11736102f43cSToomas Soome 		    0, 0, dx + state->tg_origin.tp_col,
11746102f43cSToomas Soome 		    dy + state->tg_origin.tp_row, width, 1, 0);
11756102f43cSToomas Soome 
11766102f43cSToomas Soome 		sy += step;
11776102f43cSToomas Soome 		dy += step;
11786102f43cSToomas Soome 	}
11793630506bSToomas Soome }
11803630506bSToomas Soome 
11813630506bSToomas Soome static void
gfx_fb_copy_line(teken_gfx_t * state,int ncol,teken_pos_t * s,teken_pos_t * d)11823630506bSToomas Soome gfx_fb_copy_line(teken_gfx_t *state, int ncol, teken_pos_t *s, teken_pos_t *d)
11833630506bSToomas Soome {
11843630506bSToomas Soome 	teken_rect_t sr;
11853630506bSToomas Soome 	teken_pos_t dp;
11863630506bSToomas Soome 	unsigned soffset, doffset;
11873630506bSToomas Soome 	bool mark = false;
11883630506bSToomas Soome 	int x;
11893630506bSToomas Soome 
11903630506bSToomas Soome 	soffset = s->tp_col + s->tp_row * state->tg_tp.tp_col;
11913630506bSToomas Soome 	doffset = d->tp_col + d->tp_row * state->tg_tp.tp_col;
11923630506bSToomas Soome 
11933630506bSToomas Soome 	for (x = 0; x < ncol; x++) {
11943630506bSToomas Soome 		if (is_same_pixel(&screen_buffer[soffset + x],
11953630506bSToomas Soome 		    &screen_buffer[doffset + x])) {
11963630506bSToomas Soome 			if (mark) {
11973630506bSToomas Soome 				gfx_fb_copy_area(state, &sr, &dp);
11983630506bSToomas Soome 				mark = false;
11993630506bSToomas Soome 			}
12003630506bSToomas Soome 		} else {
12013630506bSToomas Soome 			screen_buffer[doffset + x] = screen_buffer[soffset + x];
12023630506bSToomas Soome 			if (mark) {
12033630506bSToomas Soome 				/* update end point */
1204798ea06fSElliott Mitchell 				sr.tr_end.tp_col = s->tp_col + x;
12053630506bSToomas Soome 			} else {
12063630506bSToomas Soome 				/* set up new rectangle */
12073630506bSToomas Soome 				mark = true;
12083630506bSToomas Soome 				sr.tr_begin.tp_col = s->tp_col + x;
12093630506bSToomas Soome 				sr.tr_begin.tp_row = s->tp_row;
12103630506bSToomas Soome 				sr.tr_end.tp_col = s->tp_col + x;
12113630506bSToomas Soome 				sr.tr_end.tp_row = s->tp_row;
12123630506bSToomas Soome 				dp.tp_col = d->tp_col + x;
12133630506bSToomas Soome 				dp.tp_row = d->tp_row;
12143630506bSToomas Soome 			}
12153630506bSToomas Soome 		}
12163630506bSToomas Soome 	}
12173630506bSToomas Soome 	if (mark) {
12183630506bSToomas Soome 		gfx_fb_copy_area(state, &sr, &dp);
12193630506bSToomas Soome 	}
12203630506bSToomas Soome }
12213630506bSToomas Soome 
12223630506bSToomas Soome void
gfx_fb_copy(void * arg,const teken_rect_t * r,const teken_pos_t * p)12233630506bSToomas Soome gfx_fb_copy(void *arg, const teken_rect_t *r, const teken_pos_t *p)
12243630506bSToomas Soome {
12253630506bSToomas Soome 	teken_gfx_t *state = arg;
12263630506bSToomas Soome 	unsigned doffset, soffset;
12273630506bSToomas Soome 	teken_pos_t d, s;
12283630506bSToomas Soome 	int nrow, ncol, y; /* Has to be signed - >= 0 comparison */
12293630506bSToomas Soome 
12303630506bSToomas Soome 	/*
12313630506bSToomas Soome 	 * Copying is a little tricky. We must make sure we do it in
12323630506bSToomas Soome 	 * correct order, to make sure we don't overwrite our own data.
12333630506bSToomas Soome 	 */
12343630506bSToomas Soome 
12353630506bSToomas Soome 	nrow = r->tr_end.tp_row - r->tr_begin.tp_row;
12363630506bSToomas Soome 	ncol = r->tr_end.tp_col - r->tr_begin.tp_col;
12373630506bSToomas Soome 
12383630506bSToomas Soome 	if (p->tp_row + nrow > state->tg_tp.tp_row ||
12393630506bSToomas Soome 	    p->tp_col + ncol > state->tg_tp.tp_col)
12403630506bSToomas Soome 		return;
12413630506bSToomas Soome 
12423630506bSToomas Soome 	soffset = r->tr_begin.tp_col + r->tr_begin.tp_row * state->tg_tp.tp_col;
12433630506bSToomas Soome 	doffset = p->tp_col + p->tp_row * state->tg_tp.tp_col;
12443630506bSToomas Soome 
12453630506bSToomas Soome 	/* remove the cursor */
12463630506bSToomas Soome 	if (state->tg_cursor_visible)
12473630506bSToomas Soome 		gfx_fb_cursor_draw(state, &state->tg_cursor, false);
12483630506bSToomas Soome 
12493630506bSToomas Soome 	/*
12503630506bSToomas Soome 	 * Copy line by line.
12513630506bSToomas Soome 	 */
12523630506bSToomas Soome 	if (doffset <= soffset) {
12533630506bSToomas Soome 		s = r->tr_begin;
12543630506bSToomas Soome 		d = *p;
12553630506bSToomas Soome 		for (y = 0; y < nrow; y++) {
12563630506bSToomas Soome 			s.tp_row = r->tr_begin.tp_row + y;
12573630506bSToomas Soome 			d.tp_row = p->tp_row + y;
12583630506bSToomas Soome 
12593630506bSToomas Soome 			gfx_fb_copy_line(state, ncol, &s, &d);
12603630506bSToomas Soome 		}
12613630506bSToomas Soome 	} else {
12623630506bSToomas Soome 		for (y = nrow - 1; y >= 0; y--) {
12633630506bSToomas Soome 			s.tp_row = r->tr_begin.tp_row + y;
12643630506bSToomas Soome 			d.tp_row = p->tp_row + y;
12653630506bSToomas Soome 
12663630506bSToomas Soome 			gfx_fb_copy_line(state, ncol, &s, &d);
12673630506bSToomas Soome 		}
12683630506bSToomas Soome 	}
12693630506bSToomas Soome 
12703630506bSToomas Soome 	/* display the cursor */
12713630506bSToomas Soome 	if (state->tg_cursor_visible) {
12723630506bSToomas Soome 		const teken_pos_t *c;
12733630506bSToomas Soome 
12743630506bSToomas Soome 		c = teken_get_cursor(&state->tg_teken);
12753630506bSToomas Soome 		gfx_fb_cursor_draw(state, c, true);
12763630506bSToomas Soome 	}
12773630506bSToomas Soome }
12783630506bSToomas Soome 
12793630506bSToomas Soome /*
12803630506bSToomas Soome  * Implements alpha blending for RGBA data, could use pixels for arguments,
12813630506bSToomas Soome  * but byte stream seems more generic.
12823630506bSToomas Soome  * The generic alpha blending is:
12833630506bSToomas Soome  * blend = alpha * fg + (1.0 - alpha) * bg.
12843630506bSToomas Soome  * Since our alpha is not from range [0..1], we scale appropriately.
12853630506bSToomas Soome  */
12863630506bSToomas Soome static uint8_t
alpha_blend(uint8_t fg,uint8_t bg,uint8_t alpha)12873630506bSToomas Soome alpha_blend(uint8_t fg, uint8_t bg, uint8_t alpha)
12883630506bSToomas Soome {
12893630506bSToomas Soome 	uint16_t blend, h, l;
12903630506bSToomas Soome 
12913630506bSToomas Soome 	/* trivial corner cases */
12923630506bSToomas Soome 	if (alpha == 0)
12933630506bSToomas Soome 		return (bg);
12943630506bSToomas Soome 	if (alpha == 0xFF)
12953630506bSToomas Soome 		return (fg);
12963630506bSToomas Soome 	blend = (alpha * fg + (0xFF - alpha) * bg);
12973630506bSToomas Soome 	/* Division by 0xFF */
12983630506bSToomas Soome 	h = blend >> 8;
12993630506bSToomas Soome 	l = blend & 0xFF;
13003630506bSToomas Soome 	if (h + l >= 0xFF)
13013630506bSToomas Soome 		h++;
13023630506bSToomas Soome 	return (h);
13033630506bSToomas Soome }
13043630506bSToomas Soome 
13053630506bSToomas Soome /*
13063630506bSToomas Soome  * Implements alpha blending for RGBA data, could use pixels for arguments,
13073630506bSToomas Soome  * but byte stream seems more generic.
13083630506bSToomas Soome  * The generic alpha blending is:
13093630506bSToomas Soome  * blend = alpha * fg + (1.0 - alpha) * bg.
13103630506bSToomas Soome  * Since our alpha is not from range [0..1], we scale appropriately.
13113630506bSToomas Soome  */
13123630506bSToomas Soome static void
bitmap_cpy(void * dst,void * src,uint32_t size)13133630506bSToomas Soome bitmap_cpy(void *dst, void *src, uint32_t size)
13143630506bSToomas Soome {
13153630506bSToomas Soome #if defined(EFI)
13163630506bSToomas Soome 	EFI_GRAPHICS_OUTPUT_BLT_PIXEL *ps, *pd;
13173630506bSToomas Soome #else
13183630506bSToomas Soome 	struct paletteentry *ps, *pd;
13193630506bSToomas Soome #endif
13203630506bSToomas Soome 	uint32_t i;
13213630506bSToomas Soome 	uint8_t a;
13223630506bSToomas Soome 
13233630506bSToomas Soome 	ps = src;
13243630506bSToomas Soome 	pd = dst;
13253630506bSToomas Soome 
13263630506bSToomas Soome 	/*
13273630506bSToomas Soome 	 * we only implement alpha blending for depth 32.
13283630506bSToomas Soome 	 */
13293630506bSToomas Soome 	for (i = 0; i < size; i ++) {
13303630506bSToomas Soome 		a = ps[i].Reserved;
13313630506bSToomas Soome 		pd[i].Red = alpha_blend(ps[i].Red, pd[i].Red, a);
13323630506bSToomas Soome 		pd[i].Green = alpha_blend(ps[i].Green, pd[i].Green, a);
13333630506bSToomas Soome 		pd[i].Blue = alpha_blend(ps[i].Blue, pd[i].Blue, a);
13343630506bSToomas Soome 		pd[i].Reserved = a;
13353630506bSToomas Soome 	}
13363630506bSToomas Soome }
13373630506bSToomas Soome 
13383630506bSToomas Soome static void *
allocate_glyphbuffer(uint32_t width,uint32_t height)13393630506bSToomas Soome allocate_glyphbuffer(uint32_t width, uint32_t height)
13403630506bSToomas Soome {
13413630506bSToomas Soome 	size_t size;
13423630506bSToomas Soome 
13433630506bSToomas Soome 	size = sizeof (*GlyphBuffer) * width * height;
13443630506bSToomas Soome 	if (size != GlyphBufferSize) {
13453630506bSToomas Soome 		free(GlyphBuffer);
13463630506bSToomas Soome 		GlyphBuffer = malloc(size);
13473630506bSToomas Soome 		if (GlyphBuffer == NULL)
13483630506bSToomas Soome 			return (NULL);
13493630506bSToomas Soome 		GlyphBufferSize = size;
13503630506bSToomas Soome 	}
13513630506bSToomas Soome 	return (GlyphBuffer);
13523630506bSToomas Soome }
13533630506bSToomas Soome 
13543630506bSToomas Soome void
gfx_fb_cons_display(uint32_t x,uint32_t y,uint32_t width,uint32_t height,void * data)13553630506bSToomas Soome gfx_fb_cons_display(uint32_t x, uint32_t y, uint32_t width, uint32_t height,
13563630506bSToomas Soome     void *data)
13573630506bSToomas Soome {
13583630506bSToomas Soome #if defined(EFI)
13596102f43cSToomas Soome 	EFI_GRAPHICS_OUTPUT_BLT_PIXEL *buf, *p;
13603630506bSToomas Soome #else
13616102f43cSToomas Soome 	struct paletteentry *buf, *p;
13623630506bSToomas Soome #endif
13633630506bSToomas Soome 	size_t size;
13643630506bSToomas Soome 
13656102f43cSToomas Soome 	/*
13666102f43cSToomas Soome 	 * If we do have shadow fb, we will use shadow to render data,
13676102f43cSToomas Soome 	 * and copy shadow to video.
13686102f43cSToomas Soome 	 */
13696102f43cSToomas Soome 	if (gfx_state.tg_shadow_fb != NULL) {
13706102f43cSToomas Soome 		uint32_t pitch = gfx_state.tg_fb.fb_width;
13716102f43cSToomas Soome 
13726102f43cSToomas Soome 		/* Copy rectangle line by line. */
13736102f43cSToomas Soome 		p = data;
13746102f43cSToomas Soome 		for (uint32_t sy = 0; sy < height; sy++) {
13756102f43cSToomas Soome 			buf = (void *)(gfx_state.tg_shadow_fb +
13766102f43cSToomas Soome 			    (y - gfx_state.tg_origin.tp_row) * pitch +
13776102f43cSToomas Soome 			    x - gfx_state.tg_origin.tp_col);
13786102f43cSToomas Soome 			bitmap_cpy(buf, &p[sy * width], width);
13796102f43cSToomas Soome 			(void) gfxfb_blt(buf, GfxFbBltBufferToVideo,
13806102f43cSToomas Soome 			    0, 0, x, y, width, 1, 0);
13816102f43cSToomas Soome 			y++;
13826102f43cSToomas Soome 		}
13836102f43cSToomas Soome 		return;
13846102f43cSToomas Soome 	}
13853630506bSToomas Soome 
13863630506bSToomas Soome 	/*
13873630506bSToomas Soome 	 * Common data to display is glyph, use preallocated
13883630506bSToomas Soome 	 * glyph buffer.
13893630506bSToomas Soome 	 */
13903630506bSToomas Soome         if (gfx_state.tg_glyph_size != GlyphBufferSize)
13913630506bSToomas Soome                 (void) allocate_glyphbuffer(width, height);
13923630506bSToomas Soome 
13936102f43cSToomas Soome 	size = width * height * sizeof(*buf);
13943630506bSToomas Soome 	if (size == GlyphBufferSize)
13953630506bSToomas Soome 		buf = GlyphBuffer;
13963630506bSToomas Soome 	else
13973630506bSToomas Soome 		buf = malloc(size);
13983630506bSToomas Soome 	if (buf == NULL)
13993630506bSToomas Soome 		return;
14003630506bSToomas Soome 
14013630506bSToomas Soome 	if (gfxfb_blt(buf, GfxFbBltVideoToBltBuffer, x, y, 0, 0,
14023630506bSToomas Soome 	    width, height, 0) == 0) {
14033630506bSToomas Soome 		bitmap_cpy(buf, data, width * height);
14043630506bSToomas Soome 		(void) gfxfb_blt(buf, GfxFbBltBufferToVideo, 0, 0, x, y,
14053630506bSToomas Soome 		    width, height, 0);
14063630506bSToomas Soome 	}
14073630506bSToomas Soome 	if (buf != GlyphBuffer)
14083630506bSToomas Soome 		free(buf);
14093630506bSToomas Soome }
14103630506bSToomas Soome 
14113630506bSToomas Soome /*
14123630506bSToomas Soome  * Public graphics primitives.
14133630506bSToomas Soome  */
14143630506bSToomas Soome 
14153630506bSToomas Soome static int
isqrt(int num)14163630506bSToomas Soome isqrt(int num)
14173630506bSToomas Soome {
14183630506bSToomas Soome 	int res = 0;
14193630506bSToomas Soome 	int bit = 1 << 30;
14203630506bSToomas Soome 
14213630506bSToomas Soome 	/* "bit" starts at the highest power of four <= the argument. */
14223630506bSToomas Soome 	while (bit > num)
14233630506bSToomas Soome 		bit >>= 2;
14243630506bSToomas Soome 
14253630506bSToomas Soome 	while (bit != 0) {
14263630506bSToomas Soome 		if (num >= res + bit) {
14273630506bSToomas Soome 			num -= res + bit;
14283630506bSToomas Soome 			res = (res >> 1) + bit;
14293630506bSToomas Soome 		} else {
14303630506bSToomas Soome 			res >>= 1;
14313630506bSToomas Soome 		}
14323630506bSToomas Soome 		bit >>= 2;
14333630506bSToomas Soome 	}
14343630506bSToomas Soome 	return (res);
14353630506bSToomas Soome }
14363630506bSToomas Soome 
14375365af66SToomas Soome static uint32_t
gfx_fb_getcolor(void)14385365af66SToomas Soome gfx_fb_getcolor(void)
14393630506bSToomas Soome {
14403630506bSToomas Soome 	uint32_t c;
14413630506bSToomas Soome 	const teken_attr_t *ap;
14423630506bSToomas Soome 
14433630506bSToomas Soome 	ap = teken_get_curattr(&gfx_state.tg_teken);
14443630506bSToomas Soome         if (ap->ta_format & TF_REVERSE) {
14453630506bSToomas Soome 		c = ap->ta_bgcolor;
14463630506bSToomas Soome 		if (ap->ta_format & TF_BLINK)
14473630506bSToomas Soome 			c |= TC_LIGHT;
14483630506bSToomas Soome 	} else {
14493630506bSToomas Soome 		c = ap->ta_fgcolor;
14503630506bSToomas Soome 		if (ap->ta_format & TF_BOLD)
14513630506bSToomas Soome 			c |= TC_LIGHT;
14523630506bSToomas Soome 	}
14533630506bSToomas Soome 
14545365af66SToomas Soome 	return (gfx_fb_color_map(c));
14555365af66SToomas Soome }
14565365af66SToomas Soome 
14575365af66SToomas Soome /* set pixel in framebuffer using gfx coordinates */
14585365af66SToomas Soome void
gfx_fb_setpixel(uint32_t x,uint32_t y)14595365af66SToomas Soome gfx_fb_setpixel(uint32_t x, uint32_t y)
14605365af66SToomas Soome {
14615365af66SToomas Soome 	uint32_t c;
14625365af66SToomas Soome 
14635365af66SToomas Soome 	if (gfx_state.tg_fb_type == FB_TEXT)
14645365af66SToomas Soome 		return;
14655365af66SToomas Soome 
14665365af66SToomas Soome 	c = gfx_fb_getcolor();
14673630506bSToomas Soome 
14683630506bSToomas Soome 	if (x >= gfx_state.tg_fb.fb_width ||
14693630506bSToomas Soome 	    y >= gfx_state.tg_fb.fb_height)
14703630506bSToomas Soome 		return;
14713630506bSToomas Soome 
14723630506bSToomas Soome 	gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x, y, 1, 1, 0);
14733630506bSToomas Soome }
14743630506bSToomas Soome 
14753630506bSToomas Soome /*
14763630506bSToomas Soome  * draw rectangle in framebuffer using gfx coordinates.
14773630506bSToomas Soome  */
14783630506bSToomas Soome void
gfx_fb_drawrect(uint32_t x1,uint32_t y1,uint32_t x2,uint32_t y2,uint32_t fill)14793630506bSToomas Soome gfx_fb_drawrect(uint32_t x1, uint32_t y1, uint32_t x2, uint32_t y2,
14803630506bSToomas Soome     uint32_t fill)
14813630506bSToomas Soome {
14825365af66SToomas Soome 	uint32_t c;
14833630506bSToomas Soome 
14843630506bSToomas Soome 	if (gfx_state.tg_fb_type == FB_TEXT)
14853630506bSToomas Soome 		return;
14863630506bSToomas Soome 
14875365af66SToomas Soome 	c = gfx_fb_getcolor();
14885365af66SToomas Soome 
14895365af66SToomas Soome 	if (fill != 0) {
14905365af66SToomas Soome 		gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x1, y1, x2 - x1,
14915365af66SToomas Soome 		    y2 - y1, 0);
14923630506bSToomas Soome 	} else {
14935365af66SToomas Soome 		gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x1, y1, x2 - x1, 1, 0);
14945365af66SToomas Soome 		gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x1, y2, x2 - x1, 1, 0);
14955365af66SToomas Soome 		gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x1, y1, 1, y2 - y1, 0);
14965365af66SToomas Soome 		gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x2, y1, 1, y2 - y1, 0);
14973630506bSToomas Soome 	}
14983630506bSToomas Soome }
14993630506bSToomas Soome 
15003630506bSToomas Soome void
gfx_fb_line(uint32_t x0,uint32_t y0,uint32_t x1,uint32_t y1,uint32_t wd)15013630506bSToomas Soome gfx_fb_line(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t wd)
15023630506bSToomas Soome {
15033630506bSToomas Soome 	int dx, sx, dy, sy;
15043630506bSToomas Soome 	int err, e2, x2, y2, ed, width;
15053630506bSToomas Soome 
15063630506bSToomas Soome 	if (gfx_state.tg_fb_type == FB_TEXT)
15073630506bSToomas Soome 		return;
15083630506bSToomas Soome 
15093630506bSToomas Soome 	width = wd;
15103630506bSToomas Soome 	sx = x0 < x1? 1 : -1;
15113630506bSToomas Soome 	sy = y0 < y1? 1 : -1;
15123630506bSToomas Soome 	dx = x1 > x0? x1 - x0 : x0 - x1;
15133630506bSToomas Soome 	dy = y1 > y0? y1 - y0 : y0 - y1;
15143630506bSToomas Soome 	err = dx + dy;
15153630506bSToomas Soome 	ed = dx + dy == 0 ? 1: isqrt(dx * dx + dy * dy);
15163630506bSToomas Soome 
15173630506bSToomas Soome 	for (;;) {
15183630506bSToomas Soome 		gfx_fb_setpixel(x0, y0);
15193630506bSToomas Soome 		e2 = err;
15203630506bSToomas Soome 		x2 = x0;
15213630506bSToomas Soome 		if ((e2 << 1) >= -dx) {		/* x step */
15223630506bSToomas Soome 			e2 += dy;
15233630506bSToomas Soome 			y2 = y0;
15243630506bSToomas Soome 			while (e2 < ed * width &&
15253630506bSToomas Soome 			    (y1 != (uint32_t)y2 || dx > dy)) {
15263630506bSToomas Soome 				y2 += sy;
15273630506bSToomas Soome 				gfx_fb_setpixel(x0, y2);
15283630506bSToomas Soome 				e2 += dx;
15293630506bSToomas Soome 			}
15303630506bSToomas Soome 			if (x0 == x1)
15313630506bSToomas Soome 				break;
15323630506bSToomas Soome 			e2 = err;
15333630506bSToomas Soome 			err -= dy;
15343630506bSToomas Soome 			x0 += sx;
15353630506bSToomas Soome 		}
15363630506bSToomas Soome 		if ((e2 << 1) <= dy) {		/* y step */
15373630506bSToomas Soome 			e2 = dx-e2;
15383630506bSToomas Soome 			while (e2 < ed * width &&
15393630506bSToomas Soome 			    (x1 != (uint32_t)x2 || dx < dy)) {
15403630506bSToomas Soome 				x2 += sx;
15413630506bSToomas Soome 				gfx_fb_setpixel(x2, y0);
15423630506bSToomas Soome 				e2 += dy;
15433630506bSToomas Soome 			}
15443630506bSToomas Soome 			if (y0 == y1)
15453630506bSToomas Soome 				break;
15463630506bSToomas Soome 			err += dx;
15473630506bSToomas Soome 			y0 += sy;
15483630506bSToomas Soome 		}
15493630506bSToomas Soome 	}
15503630506bSToomas Soome }
15513630506bSToomas Soome 
15523630506bSToomas Soome /*
15533630506bSToomas Soome  * quadratic Bézier curve limited to gradients without sign change.
15543630506bSToomas Soome  */
15553630506bSToomas Soome 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)15563630506bSToomas Soome gfx_fb_bezier(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t x2,
15573630506bSToomas Soome     uint32_t y2, uint32_t wd)
15583630506bSToomas Soome {
15593630506bSToomas Soome 	int sx, sy, xx, yy, xy, width;
15603630506bSToomas Soome 	int dx, dy, err, curvature;
15613630506bSToomas Soome 	int i;
15623630506bSToomas Soome 
15633630506bSToomas Soome 	if (gfx_state.tg_fb_type == FB_TEXT)
15643630506bSToomas Soome 		return;
15653630506bSToomas Soome 
15663630506bSToomas Soome 	width = wd;
15673630506bSToomas Soome 	sx = x2 - x1;
15683630506bSToomas Soome 	sy = y2 - y1;
15693630506bSToomas Soome 	xx = x0 - x1;
15703630506bSToomas Soome 	yy = y0 - y1;
15713630506bSToomas Soome 	curvature = xx*sy - yy*sx;
15723630506bSToomas Soome 
15733630506bSToomas Soome 	if (sx*sx + sy*sy > xx*xx+yy*yy) {
15743630506bSToomas Soome 		x2 = x0;
15753630506bSToomas Soome 		x0 = sx + x1;
15763630506bSToomas Soome 		y2 = y0;
15773630506bSToomas Soome 		y0 = sy + y1;
15783630506bSToomas Soome 		curvature = -curvature;
15793630506bSToomas Soome 	}
15803630506bSToomas Soome 	if (curvature != 0) {
15813630506bSToomas Soome 		xx += sx;
15823630506bSToomas Soome 		sx = x0 < x2? 1 : -1;
15833630506bSToomas Soome 		xx *= sx;
15843630506bSToomas Soome 		yy += sy;
15853630506bSToomas Soome 		sy = y0 < y2? 1 : -1;
15863630506bSToomas Soome 		yy *= sy;
15873630506bSToomas Soome 		xy = (xx*yy) << 1;
15883630506bSToomas Soome 		xx *= xx;
15893630506bSToomas Soome 		yy *= yy;
15903630506bSToomas Soome 		if (curvature * sx * sy < 0) {
15913630506bSToomas Soome 			xx = -xx;
15923630506bSToomas Soome 			yy = -yy;
15933630506bSToomas Soome 			xy = -xy;
15943630506bSToomas Soome 			curvature = -curvature;
15953630506bSToomas Soome 		}
15963630506bSToomas Soome 		dx = 4 * sy * curvature * (x1 - x0) + xx - xy;
15973630506bSToomas Soome 		dy = 4 * sx * curvature * (y0 - y1) + yy - xy;
15983630506bSToomas Soome 		xx += xx;
15993630506bSToomas Soome 		yy += yy;
16003630506bSToomas Soome 		err = dx + dy + xy;
16013630506bSToomas Soome 		do {
16023630506bSToomas Soome 			for (i = 0; i <= width; i++)
16033630506bSToomas Soome 				gfx_fb_setpixel(x0 + i, y0);
16043630506bSToomas Soome 			if (x0 == x2 && y0 == y2)
16053630506bSToomas Soome 				return;  /* last pixel -> curve finished */
16063630506bSToomas Soome 			y1 = 2 * err < dx;
16073630506bSToomas Soome 			if (2 * err > dy) {
16083630506bSToomas Soome 				x0 += sx;
16093630506bSToomas Soome 				dx -= xy;
16103630506bSToomas Soome 				dy += yy;
16113630506bSToomas Soome 				err += dy;
16123630506bSToomas Soome 			}
16133630506bSToomas Soome 			if (y1 != 0) {
16143630506bSToomas Soome 				y0 += sy;
16153630506bSToomas Soome 				dy -= xy;
16163630506bSToomas Soome 				dx += xx;
16173630506bSToomas Soome 				err += dx;
16183630506bSToomas Soome 			}
16193630506bSToomas Soome 		} while (dy < dx); /* gradient negates -> algorithm fails */
16203630506bSToomas Soome 	}
16213630506bSToomas Soome 	gfx_fb_line(x0, y0, x2, y2, width);
16223630506bSToomas Soome }
16233630506bSToomas Soome 
16243630506bSToomas Soome /*
16253630506bSToomas Soome  * draw rectangle using terminal coordinates and current foreground color.
16263630506bSToomas Soome  */
16273630506bSToomas Soome void
gfx_term_drawrect(uint32_t ux1,uint32_t uy1,uint32_t ux2,uint32_t uy2)16283630506bSToomas Soome gfx_term_drawrect(uint32_t ux1, uint32_t uy1, uint32_t ux2, uint32_t uy2)
16293630506bSToomas Soome {
16303630506bSToomas Soome 	int x1, y1, x2, y2;
16313630506bSToomas Soome 	int xshift, yshift;
16323630506bSToomas Soome 	int width, i;
16333630506bSToomas Soome 	uint32_t vf_width, vf_height;
16343630506bSToomas Soome 	teken_rect_t r;
16353630506bSToomas Soome 
16363630506bSToomas Soome 	if (gfx_state.tg_fb_type == FB_TEXT)
16373630506bSToomas Soome 		return;
16383630506bSToomas Soome 
16393630506bSToomas Soome 	vf_width = gfx_state.tg_font.vf_width;
16403630506bSToomas Soome 	vf_height = gfx_state.tg_font.vf_height;
16413630506bSToomas Soome 	width = vf_width / 4;			/* line width */
16423630506bSToomas Soome 	xshift = (vf_width - width) / 2;
16433630506bSToomas Soome 	yshift = (vf_height - width) / 2;
16443630506bSToomas Soome 
16453630506bSToomas Soome 	/* Shift coordinates */
16463630506bSToomas Soome 	if (ux1 != 0)
16473630506bSToomas Soome 		ux1--;
16483630506bSToomas Soome 	if (uy1 != 0)
16493630506bSToomas Soome 		uy1--;
16503630506bSToomas Soome 	ux2--;
16513630506bSToomas Soome 	uy2--;
16523630506bSToomas Soome 
16533630506bSToomas Soome 	/* mark area used in terminal */
16543630506bSToomas Soome 	r.tr_begin.tp_col = ux1;
16553630506bSToomas Soome 	r.tr_begin.tp_row = uy1;
16563630506bSToomas Soome 	r.tr_end.tp_col = ux2 + 1;
16573630506bSToomas Soome 	r.tr_end.tp_row = uy2 + 1;
16583630506bSToomas Soome 
16593630506bSToomas Soome 	term_image_display(&gfx_state, &r);
16603630506bSToomas Soome 
16613630506bSToomas Soome 	/*
16623630506bSToomas Soome 	 * Draw horizontal lines width points thick, shifted from outer edge.
16633630506bSToomas Soome 	 */
16643630506bSToomas Soome 	x1 = (ux1 + 1) * vf_width + gfx_state.tg_origin.tp_col;
16653630506bSToomas Soome 	y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row + yshift;
16663630506bSToomas Soome 	x2 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
16673630506bSToomas Soome 	gfx_fb_drawrect(x1, y1, x2, y1 + width, 1);
16683630506bSToomas Soome 	y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
16693630506bSToomas Soome 	y2 += vf_height - yshift - width;
16703630506bSToomas Soome 	gfx_fb_drawrect(x1, y2, x2, y2 + width, 1);
16713630506bSToomas Soome 
16723630506bSToomas Soome 	/*
16733630506bSToomas Soome 	 * Draw vertical lines width points thick, shifted from outer edge.
16743630506bSToomas Soome 	 */
16753630506bSToomas Soome 	x1 = ux1 * vf_width + gfx_state.tg_origin.tp_col + xshift;
16763630506bSToomas Soome 	y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row;
16773630506bSToomas Soome 	y1 += vf_height;
16783630506bSToomas Soome 	y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
16793630506bSToomas Soome 	gfx_fb_drawrect(x1, y1, x1 + width, y2, 1);
16803630506bSToomas Soome 	x1 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
16813630506bSToomas Soome 	x1 += vf_width - xshift - width;
16823630506bSToomas Soome 	gfx_fb_drawrect(x1, y1, x1 + width, y2, 1);
16833630506bSToomas Soome 
16843630506bSToomas Soome 	/* Draw upper left corner. */
16853630506bSToomas Soome 	x1 = ux1 * vf_width + gfx_state.tg_origin.tp_col + xshift;
16863630506bSToomas Soome 	y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row;
16873630506bSToomas Soome 	y1 += vf_height;
16883630506bSToomas Soome 
16893630506bSToomas Soome 	x2 = ux1 * vf_width + gfx_state.tg_origin.tp_col;
16903630506bSToomas Soome 	x2 += vf_width;
16913630506bSToomas Soome 	y2 = uy1 * vf_height + gfx_state.tg_origin.tp_row + yshift;
16923630506bSToomas Soome 	for (i = 0; i <= width; i++)
16933630506bSToomas Soome 		gfx_fb_bezier(x1 + i, y1, x1 + i, y2 + i, x2, y2 + i, width-i);
16943630506bSToomas Soome 
16953630506bSToomas Soome 	/* Draw lower left corner. */
16963630506bSToomas Soome 	x1 = ux1 * vf_width + gfx_state.tg_origin.tp_col;
16973630506bSToomas Soome 	x1 += vf_width;
16983630506bSToomas Soome 	y1 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
16993630506bSToomas Soome 	y1 += vf_height - yshift;
17003630506bSToomas Soome 	x2 = ux1 * vf_width + gfx_state.tg_origin.tp_col + xshift;
17013630506bSToomas Soome 	y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
17023630506bSToomas Soome 	for (i = 0; i <= width; i++)
17033630506bSToomas Soome 		gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i);
17043630506bSToomas Soome 
17053630506bSToomas Soome 	/* Draw upper right corner. */
17063630506bSToomas Soome 	x1 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
17073630506bSToomas Soome 	y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row + yshift;
17083630506bSToomas Soome 	x2 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
17093630506bSToomas Soome 	x2 += vf_width - xshift - width;
17103630506bSToomas Soome 	y2 = uy1 * vf_height + gfx_state.tg_origin.tp_row;
17113630506bSToomas Soome 	y2 += vf_height;
17123630506bSToomas Soome 	for (i = 0; i <= width; i++)
17133630506bSToomas Soome 		gfx_fb_bezier(x1, y1 + i, x2 + i, y1 + i, x2 + i, y2, width-i);
17143630506bSToomas Soome 
17153630506bSToomas Soome 	/* Draw lower right corner. */
17163630506bSToomas Soome 	x1 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
17173630506bSToomas Soome 	y1 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
17183630506bSToomas Soome 	y1 += vf_height - yshift;
17193630506bSToomas Soome 	x2 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
17203630506bSToomas Soome 	x2 += vf_width - xshift - width;
17213630506bSToomas Soome 	y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
17223630506bSToomas Soome 	for (i = 0; i <= width; i++)
17233630506bSToomas Soome 		gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i);
17243630506bSToomas Soome }
17253630506bSToomas Soome 
17263630506bSToomas Soome int
gfx_fb_putimage(png_t * png,uint32_t ux1,uint32_t uy1,uint32_t ux2,uint32_t uy2,uint32_t flags)17273630506bSToomas Soome gfx_fb_putimage(png_t *png, uint32_t ux1, uint32_t uy1, uint32_t ux2,
17283630506bSToomas Soome     uint32_t uy2, uint32_t flags)
17293630506bSToomas Soome {
17303630506bSToomas Soome #if defined(EFI)
17313630506bSToomas Soome 	EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
17323630506bSToomas Soome #else
17333630506bSToomas Soome 	struct paletteentry *p;
17343630506bSToomas Soome #endif
17353630506bSToomas Soome 	uint8_t *data;
17363630506bSToomas Soome 	uint32_t i, j, x, y, fheight, fwidth;
17373630506bSToomas Soome 	int rs, gs, bs;
17383630506bSToomas Soome 	uint8_t r, g, b, a;
17393630506bSToomas Soome 	bool scale = false;
17403630506bSToomas Soome 	bool trace = false;
17413630506bSToomas Soome 	teken_rect_t rect;
17423630506bSToomas Soome 
17433630506bSToomas Soome 	trace = (flags & FL_PUTIMAGE_DEBUG) != 0;
17443630506bSToomas Soome 
17453630506bSToomas Soome 	if (gfx_state.tg_fb_type == FB_TEXT) {
17463630506bSToomas Soome 		if (trace)
17473630506bSToomas Soome 			printf("Framebuffer not active.\n");
17483630506bSToomas Soome 		return (1);
17493630506bSToomas Soome 	}
17503630506bSToomas Soome 
17513630506bSToomas Soome 	if (png->color_type != PNG_TRUECOLOR_ALPHA) {
17523630506bSToomas Soome 		if (trace)
17533630506bSToomas Soome 			printf("Not truecolor image.\n");
17543630506bSToomas Soome 		return (1);
17553630506bSToomas Soome 	}
17563630506bSToomas Soome 
17573630506bSToomas Soome 	if (ux1 > gfx_state.tg_fb.fb_width ||
17583630506bSToomas Soome 	    uy1 > gfx_state.tg_fb.fb_height) {
17593630506bSToomas Soome 		if (trace)
17603630506bSToomas Soome 			printf("Top left coordinate off screen.\n");
17613630506bSToomas Soome 		return (1);
17623630506bSToomas Soome 	}
17633630506bSToomas Soome 
17643630506bSToomas Soome 	if (png->width > UINT16_MAX || png->height > UINT16_MAX) {
17653630506bSToomas Soome 		if (trace)
17663630506bSToomas Soome 			printf("Image too large.\n");
17673630506bSToomas Soome 		return (1);
17683630506bSToomas Soome 	}
17693630506bSToomas Soome 
17703630506bSToomas Soome 	if (png->width < 1 || png->height < 1) {
17713630506bSToomas Soome 		if (trace)
17723630506bSToomas Soome 			printf("Image too small.\n");
17733630506bSToomas Soome 		return (1);
17743630506bSToomas Soome 	}
17753630506bSToomas Soome 
17763630506bSToomas Soome 	/*
17773630506bSToomas Soome 	 * If 0 was passed for either ux2 or uy2, then calculate the missing
17783630506bSToomas Soome 	 * part of the bottom right coordinate.
17793630506bSToomas Soome 	 */
17803630506bSToomas Soome 	scale = true;
17813630506bSToomas Soome 	if (ux2 == 0 && uy2 == 0) {
17823630506bSToomas Soome 		/* Both 0, use the native resolution of the image */
17833630506bSToomas Soome 		ux2 = ux1 + png->width;
17843630506bSToomas Soome 		uy2 = uy1 + png->height;
17853630506bSToomas Soome 		scale = false;
17863630506bSToomas Soome 	} else if (ux2 == 0) {
17873630506bSToomas Soome 		/* Set ux2 from uy2/uy1 to maintain aspect ratio */
17883630506bSToomas Soome 		ux2 = ux1 + (png->width * (uy2 - uy1)) / png->height;
17893630506bSToomas Soome 	} else if (uy2 == 0) {
17903630506bSToomas Soome 		/* Set uy2 from ux2/ux1 to maintain aspect ratio */
17913630506bSToomas Soome 		uy2 = uy1 + (png->height * (ux2 - ux1)) / png->width;
17923630506bSToomas Soome 	}
17933630506bSToomas Soome 
17943630506bSToomas Soome 	if (ux2 > gfx_state.tg_fb.fb_width ||
17953630506bSToomas Soome 	    uy2 > gfx_state.tg_fb.fb_height) {
17963630506bSToomas Soome 		if (trace)
17973630506bSToomas Soome 			printf("Bottom right coordinate off screen.\n");
17983630506bSToomas Soome 		return (1);
17993630506bSToomas Soome 	}
18003630506bSToomas Soome 
18013630506bSToomas Soome 	fwidth = ux2 - ux1;
18023630506bSToomas Soome 	fheight = uy2 - uy1;
18033630506bSToomas Soome 
18043630506bSToomas Soome 	/*
18053630506bSToomas Soome 	 * If the original image dimensions have been passed explicitly,
18063630506bSToomas Soome 	 * disable scaling.
18073630506bSToomas Soome 	 */
18083630506bSToomas Soome 	if (fwidth == png->width && fheight == png->height)
18093630506bSToomas Soome 		scale = false;
18103630506bSToomas Soome 
18113630506bSToomas Soome 	if (ux1 == 0) {
18123630506bSToomas Soome 		/*
18133630506bSToomas Soome 		 * No top left X co-ordinate (real coordinates start at 1),
18143630506bSToomas Soome 		 * place as far right as it will fit.
18153630506bSToomas Soome 		 */
18163630506bSToomas Soome 		ux2 = gfx_state.tg_fb.fb_width - gfx_state.tg_origin.tp_col;
18173630506bSToomas Soome 		ux1 = ux2 - fwidth;
18183630506bSToomas Soome 	}
18193630506bSToomas Soome 
18203630506bSToomas Soome 	if (uy1 == 0) {
18213630506bSToomas Soome 		/*
18223630506bSToomas Soome 		 * No top left Y co-ordinate (real coordinates start at 1),
18233630506bSToomas Soome 		 * place as far down as it will fit.
18243630506bSToomas Soome 		 */
18253630506bSToomas Soome 		uy2 = gfx_state.tg_fb.fb_height - gfx_state.tg_origin.tp_row;
18263630506bSToomas Soome 		uy1 = uy2 - fheight;
18273630506bSToomas Soome 	}
18283630506bSToomas Soome 
18293630506bSToomas Soome 	if (ux1 >= ux2 || uy1 >= uy2) {
18303630506bSToomas Soome 		if (trace)
18313630506bSToomas Soome 			printf("Image dimensions reversed.\n");
18323630506bSToomas Soome 		return (1);
18333630506bSToomas Soome 	}
18343630506bSToomas Soome 
18353630506bSToomas Soome 	if (fwidth < 2 || fheight < 2) {
18363630506bSToomas Soome 		if (trace)
18373630506bSToomas Soome 			printf("Target area too small\n");
18383630506bSToomas Soome 		return (1);
18393630506bSToomas Soome 	}
18403630506bSToomas Soome 
18413630506bSToomas Soome 	if (trace)
18423630506bSToomas Soome 		printf("Image %ux%u -> %ux%u @%ux%u\n",
18433630506bSToomas Soome 		    png->width, png->height, fwidth, fheight, ux1, uy1);
18443630506bSToomas Soome 
18453630506bSToomas Soome 	rect.tr_begin.tp_col = ux1 / gfx_state.tg_font.vf_width;
18463630506bSToomas Soome 	rect.tr_begin.tp_row = uy1 / gfx_state.tg_font.vf_height;
18473630506bSToomas Soome 	rect.tr_end.tp_col = (ux1 + fwidth) / gfx_state.tg_font.vf_width;
18483630506bSToomas Soome 	rect.tr_end.tp_row = (uy1 + fheight) / gfx_state.tg_font.vf_height;
18493630506bSToomas Soome 
18503630506bSToomas Soome 	/*
18513630506bSToomas Soome 	 * mark area used in terminal
18523630506bSToomas Soome 	 */
18533630506bSToomas Soome 	if (!(flags & FL_PUTIMAGE_NOSCROLL))
18543630506bSToomas Soome 		term_image_display(&gfx_state, &rect);
18553630506bSToomas Soome 
18563630506bSToomas Soome 	if ((flags & FL_PUTIMAGE_BORDER))
18573630506bSToomas Soome 		gfx_fb_drawrect(ux1, uy1, ux2, uy2, 0);
18583630506bSToomas Soome 
18593630506bSToomas Soome 	data = malloc(fwidth * fheight * sizeof(*p));
18603630506bSToomas Soome 	p = (void *)data;
18613630506bSToomas Soome 	if (data == NULL) {
18623630506bSToomas Soome 		if (trace)
18633630506bSToomas Soome 			printf("Out of memory.\n");
18643630506bSToomas Soome 		return (1);
18653630506bSToomas Soome 	}
18663630506bSToomas Soome 
18673630506bSToomas Soome 	/*
18683630506bSToomas Soome 	 * Build image for our framebuffer.
18693630506bSToomas Soome 	 */
18703630506bSToomas Soome 
18713630506bSToomas Soome 	/* Helper to calculate the pixel index from the source png */
18723630506bSToomas Soome #define	GETPIXEL(xx, yy)	(((yy) * png->width + (xx)) * png->bpp)
18733630506bSToomas Soome 
18743630506bSToomas Soome 	/*
18753630506bSToomas Soome 	 * For each of the x and y directions, calculate the number of pixels
18763630506bSToomas Soome 	 * in the source image that correspond to a single pixel in the target.
18773630506bSToomas Soome 	 * Use fixed-point arithmetic with 16-bits for each of the integer and
18783630506bSToomas Soome 	 * fractional parts.
18793630506bSToomas Soome 	 */
18803630506bSToomas Soome 	const uint32_t wcstep = ((png->width - 1) << 16) / (fwidth - 1);
18813630506bSToomas Soome 	const uint32_t hcstep = ((png->height - 1) << 16) / (fheight - 1);
18823630506bSToomas Soome 
18833630506bSToomas Soome 	rs = 8 - (fls(gfx_state.tg_fb.fb_mask_red) -
18843630506bSToomas Soome 	    ffs(gfx_state.tg_fb.fb_mask_red) + 1);
18853630506bSToomas Soome 	gs = 8 - (fls(gfx_state.tg_fb.fb_mask_green) -
18863630506bSToomas Soome 	    ffs(gfx_state.tg_fb.fb_mask_green) + 1);
18873630506bSToomas Soome 	bs = 8 - (fls(gfx_state.tg_fb.fb_mask_blue) -
18883630506bSToomas Soome 	    ffs(gfx_state.tg_fb.fb_mask_blue) + 1);
18893630506bSToomas Soome 
18903630506bSToomas Soome 	uint32_t hc = 0;
18913630506bSToomas Soome 	for (y = 0; y < fheight; y++) {
18923630506bSToomas Soome 		uint32_t hc2 = (hc >> 9) & 0x7f;
18933630506bSToomas Soome 		uint32_t hc1 = 0x80 - hc2;
18943630506bSToomas Soome 
18953630506bSToomas Soome 		uint32_t offset_y = hc >> 16;
18963630506bSToomas Soome 		uint32_t offset_y1 = offset_y + 1;
18973630506bSToomas Soome 
18983630506bSToomas Soome 		uint32_t wc = 0;
18993630506bSToomas Soome 		for (x = 0; x < fwidth; x++) {
19003630506bSToomas Soome 			uint32_t wc2 = (wc >> 9) & 0x7f;
19013630506bSToomas Soome 			uint32_t wc1 = 0x80 - wc2;
19023630506bSToomas Soome 
19033630506bSToomas Soome 			uint32_t offset_x = wc >> 16;
19043630506bSToomas Soome 			uint32_t offset_x1 = offset_x + 1;
19053630506bSToomas Soome 
19063630506bSToomas Soome 			/* Target pixel index */
19073630506bSToomas Soome 			j = y * fwidth + x;
19083630506bSToomas Soome 
19093630506bSToomas Soome 			if (!scale) {
19103630506bSToomas Soome 				i = GETPIXEL(x, y);
19113630506bSToomas Soome 				r = png->image[i];
19123630506bSToomas Soome 				g = png->image[i + 1];
19133630506bSToomas Soome 				b = png->image[i + 2];
19143630506bSToomas Soome 				a = png->image[i + 3];
19153630506bSToomas Soome 			} else {
19163630506bSToomas Soome 				uint8_t pixel[4];
19173630506bSToomas Soome 
19183630506bSToomas Soome 				uint32_t p00 = GETPIXEL(offset_x, offset_y);
19193630506bSToomas Soome 				uint32_t p01 = GETPIXEL(offset_x, offset_y1);
19203630506bSToomas Soome 				uint32_t p10 = GETPIXEL(offset_x1, offset_y);
19213630506bSToomas Soome 				uint32_t p11 = GETPIXEL(offset_x1, offset_y1);
19223630506bSToomas Soome 
19233630506bSToomas Soome 				/*
19243630506bSToomas Soome 				 * Given a 2x2 array of pixels in the source
19253630506bSToomas Soome 				 * image, combine them to produce a single
19263630506bSToomas Soome 				 * value for the pixel in the target image.
19273630506bSToomas Soome 				 * Each column of pixels is combined using
19283630506bSToomas Soome 				 * a weighted average where the top and bottom
19293630506bSToomas Soome 				 * pixels contribute hc1 and hc2 respectively.
19303630506bSToomas Soome 				 * The calculation for bottom pixel pB and
19313630506bSToomas Soome 				 * top pixel pT is:
19323630506bSToomas Soome 				 *   (pT * hc1 + pB * hc2) / (hc1 + hc2)
19333630506bSToomas Soome 				 * Once the values are determined for the two
19343630506bSToomas Soome 				 * columns of pixels, then the columns are
19353630506bSToomas Soome 				 * averaged together in the same way but using
19363630506bSToomas Soome 				 * wc1 and wc2 for the weightings.
19373630506bSToomas Soome 				 *
19383630506bSToomas Soome 				 * Since hc1 and hc2 are chosen so that
19393630506bSToomas Soome 				 * hc1 + hc2 == 128 (and same for wc1 + wc2),
19403630506bSToomas Soome 				 * the >> 14 below is a quick way to divide by
19413630506bSToomas Soome 				 * (hc1 + hc2) * (wc1 + wc2)
19423630506bSToomas Soome 				 */
19433630506bSToomas Soome 				for (i = 0; i < 4; i++)
19443630506bSToomas Soome 					pixel[i] = (
19453630506bSToomas Soome 					    (png->image[p00 + i] * hc1 +
19463630506bSToomas Soome 					    png->image[p01 + i] * hc2) * wc1 +
19473630506bSToomas Soome 					    (png->image[p10 + i] * hc1 +
19483630506bSToomas Soome 					    png->image[p11 + i] * hc2) * wc2)
19493630506bSToomas Soome 					    >> 14;
19503630506bSToomas Soome 
19513630506bSToomas Soome 				r = pixel[0];
19523630506bSToomas Soome 				g = pixel[1];
19533630506bSToomas Soome 				b = pixel[2];
19543630506bSToomas Soome 				a = pixel[3];
19553630506bSToomas Soome 			}
19563630506bSToomas Soome 
19573630506bSToomas Soome 			if (trace)
19583630506bSToomas Soome 				printf("r/g/b: %x/%x/%x\n", r, g, b);
19593630506bSToomas Soome 			/*
19603630506bSToomas Soome 			 * Rough colorspace reduction for 15/16 bit colors.
19613630506bSToomas Soome 			 */
19623630506bSToomas Soome 			p[j].Red = r >> rs;
19633630506bSToomas Soome                         p[j].Green = g >> gs;
19643630506bSToomas Soome                         p[j].Blue = b >> bs;
19653630506bSToomas Soome                         p[j].Reserved = a;
19663630506bSToomas Soome 
19673630506bSToomas Soome 			wc += wcstep;
19683630506bSToomas Soome 		}
19693630506bSToomas Soome 		hc += hcstep;
19703630506bSToomas Soome 	}
19713630506bSToomas Soome 
19723630506bSToomas Soome 	gfx_fb_cons_display(ux1, uy1, fwidth, fheight, data);
19733630506bSToomas Soome 	free(data);
19743630506bSToomas Soome 	return (0);
19753630506bSToomas Soome }
19763630506bSToomas Soome 
19773630506bSToomas Soome /*
19783630506bSToomas Soome  * Reset font flags to FONT_AUTO.
19793630506bSToomas Soome  */
19803630506bSToomas Soome void
reset_font_flags(void)19813630506bSToomas Soome reset_font_flags(void)
19823630506bSToomas Soome {
19833630506bSToomas Soome 	struct fontlist *fl;
19843630506bSToomas Soome 
19853630506bSToomas Soome 	STAILQ_FOREACH(fl, &fonts, font_next) {
19863630506bSToomas Soome 		fl->font_flags = FONT_AUTO;
19873630506bSToomas Soome 	}
19883630506bSToomas Soome }
19893630506bSToomas Soome 
1990becaac39SToomas Soome /* Return  w^2 + h^2 or 0, if the dimensions are unknown */
1991becaac39SToomas Soome static unsigned
edid_diagonal_squared(void)1992becaac39SToomas Soome edid_diagonal_squared(void)
1993becaac39SToomas Soome {
1994becaac39SToomas Soome 	unsigned w, h;
1995becaac39SToomas Soome 
1996becaac39SToomas Soome 	if (edid_info == NULL)
1997becaac39SToomas Soome 		return (0);
1998becaac39SToomas Soome 
1999becaac39SToomas Soome 	w = edid_info->display.max_horizontal_image_size;
2000becaac39SToomas Soome 	h = edid_info->display.max_vertical_image_size;
2001becaac39SToomas Soome 
2002becaac39SToomas Soome 	/* If either one is 0, we have aspect ratio, not size */
2003becaac39SToomas Soome 	if (w == 0 || h == 0)
2004becaac39SToomas Soome 		return (0);
2005becaac39SToomas Soome 
2006becaac39SToomas Soome 	/*
2007becaac39SToomas Soome 	 * some monitors encode the aspect ratio instead of the physical size.
2008becaac39SToomas Soome 	 */
2009becaac39SToomas Soome 	if ((w == 16 && h == 9) || (w == 16 && h == 10) ||
2010becaac39SToomas Soome 	    (w == 4 && h == 3) || (w == 5 && h == 4))
2011becaac39SToomas Soome 		return (0);
2012becaac39SToomas Soome 
2013becaac39SToomas Soome 	/*
2014becaac39SToomas Soome 	 * translate cm to inch, note we scale by 100 here.
2015becaac39SToomas Soome 	 */
2016becaac39SToomas Soome 	w = w * 100 / 254;
2017becaac39SToomas Soome 	h = h * 100 / 254;
2018becaac39SToomas Soome 
2019becaac39SToomas Soome 	/* Return w^2 + h^2 */
2020becaac39SToomas Soome 	return (w * w + h * h);
2021becaac39SToomas Soome }
2022becaac39SToomas Soome 
2023becaac39SToomas Soome /*
2024becaac39SToomas Soome  * calculate pixels per inch.
2025becaac39SToomas Soome  */
2026becaac39SToomas Soome static unsigned
gfx_get_ppi(void)2027becaac39SToomas Soome gfx_get_ppi(void)
2028becaac39SToomas Soome {
2029becaac39SToomas Soome 	unsigned dp, di;
2030becaac39SToomas Soome 
2031becaac39SToomas Soome 	di = edid_diagonal_squared();
2032becaac39SToomas Soome 	if (di == 0)
2033becaac39SToomas Soome 		return (0);
2034becaac39SToomas Soome 
2035becaac39SToomas Soome 	dp = gfx_state.tg_fb.fb_width *
2036becaac39SToomas Soome 	    gfx_state.tg_fb.fb_width +
2037becaac39SToomas Soome 	    gfx_state.tg_fb.fb_height *
2038becaac39SToomas Soome 	    gfx_state.tg_fb.fb_height;
2039becaac39SToomas Soome 
2040becaac39SToomas Soome 	return (isqrt(dp / di));
2041becaac39SToomas Soome }
2042becaac39SToomas Soome 
2043becaac39SToomas Soome /*
2044becaac39SToomas Soome  * Calculate font size from density independent pixels (dp):
2045becaac39SToomas Soome  * ((16dp * ppi) / 160) * display_factor.
2046becaac39SToomas Soome  * Here we are using fixed constants: 1dp == 160 ppi and
2047becaac39SToomas Soome  * display_factor 2.
2048becaac39SToomas Soome  *
2049becaac39SToomas Soome  * We are rounding font size up and are searching for font which is
2050becaac39SToomas Soome  * not smaller than calculated size value.
2051becaac39SToomas Soome  */
2052becaac39SToomas Soome static vt_font_bitmap_data_t *
gfx_get_font(void)2053becaac39SToomas Soome gfx_get_font(void)
2054becaac39SToomas Soome {
2055becaac39SToomas Soome 	unsigned ppi, size;
2056becaac39SToomas Soome 	vt_font_bitmap_data_t *font = NULL;
2057becaac39SToomas Soome 	struct fontlist *fl, *next;
2058becaac39SToomas Soome 
2059becaac39SToomas Soome 	/* Text mode is not supported here. */
2060becaac39SToomas Soome 	if (gfx_state.tg_fb_type == FB_TEXT)
2061becaac39SToomas Soome 		return (NULL);
2062becaac39SToomas Soome 
2063becaac39SToomas Soome 	ppi = gfx_get_ppi();
2064becaac39SToomas Soome 	if (ppi == 0)
2065becaac39SToomas Soome 		return (NULL);
2066becaac39SToomas Soome 
2067becaac39SToomas Soome 	/*
2068becaac39SToomas Soome 	 * We will search for 16dp font.
2069becaac39SToomas Soome 	 * We are using scale up by 10 for roundup.
2070becaac39SToomas Soome 	 */
2071becaac39SToomas Soome 	size = (16 * ppi * 10) / 160;
2072becaac39SToomas Soome 	/* Apply display factor 2.  */
2073becaac39SToomas Soome 	size = roundup(size * 2, 10) / 10;
2074becaac39SToomas Soome 
2075becaac39SToomas Soome 	STAILQ_FOREACH(fl, &fonts, font_next) {
2076becaac39SToomas Soome 		next = STAILQ_NEXT(fl, font_next);
2077becaac39SToomas Soome 
2078becaac39SToomas Soome 		/*
2079becaac39SToomas Soome 		 * If this is last font or, if next font is smaller,
2080becaac39SToomas Soome 		 * we have our font. Make sure, it actually is loaded.
2081becaac39SToomas Soome 		 */
2082becaac39SToomas Soome 		if (next == NULL || next->font_data->vfbd_height < size) {
2083becaac39SToomas Soome 			font = fl->font_data;
2084becaac39SToomas Soome 			if (font->vfbd_font == NULL ||
2085becaac39SToomas Soome 			    fl->font_flags == FONT_RELOAD) {
2086becaac39SToomas Soome 				if (fl->font_load != NULL &&
2087becaac39SToomas Soome 				    fl->font_name != NULL)
2088becaac39SToomas Soome 					font = fl->font_load(fl->font_name);
2089becaac39SToomas Soome 			}
2090becaac39SToomas Soome 			break;
2091becaac39SToomas Soome 		}
2092becaac39SToomas Soome 	}
2093becaac39SToomas Soome 
2094becaac39SToomas Soome 	return (font);
2095becaac39SToomas Soome }
2096becaac39SToomas Soome 
20973630506bSToomas Soome static vt_font_bitmap_data_t *
set_font(teken_unit_t * rows,teken_unit_t * cols,teken_unit_t h,teken_unit_t w)20983630506bSToomas Soome set_font(teken_unit_t *rows, teken_unit_t *cols, teken_unit_t h, teken_unit_t w)
20993630506bSToomas Soome {
21003630506bSToomas Soome 	vt_font_bitmap_data_t *font = NULL;
21013630506bSToomas Soome 	struct fontlist *fl;
21023630506bSToomas Soome 	unsigned height = h;
21033630506bSToomas Soome 	unsigned width = w;
21043630506bSToomas Soome 
21053630506bSToomas Soome 	/*
21063630506bSToomas Soome 	 * First check for manually loaded font.
21073630506bSToomas Soome 	 */
21083630506bSToomas Soome 	STAILQ_FOREACH(fl, &fonts, font_next) {
21093630506bSToomas Soome 		if (fl->font_flags == FONT_MANUAL) {
21103630506bSToomas Soome 			font = fl->font_data;
21113630506bSToomas Soome 			if (font->vfbd_font == NULL && fl->font_load != NULL &&
21123630506bSToomas Soome 			    fl->font_name != NULL) {
21133630506bSToomas Soome 				font = fl->font_load(fl->font_name);
21143630506bSToomas Soome 			}
21153630506bSToomas Soome 			if (font == NULL || font->vfbd_font == NULL)
21163630506bSToomas Soome 				font = NULL;
21173630506bSToomas Soome 			break;
21183630506bSToomas Soome 		}
21193630506bSToomas Soome 	}
21203630506bSToomas Soome 
2121becaac39SToomas Soome 	if (font == NULL)
2122becaac39SToomas Soome 		font = gfx_get_font();
2123becaac39SToomas Soome 
21243630506bSToomas Soome 	if (font != NULL) {
212596bef205SToomas Soome 		*rows = height / font->vfbd_height;
212696bef205SToomas Soome 		*cols = width / font->vfbd_width;
21273630506bSToomas Soome 		return (font);
21283630506bSToomas Soome 	}
21293630506bSToomas Soome 
21303630506bSToomas Soome 	/*
2131a26f7358SToomas Soome 	 * Find best font for these dimensions, or use default.
2132a26f7358SToomas Soome 	 * If height >= VT_FB_MAX_HEIGHT and width >= VT_FB_MAX_WIDTH,
2133a26f7358SToomas Soome 	 * do not use smaller font than our DEFAULT_FONT_DATA.
21343630506bSToomas Soome 	 */
21353630506bSToomas Soome 	STAILQ_FOREACH(fl, &fonts, font_next) {
21363630506bSToomas Soome 		font = fl->font_data;
2137a26f7358SToomas Soome 		if ((*rows * font->vfbd_height <= height &&
2138a26f7358SToomas Soome 		    *cols * font->vfbd_width <= width) ||
2139a26f7358SToomas Soome 		    (height >= VT_FB_MAX_HEIGHT &&
2140a26f7358SToomas Soome 		    width >= VT_FB_MAX_WIDTH &&
2141a26f7358SToomas Soome 		    font->vfbd_height == DEFAULT_FONT_DATA.vfbd_height &&
2142a26f7358SToomas Soome 		    font->vfbd_width == DEFAULT_FONT_DATA.vfbd_width)) {
21433630506bSToomas Soome 			if (font->vfbd_font == NULL ||
21443630506bSToomas Soome 			    fl->font_flags == FONT_RELOAD) {
21453630506bSToomas Soome 				if (fl->font_load != NULL &&
21463630506bSToomas Soome 				    fl->font_name != NULL) {
21473630506bSToomas Soome 					font = fl->font_load(fl->font_name);
21483630506bSToomas Soome 				}
21493630506bSToomas Soome 				if (font == NULL)
21503630506bSToomas Soome 					continue;
21513630506bSToomas Soome 			}
215296bef205SToomas Soome 			*rows = height / font->vfbd_height;
215396bef205SToomas Soome 			*cols = width / font->vfbd_width;
21543630506bSToomas Soome 			break;
21553630506bSToomas Soome 		}
21563630506bSToomas Soome 		font = NULL;
21573630506bSToomas Soome 	}
21583630506bSToomas Soome 
21593630506bSToomas Soome 	if (font == NULL) {
21603630506bSToomas Soome 		/*
21613630506bSToomas Soome 		 * We have fonts sorted smallest last, try it before
21623630506bSToomas Soome 		 * falling back to builtin.
21633630506bSToomas Soome 		 */
21643630506bSToomas Soome 		fl = STAILQ_LAST(&fonts, fontlist, font_next);
21653630506bSToomas Soome 		if (fl != NULL && fl->font_load != NULL &&
21663630506bSToomas Soome 		    fl->font_name != NULL) {
21673630506bSToomas Soome 			font = fl->font_load(fl->font_name);
21683630506bSToomas Soome 		}
21693630506bSToomas Soome 		if (font == NULL)
21703630506bSToomas Soome 			font = &DEFAULT_FONT_DATA;
21713630506bSToomas Soome 
217296bef205SToomas Soome 		*rows = height / font->vfbd_height;
217396bef205SToomas Soome 		*cols = width / font->vfbd_width;
21743630506bSToomas Soome 	}
21753630506bSToomas Soome 
21763630506bSToomas Soome 	return (font);
21773630506bSToomas Soome }
21783630506bSToomas Soome 
21793630506bSToomas Soome static void
cons_clear(void)21803630506bSToomas Soome cons_clear(void)
21813630506bSToomas Soome {
21823630506bSToomas Soome 	char clear[] = { '\033', 'c' };
21833630506bSToomas Soome 
21843630506bSToomas Soome 	/* Reset terminal */
21853630506bSToomas Soome 	teken_input(&gfx_state.tg_teken, clear, sizeof(clear));
21863630506bSToomas Soome 	gfx_state.tg_functions->tf_param(&gfx_state, TP_SHOWCURSOR, 0);
21873630506bSToomas Soome }
21883630506bSToomas Soome 
21893630506bSToomas Soome void
setup_font(teken_gfx_t * state,teken_unit_t height,teken_unit_t width)21903630506bSToomas Soome setup_font(teken_gfx_t *state, teken_unit_t height, teken_unit_t width)
21913630506bSToomas Soome {
21923630506bSToomas Soome 	vt_font_bitmap_data_t *font_data;
21933630506bSToomas Soome 	teken_pos_t *tp = &state->tg_tp;
21943630506bSToomas Soome 	char env[8];
21953630506bSToomas Soome 	int i;
21963630506bSToomas Soome 
21973630506bSToomas Soome 	/*
21983630506bSToomas Soome 	 * set_font() will select a appropriate sized font for
21993630506bSToomas Soome 	 * the number of rows and columns selected.  If we don't
22003630506bSToomas Soome 	 * have a font that will fit, then it will use the
22013630506bSToomas Soome 	 * default builtin font and adjust the rows and columns
22023630506bSToomas Soome 	 * to fit on the screen.
22033630506bSToomas Soome 	 */
22043630506bSToomas Soome 	font_data = set_font(&tp->tp_row, &tp->tp_col, height, width);
22053630506bSToomas Soome 
22063630506bSToomas Soome         if (font_data == NULL)
22073630506bSToomas Soome 		panic("out of memory");
22083630506bSToomas Soome 
22093630506bSToomas Soome 	for (i = 0; i < VFNT_MAPS; i++) {
22103630506bSToomas Soome 		state->tg_font.vf_map[i] =
22113630506bSToomas Soome 		    font_data->vfbd_font->vf_map[i];
22123630506bSToomas Soome 		state->tg_font.vf_map_count[i] =
22133630506bSToomas Soome 		    font_data->vfbd_font->vf_map_count[i];
22143630506bSToomas Soome 	}
22153630506bSToomas Soome 
22163630506bSToomas Soome 	state->tg_font.vf_bytes = font_data->vfbd_font->vf_bytes;
22173630506bSToomas Soome 	state->tg_font.vf_height = font_data->vfbd_font->vf_height;
22183630506bSToomas Soome 	state->tg_font.vf_width = font_data->vfbd_font->vf_width;
22193630506bSToomas Soome 
22203630506bSToomas Soome 	snprintf(env, sizeof (env), "%ux%u",
22213630506bSToomas Soome 	    state->tg_font.vf_width, state->tg_font.vf_height);
22223630506bSToomas Soome 	env_setenv("screen.font", EV_VOLATILE | EV_NOHOOK,
22233630506bSToomas Soome 	    env, font_set, env_nounset);
22243630506bSToomas Soome }
22253630506bSToomas Soome 
22263630506bSToomas Soome /* Binary search for the glyph. Return 0 if not found. */
22273630506bSToomas Soome static uint16_t
font_bisearch(const vfnt_map_t * map,uint32_t len,teken_char_t src)22283630506bSToomas Soome font_bisearch(const vfnt_map_t *map, uint32_t len, teken_char_t src)
22293630506bSToomas Soome {
22303630506bSToomas Soome 	unsigned min, mid, max;
22313630506bSToomas Soome 
22323630506bSToomas Soome 	min = 0;
22333630506bSToomas Soome 	max = len - 1;
22343630506bSToomas Soome 
22353630506bSToomas Soome 	/* Empty font map. */
22363630506bSToomas Soome 	if (len == 0)
22373630506bSToomas Soome 		return (0);
22383630506bSToomas Soome 	/* Character below minimal entry. */
22393630506bSToomas Soome 	if (src < map[0].vfm_src)
22403630506bSToomas Soome 		return (0);
22413630506bSToomas Soome 	/* Optimization: ASCII characters occur very often. */
22423630506bSToomas Soome 	if (src <= map[0].vfm_src + map[0].vfm_len)
22433630506bSToomas Soome 		return (src - map[0].vfm_src + map[0].vfm_dst);
22443630506bSToomas Soome 	/* Character above maximum entry. */
22453630506bSToomas Soome 	if (src > map[max].vfm_src + map[max].vfm_len)
22463630506bSToomas Soome 		return (0);
22473630506bSToomas Soome 
22483630506bSToomas Soome 	/* Binary search. */
22493630506bSToomas Soome 	while (max >= min) {
22503630506bSToomas Soome 		mid = (min + max) / 2;
22513630506bSToomas Soome 		if (src < map[mid].vfm_src)
22523630506bSToomas Soome 			max = mid - 1;
22533630506bSToomas Soome 		else if (src > map[mid].vfm_src + map[mid].vfm_len)
22543630506bSToomas Soome 			min = mid + 1;
22553630506bSToomas Soome 		else
22563630506bSToomas Soome 			return (src - map[mid].vfm_src + map[mid].vfm_dst);
22573630506bSToomas Soome 	}
22583630506bSToomas Soome 
22593630506bSToomas Soome 	return (0);
22603630506bSToomas Soome }
22613630506bSToomas Soome 
22623630506bSToomas Soome /*
22633630506bSToomas Soome  * Return glyph bitmap. If glyph is not found, we will return bitmap
22643630506bSToomas Soome  * for the first (offset 0) glyph.
22653630506bSToomas Soome  */
22663630506bSToomas Soome uint8_t *
font_lookup(const struct vt_font * vf,teken_char_t c,const teken_attr_t * a)22673630506bSToomas Soome font_lookup(const struct vt_font *vf, teken_char_t c, const teken_attr_t *a)
22683630506bSToomas Soome {
22693630506bSToomas Soome 	uint16_t dst;
22703630506bSToomas Soome 	size_t stride;
22713630506bSToomas Soome 
22723630506bSToomas Soome 	/* Substitute bold with normal if not found. */
22733630506bSToomas Soome 	if (a->ta_format & TF_BOLD) {
22743630506bSToomas Soome 		dst = font_bisearch(vf->vf_map[VFNT_MAP_BOLD],
22753630506bSToomas Soome 		    vf->vf_map_count[VFNT_MAP_BOLD], c);
22763630506bSToomas Soome 		if (dst != 0)
22773630506bSToomas Soome 			goto found;
22783630506bSToomas Soome 	}
22793630506bSToomas Soome 	dst = font_bisearch(vf->vf_map[VFNT_MAP_NORMAL],
22803630506bSToomas Soome 	    vf->vf_map_count[VFNT_MAP_NORMAL], c);
22813630506bSToomas Soome 
22823630506bSToomas Soome found:
22833630506bSToomas Soome 	stride = howmany(vf->vf_width, 8) * vf->vf_height;
22843630506bSToomas Soome 	return (&vf->vf_bytes[dst * stride]);
22853630506bSToomas Soome }
22863630506bSToomas Soome 
22873630506bSToomas Soome static int
load_mapping(int fd,struct vt_font * fp,int n)22883630506bSToomas Soome load_mapping(int fd, struct vt_font *fp, int n)
22893630506bSToomas Soome {
22903630506bSToomas Soome 	size_t i, size;
22913630506bSToomas Soome 	ssize_t rv;
22923630506bSToomas Soome 	vfnt_map_t *mp;
22933630506bSToomas Soome 
22943630506bSToomas Soome 	if (fp->vf_map_count[n] == 0)
22953630506bSToomas Soome 		return (0);
22963630506bSToomas Soome 
22973630506bSToomas Soome 	size = fp->vf_map_count[n] * sizeof(*mp);
22983630506bSToomas Soome 	mp = malloc(size);
22993630506bSToomas Soome 	if (mp == NULL)
23003630506bSToomas Soome 		return (ENOMEM);
23013630506bSToomas Soome 	fp->vf_map[n] = mp;
23023630506bSToomas Soome 
23033630506bSToomas Soome 	rv = read(fd, mp, size);
23043630506bSToomas Soome 	if (rv < 0 || (size_t)rv != size) {
23053630506bSToomas Soome 		free(fp->vf_map[n]);
23063630506bSToomas Soome 		fp->vf_map[n] = NULL;
23073630506bSToomas Soome 		return (EIO);
23083630506bSToomas Soome 	}
23093630506bSToomas Soome 
23103630506bSToomas Soome 	for (i = 0; i < fp->vf_map_count[n]; i++) {
23113630506bSToomas Soome 		mp[i].vfm_src = be32toh(mp[i].vfm_src);
23123630506bSToomas Soome 		mp[i].vfm_dst = be16toh(mp[i].vfm_dst);
23133630506bSToomas Soome 		mp[i].vfm_len = be16toh(mp[i].vfm_len);
23143630506bSToomas Soome 	}
23153630506bSToomas Soome 	return (0);
23163630506bSToomas Soome }
23173630506bSToomas Soome 
23183630506bSToomas Soome static int
builtin_mapping(struct vt_font * fp,int n)23193630506bSToomas Soome builtin_mapping(struct vt_font *fp, int n)
23203630506bSToomas Soome {
23213630506bSToomas Soome 	size_t size;
23223630506bSToomas Soome 	struct vfnt_map *mp;
23233630506bSToomas Soome 
23243630506bSToomas Soome 	if (n >= VFNT_MAPS)
23253630506bSToomas Soome 		return (EINVAL);
23263630506bSToomas Soome 
23273630506bSToomas Soome 	if (fp->vf_map_count[n] == 0)
23283630506bSToomas Soome 		return (0);
23293630506bSToomas Soome 
23303630506bSToomas Soome 	size = fp->vf_map_count[n] * sizeof(*mp);
23313630506bSToomas Soome 	mp = malloc(size);
23323630506bSToomas Soome 	if (mp == NULL)
23333630506bSToomas Soome 		return (ENOMEM);
23343630506bSToomas Soome 	fp->vf_map[n] = mp;
23353630506bSToomas Soome 
23363630506bSToomas Soome 	memcpy(mp, DEFAULT_FONT_DATA.vfbd_font->vf_map[n], size);
23373630506bSToomas Soome 	return (0);
23383630506bSToomas Soome }
23393630506bSToomas Soome 
23403630506bSToomas Soome /*
23413630506bSToomas Soome  * Load font from builtin or from file.
23423630506bSToomas Soome  * We do need special case for builtin because the builtin font glyphs
23433630506bSToomas Soome  * are compressed and we do need to uncompress them.
23443630506bSToomas Soome  * Having single load_font() for both cases will help us to simplify
23453630506bSToomas Soome  * font switch handling.
23463630506bSToomas Soome  */
23473630506bSToomas Soome static vt_font_bitmap_data_t *
load_font(char * path)23483630506bSToomas Soome load_font(char *path)
23493630506bSToomas Soome {
23503630506bSToomas Soome 	int fd, i;
23513630506bSToomas Soome 	uint32_t glyphs;
23523630506bSToomas Soome 	struct font_header fh;
23533630506bSToomas Soome 	struct fontlist *fl;
23543630506bSToomas Soome 	vt_font_bitmap_data_t *bp;
23553630506bSToomas Soome 	struct vt_font *fp;
23563630506bSToomas Soome 	size_t size;
23573630506bSToomas Soome 	ssize_t rv;
23583630506bSToomas Soome 
23593630506bSToomas Soome 	/* Get our entry from the font list. */
23603630506bSToomas Soome 	STAILQ_FOREACH(fl, &fonts, font_next) {
23613630506bSToomas Soome 		if (strcmp(fl->font_name, path) == 0)
23623630506bSToomas Soome 			break;
23633630506bSToomas Soome 	}
23643630506bSToomas Soome 	if (fl == NULL)
23653630506bSToomas Soome 		return (NULL);	/* Should not happen. */
23663630506bSToomas Soome 
23673630506bSToomas Soome 	bp = fl->font_data;
23683630506bSToomas Soome 	if (bp->vfbd_font != NULL && fl->font_flags != FONT_RELOAD)
23693630506bSToomas Soome 		return (bp);
23703630506bSToomas Soome 
23713630506bSToomas Soome 	fd = -1;
23723630506bSToomas Soome 	/*
23733630506bSToomas Soome 	 * Special case for builtin font.
23743630506bSToomas Soome 	 * Builtin font is the very first font we load, we do not have
23753630506bSToomas Soome 	 * previous loads to be released.
23763630506bSToomas Soome 	 */
23773630506bSToomas Soome 	if (fl->font_flags == FONT_BUILTIN) {
23783630506bSToomas Soome 		if ((fp = calloc(1, sizeof(struct vt_font))) == NULL)
23793630506bSToomas Soome 			return (NULL);
23803630506bSToomas Soome 
23813630506bSToomas Soome 		fp->vf_width = DEFAULT_FONT_DATA.vfbd_width;
23823630506bSToomas Soome 		fp->vf_height = DEFAULT_FONT_DATA.vfbd_height;
23833630506bSToomas Soome 
23843630506bSToomas Soome 		fp->vf_bytes = malloc(DEFAULT_FONT_DATA.vfbd_uncompressed_size);
23853630506bSToomas Soome 		if (fp->vf_bytes == NULL) {
23863630506bSToomas Soome 			free(fp);
23873630506bSToomas Soome 			return (NULL);
23883630506bSToomas Soome 		}
23893630506bSToomas Soome 
23903630506bSToomas Soome 		bp->vfbd_uncompressed_size =
23913630506bSToomas Soome 		    DEFAULT_FONT_DATA.vfbd_uncompressed_size;
23923630506bSToomas Soome 		bp->vfbd_compressed_size =
23933630506bSToomas Soome 		    DEFAULT_FONT_DATA.vfbd_compressed_size;
23943630506bSToomas Soome 
23953630506bSToomas Soome 		if (lz4_decompress(DEFAULT_FONT_DATA.vfbd_compressed_data,
23963630506bSToomas Soome 		    fp->vf_bytes,
23973630506bSToomas Soome 		    DEFAULT_FONT_DATA.vfbd_compressed_size,
23983630506bSToomas Soome 		    DEFAULT_FONT_DATA.vfbd_uncompressed_size, 0) != 0) {
23993630506bSToomas Soome 			free(fp->vf_bytes);
24003630506bSToomas Soome 			free(fp);
24013630506bSToomas Soome 			return (NULL);
24023630506bSToomas Soome 		}
24033630506bSToomas Soome 
24043630506bSToomas Soome 		for (i = 0; i < VFNT_MAPS; i++) {
24053630506bSToomas Soome 			fp->vf_map_count[i] =
24063630506bSToomas Soome 			    DEFAULT_FONT_DATA.vfbd_font->vf_map_count[i];
24073630506bSToomas Soome 			if (builtin_mapping(fp, i) != 0)
24083630506bSToomas Soome 				goto free_done;
24093630506bSToomas Soome 		}
24103630506bSToomas Soome 
24113630506bSToomas Soome 		bp->vfbd_font = fp;
24123630506bSToomas Soome 		return (bp);
24133630506bSToomas Soome 	}
24143630506bSToomas Soome 
24153630506bSToomas Soome 	fd = open(path, O_RDONLY);
24163630506bSToomas Soome 	if (fd < 0)
24173630506bSToomas Soome 		return (NULL);
24183630506bSToomas Soome 
24193630506bSToomas Soome 	size = sizeof(fh);
24203630506bSToomas Soome 	rv = read(fd, &fh, size);
24213630506bSToomas Soome 	if (rv < 0 || (size_t)rv != size) {
24223630506bSToomas Soome 		bp = NULL;
24233630506bSToomas Soome 		goto done;
24243630506bSToomas Soome 	}
24253630506bSToomas Soome 	if (memcmp(fh.fh_magic, FONT_HEADER_MAGIC, sizeof(fh.fh_magic)) != 0) {
24263630506bSToomas Soome 		bp = NULL;
24273630506bSToomas Soome 		goto done;
24283630506bSToomas Soome 	}
24293630506bSToomas Soome 	if ((fp = calloc(1, sizeof(struct vt_font))) == NULL) {
24303630506bSToomas Soome 		bp = NULL;
24313630506bSToomas Soome 		goto done;
24323630506bSToomas Soome 	}
24333630506bSToomas Soome 	for (i = 0; i < VFNT_MAPS; i++)
24343630506bSToomas Soome 		fp->vf_map_count[i] = be32toh(fh.fh_map_count[i]);
24353630506bSToomas Soome 
24363630506bSToomas Soome 	glyphs = be32toh(fh.fh_glyph_count);
24373630506bSToomas Soome 	fp->vf_width = fh.fh_width;
24383630506bSToomas Soome 	fp->vf_height = fh.fh_height;
24393630506bSToomas Soome 
24403630506bSToomas Soome 	size = howmany(fp->vf_width, 8) * fp->vf_height * glyphs;
24413630506bSToomas Soome 	bp->vfbd_uncompressed_size = size;
24423630506bSToomas Soome 	if ((fp->vf_bytes = malloc(size)) == NULL)
24433630506bSToomas Soome 		goto free_done;
24443630506bSToomas Soome 
24453630506bSToomas Soome 	rv = read(fd, fp->vf_bytes, size);
24463630506bSToomas Soome 	if (rv < 0 || (size_t)rv != size)
24473630506bSToomas Soome 		goto free_done;
24483630506bSToomas Soome 	for (i = 0; i < VFNT_MAPS; i++) {
24493630506bSToomas Soome 		if (load_mapping(fd, fp, i) != 0)
24503630506bSToomas Soome 			goto free_done;
24513630506bSToomas Soome 	}
24523630506bSToomas Soome 
24533630506bSToomas Soome 	/*
24543630506bSToomas Soome 	 * Reset builtin flag now as we have full font loaded.
24553630506bSToomas Soome 	 */
24563630506bSToomas Soome 	if (fl->font_flags == FONT_BUILTIN)
24573630506bSToomas Soome 		fl->font_flags = FONT_AUTO;
24583630506bSToomas Soome 
24593630506bSToomas Soome 	/*
24603630506bSToomas Soome 	 * Release previously loaded entries. We can do this now, as
24613630506bSToomas Soome 	 * the new font is loaded. Note, there can be no console
24623630506bSToomas Soome 	 * output till the new font is in place and teken is notified.
24633630506bSToomas Soome 	 * We do need to keep fl->font_data for glyph dimensions.
24643630506bSToomas Soome 	 */
24653630506bSToomas Soome 	STAILQ_FOREACH(fl, &fonts, font_next) {
24663630506bSToomas Soome 		if (fl->font_data->vfbd_font == NULL)
24673630506bSToomas Soome 			continue;
24683630506bSToomas Soome 
24693630506bSToomas Soome 		for (i = 0; i < VFNT_MAPS; i++)
24703630506bSToomas Soome 			free(fl->font_data->vfbd_font->vf_map[i]);
24713630506bSToomas Soome 		free(fl->font_data->vfbd_font->vf_bytes);
24723630506bSToomas Soome 		free(fl->font_data->vfbd_font);
24733630506bSToomas Soome 		fl->font_data->vfbd_font = NULL;
24743630506bSToomas Soome 	}
24753630506bSToomas Soome 
24763630506bSToomas Soome 	bp->vfbd_font = fp;
24773630506bSToomas Soome 	bp->vfbd_compressed_size = 0;
24783630506bSToomas Soome 
24793630506bSToomas Soome done:
24803630506bSToomas Soome 	if (fd != -1)
24813630506bSToomas Soome 		close(fd);
24823630506bSToomas Soome 	return (bp);
24833630506bSToomas Soome 
24843630506bSToomas Soome free_done:
24853630506bSToomas Soome 	for (i = 0; i < VFNT_MAPS; i++)
24863630506bSToomas Soome 		free(fp->vf_map[i]);
24873630506bSToomas Soome 	free(fp->vf_bytes);
24883630506bSToomas Soome 	free(fp);
24893630506bSToomas Soome 	bp = NULL;
24903630506bSToomas Soome 	goto done;
24913630506bSToomas Soome }
24923630506bSToomas Soome 
24933630506bSToomas Soome struct name_entry {
24943630506bSToomas Soome 	char			*n_name;
24953630506bSToomas Soome 	SLIST_ENTRY(name_entry)	n_entry;
24963630506bSToomas Soome };
24973630506bSToomas Soome 
24983630506bSToomas Soome SLIST_HEAD(name_list, name_entry);
24993630506bSToomas Soome 
25003630506bSToomas Soome /* Read font names from index file. */
25013630506bSToomas Soome static struct name_list *
read_list(char * fonts)25023630506bSToomas Soome read_list(char *fonts)
25033630506bSToomas Soome {
25043630506bSToomas Soome 	struct name_list *nl;
25053630506bSToomas Soome 	struct name_entry *np;
25063630506bSToomas Soome 	char *dir, *ptr;
25073630506bSToomas Soome 	char buf[PATH_MAX];
25083630506bSToomas Soome 	int fd, len;
25093630506bSToomas Soome 
2510313724baSColin Percival 	TSENTER();
2511313724baSColin Percival 
25123630506bSToomas Soome 	dir = strdup(fonts);
25133630506bSToomas Soome 	if (dir == NULL)
25143630506bSToomas Soome 		return (NULL);
25153630506bSToomas Soome 
25163630506bSToomas Soome 	ptr = strrchr(dir, '/');
25173630506bSToomas Soome 	*ptr = '\0';
25183630506bSToomas Soome 
25193630506bSToomas Soome 	fd = open(fonts, O_RDONLY);
25203630506bSToomas Soome 	if (fd < 0)
25213630506bSToomas Soome 		return (NULL);
25223630506bSToomas Soome 
25233630506bSToomas Soome 	nl = malloc(sizeof(*nl));
25243630506bSToomas Soome 	if (nl == NULL) {
25253630506bSToomas Soome 		close(fd);
25263630506bSToomas Soome 		return (nl);
25273630506bSToomas Soome 	}
25283630506bSToomas Soome 
25293630506bSToomas Soome 	SLIST_INIT(nl);
25303630506bSToomas Soome 	while ((len = fgetstr(buf, sizeof (buf), fd)) >= 0) {
25313630506bSToomas Soome 		if (*buf == '#' || *buf == '\0')
25323630506bSToomas Soome 			continue;
25333630506bSToomas Soome 
25343630506bSToomas Soome 		if (bcmp(buf, "MENU", 4) == 0)
25353630506bSToomas Soome 			continue;
25363630506bSToomas Soome 
25373630506bSToomas Soome 		if (bcmp(buf, "FONT", 4) == 0)
25383630506bSToomas Soome 			continue;
25393630506bSToomas Soome 
25403630506bSToomas Soome 		ptr = strchr(buf, ':');
25413630506bSToomas Soome 		if (ptr == NULL)
25423630506bSToomas Soome 			continue;
25433630506bSToomas Soome 		else
25443630506bSToomas Soome 			*ptr = '\0';
25453630506bSToomas Soome 
25463630506bSToomas Soome 		np = malloc(sizeof(*np));
25473630506bSToomas Soome 		if (np == NULL) {
25483630506bSToomas Soome 			close(fd);
25493630506bSToomas Soome 			return (nl);	/* return what we have */
25503630506bSToomas Soome 		}
25513630506bSToomas Soome 		if (asprintf(&np->n_name, "%s/%s", dir, buf) < 0) {
25523630506bSToomas Soome 			free(np);
25533630506bSToomas Soome 			close(fd);
25543630506bSToomas Soome 			return (nl);    /* return what we have */
25553630506bSToomas Soome 		}
25563630506bSToomas Soome 		SLIST_INSERT_HEAD(nl, np, n_entry);
25573630506bSToomas Soome 	}
25583630506bSToomas Soome 	close(fd);
2559313724baSColin Percival 	TSEXIT();
25603630506bSToomas Soome 	return (nl);
25613630506bSToomas Soome }
25623630506bSToomas Soome 
25633630506bSToomas Soome /*
25643630506bSToomas Soome  * Read the font properties and insert new entry into the list.
25653630506bSToomas Soome  * The font list is built in descending order.
25663630506bSToomas Soome  */
25673630506bSToomas Soome static bool
insert_font(char * name,FONT_FLAGS flags)25683630506bSToomas Soome insert_font(char *name, FONT_FLAGS flags)
25693630506bSToomas Soome {
25703630506bSToomas Soome 	struct font_header fh;
25713630506bSToomas Soome 	struct fontlist *fp, *previous, *entry, *next;
25723630506bSToomas Soome 	size_t size;
25733630506bSToomas Soome 	ssize_t rv;
25743630506bSToomas Soome 	int fd;
25753630506bSToomas Soome 	char *font_name;
25763630506bSToomas Soome 
2577313724baSColin Percival 	TSENTER();
2578313724baSColin Percival 
25793630506bSToomas Soome 	font_name = NULL;
25803630506bSToomas Soome 	if (flags == FONT_BUILTIN) {
25813630506bSToomas Soome 		/*
25823630506bSToomas Soome 		 * We only install builtin font once, while setting up
25833630506bSToomas Soome 		 * initial console. Since this will happen very early,
25843630506bSToomas Soome 		 * we assume asprintf will not fail. Once we have access to
25853630506bSToomas Soome 		 * files, the builtin font will be replaced by font loaded
25863630506bSToomas Soome 		 * from file.
25873630506bSToomas Soome 		 */
25883630506bSToomas Soome 		if (!STAILQ_EMPTY(&fonts))
25893630506bSToomas Soome 			return (false);
25903630506bSToomas Soome 
25913630506bSToomas Soome 		fh.fh_width = DEFAULT_FONT_DATA.vfbd_width;
25923630506bSToomas Soome 		fh.fh_height = DEFAULT_FONT_DATA.vfbd_height;
25933630506bSToomas Soome 
25943630506bSToomas Soome 		(void) asprintf(&font_name, "%dx%d",
25953630506bSToomas Soome 		    DEFAULT_FONT_DATA.vfbd_width,
25963630506bSToomas Soome 		    DEFAULT_FONT_DATA.vfbd_height);
25973630506bSToomas Soome 	} else {
25983630506bSToomas Soome 		fd = open(name, O_RDONLY);
25993630506bSToomas Soome 		if (fd < 0)
26003630506bSToomas Soome 			return (false);
26013630506bSToomas Soome 		rv = read(fd, &fh, sizeof(fh));
26023630506bSToomas Soome 		close(fd);
26033630506bSToomas Soome 		if (rv < 0 || (size_t)rv != sizeof(fh))
26043630506bSToomas Soome 			return (false);
26053630506bSToomas Soome 
26063630506bSToomas Soome 		if (memcmp(fh.fh_magic, FONT_HEADER_MAGIC,
26073630506bSToomas Soome 		    sizeof(fh.fh_magic)) != 0)
26083630506bSToomas Soome 			return (false);
26093630506bSToomas Soome 		font_name = strdup(name);
26103630506bSToomas Soome 	}
26113630506bSToomas Soome 
26123630506bSToomas Soome 	if (font_name == NULL)
26133630506bSToomas Soome 		return (false);
26143630506bSToomas Soome 
26153630506bSToomas Soome 	/*
26163630506bSToomas Soome 	 * If we have an entry with the same glyph dimensions, replace
26173630506bSToomas Soome 	 * the file name and mark us. We only support unique dimensions.
26183630506bSToomas Soome 	 */
26193630506bSToomas Soome 	STAILQ_FOREACH(entry, &fonts, font_next) {
26203630506bSToomas Soome 		if (fh.fh_width == entry->font_data->vfbd_width &&
26213630506bSToomas Soome 		    fh.fh_height == entry->font_data->vfbd_height) {
26223630506bSToomas Soome 			free(entry->font_name);
26233630506bSToomas Soome 			entry->font_name = font_name;
26243630506bSToomas Soome 			entry->font_flags = FONT_RELOAD;
2625313724baSColin Percival 			TSEXIT();
26263630506bSToomas Soome 			return (true);
26273630506bSToomas Soome 		}
26283630506bSToomas Soome 	}
26293630506bSToomas Soome 
26303630506bSToomas Soome 	fp = calloc(sizeof(*fp), 1);
26313630506bSToomas Soome 	if (fp == NULL) {
26323630506bSToomas Soome 		free(font_name);
26333630506bSToomas Soome 		return (false);
26343630506bSToomas Soome 	}
26353630506bSToomas Soome 	fp->font_data = calloc(sizeof(*fp->font_data), 1);
26363630506bSToomas Soome 	if (fp->font_data == NULL) {
26373630506bSToomas Soome 		free(font_name);
26383630506bSToomas Soome 		free(fp);
26393630506bSToomas Soome 		return (false);
26403630506bSToomas Soome 	}
26413630506bSToomas Soome 	fp->font_name = font_name;
26423630506bSToomas Soome 	fp->font_flags = flags;
26433630506bSToomas Soome 	fp->font_load = load_font;
26443630506bSToomas Soome 	fp->font_data->vfbd_width = fh.fh_width;
26453630506bSToomas Soome 	fp->font_data->vfbd_height = fh.fh_height;
26463630506bSToomas Soome 
26473630506bSToomas Soome 	if (STAILQ_EMPTY(&fonts)) {
26483630506bSToomas Soome 		STAILQ_INSERT_HEAD(&fonts, fp, font_next);
2649313724baSColin Percival 		TSEXIT();
26503630506bSToomas Soome 		return (true);
26513630506bSToomas Soome 	}
26523630506bSToomas Soome 
26533630506bSToomas Soome 	previous = NULL;
26543630506bSToomas Soome 	size = fp->font_data->vfbd_width * fp->font_data->vfbd_height;
26553630506bSToomas Soome 
26563630506bSToomas Soome 	STAILQ_FOREACH(entry, &fonts, font_next) {
26573630506bSToomas Soome 		vt_font_bitmap_data_t *bd;
26583630506bSToomas Soome 
26593630506bSToomas Soome 		bd = entry->font_data;
26603630506bSToomas Soome 		/* Should fp be inserted before the entry? */
26613630506bSToomas Soome 		if (size > bd->vfbd_width * bd->vfbd_height) {
26623630506bSToomas Soome 			if (previous == NULL) {
26633630506bSToomas Soome 				STAILQ_INSERT_HEAD(&fonts, fp, font_next);
26643630506bSToomas Soome 			} else {
26653630506bSToomas Soome 				STAILQ_INSERT_AFTER(&fonts, previous, fp,
26663630506bSToomas Soome 				    font_next);
26673630506bSToomas Soome 			}
2668313724baSColin Percival 			TSEXIT();
26693630506bSToomas Soome 			return (true);
26703630506bSToomas Soome 		}
26713630506bSToomas Soome 		next = STAILQ_NEXT(entry, font_next);
26723630506bSToomas Soome 		if (next == NULL ||
26733630506bSToomas Soome 		    size > next->font_data->vfbd_width *
26743630506bSToomas Soome 		    next->font_data->vfbd_height) {
26753630506bSToomas Soome 			STAILQ_INSERT_AFTER(&fonts, entry, fp, font_next);
2676313724baSColin Percival 			TSEXIT();
26773630506bSToomas Soome 			return (true);
26783630506bSToomas Soome 		}
26793630506bSToomas Soome 		previous = entry;
26803630506bSToomas Soome 	}
2681313724baSColin Percival 	TSEXIT();
26823630506bSToomas Soome 	return (true);
26833630506bSToomas Soome }
26843630506bSToomas Soome 
26853630506bSToomas Soome static int
font_set(struct env_var * ev __unused,int flags __unused,const void * value)26863630506bSToomas Soome font_set(struct env_var *ev __unused, int flags __unused, const void *value)
26873630506bSToomas Soome {
26883630506bSToomas Soome 	struct fontlist *fl;
26893630506bSToomas Soome 	char *eptr;
26903630506bSToomas Soome 	unsigned long x = 0, y = 0;
26913630506bSToomas Soome 
26923630506bSToomas Soome 	/*
26933630506bSToomas Soome 	 * Attempt to extract values from "XxY" string. In case of error,
26943630506bSToomas Soome 	 * we have unmaching glyph dimensions and will just output the
26953630506bSToomas Soome 	 * available values.
26963630506bSToomas Soome 	 */
26973630506bSToomas Soome 	if (value != NULL) {
26983630506bSToomas Soome 		x = strtoul(value, &eptr, 10);
26993630506bSToomas Soome 		if (*eptr == 'x')
27003630506bSToomas Soome 			y = strtoul(eptr + 1, &eptr, 10);
27013630506bSToomas Soome 	}
27023630506bSToomas Soome 	STAILQ_FOREACH(fl, &fonts, font_next) {
27033630506bSToomas Soome 		if (fl->font_data->vfbd_width == x &&
27043630506bSToomas Soome 		    fl->font_data->vfbd_height == y)
27053630506bSToomas Soome 			break;
27063630506bSToomas Soome 	}
27073630506bSToomas Soome 	if (fl != NULL) {
27083630506bSToomas Soome 		/* Reset any FONT_MANUAL flag. */
27093630506bSToomas Soome 		reset_font_flags();
27103630506bSToomas Soome 
27113630506bSToomas Soome 		/* Mark this font manually loaded */
27123630506bSToomas Soome 		fl->font_flags = FONT_MANUAL;
27133630506bSToomas Soome 		cons_update_mode(gfx_state.tg_fb_type != FB_TEXT);
27143630506bSToomas Soome 		return (CMD_OK);
27153630506bSToomas Soome 	}
27163630506bSToomas Soome 
27173630506bSToomas Soome 	printf("Available fonts:\n");
27183630506bSToomas Soome 	STAILQ_FOREACH(fl, &fonts, font_next) {
27193630506bSToomas Soome 		printf("    %dx%d\n", fl->font_data->vfbd_width,
27203630506bSToomas Soome 		    fl->font_data->vfbd_height);
27213630506bSToomas Soome 	}
27223630506bSToomas Soome 	return (CMD_OK);
27233630506bSToomas Soome }
27243630506bSToomas Soome 
27253630506bSToomas Soome void
bios_text_font(bool use_vga_font)27263630506bSToomas Soome bios_text_font(bool use_vga_font)
27273630506bSToomas Soome {
27283630506bSToomas Soome 	if (use_vga_font)
27293630506bSToomas Soome 		(void) insert_font(VGA_8X16_FONT, FONT_MANUAL);
27303630506bSToomas Soome 	else
27313630506bSToomas Soome 		(void) insert_font(DEFAULT_8X16_FONT, FONT_MANUAL);
27323630506bSToomas Soome }
27333630506bSToomas Soome 
27343630506bSToomas Soome void
autoload_font(bool bios)27353630506bSToomas Soome autoload_font(bool bios)
27363630506bSToomas Soome {
27373630506bSToomas Soome 	struct name_list *nl;
27383630506bSToomas Soome 	struct name_entry *np;
27393630506bSToomas Soome 
2740313724baSColin Percival 	TSENTER();
2741313724baSColin Percival 
27423630506bSToomas Soome 	nl = read_list("/boot/fonts/INDEX.fonts");
27433630506bSToomas Soome 	if (nl == NULL)
27443630506bSToomas Soome 		return;
27453630506bSToomas Soome 
27463630506bSToomas Soome 	while (!SLIST_EMPTY(nl)) {
27473630506bSToomas Soome 		np = SLIST_FIRST(nl);
27483630506bSToomas Soome 		SLIST_REMOVE_HEAD(nl, n_entry);
27493630506bSToomas Soome 		if (insert_font(np->n_name, FONT_AUTO) == false)
27503630506bSToomas Soome 			printf("failed to add font: %s\n", np->n_name);
27513630506bSToomas Soome 		free(np->n_name);
27523630506bSToomas Soome 		free(np);
27533630506bSToomas Soome 	}
27543630506bSToomas Soome 
27553630506bSToomas Soome 	/*
27563630506bSToomas Soome 	 * If vga text mode was requested, load vga.font (8x16 bold) font.
27573630506bSToomas Soome 	 */
27583630506bSToomas Soome 	if (bios) {
27593630506bSToomas Soome 		bios_text_font(true);
27603630506bSToomas Soome 	}
27613630506bSToomas Soome 
27623630506bSToomas Soome 	(void) cons_update_mode(gfx_state.tg_fb_type != FB_TEXT);
2763313724baSColin Percival 
2764313724baSColin Percival 	TSEXIT();
27653630506bSToomas Soome }
27663630506bSToomas Soome 
27673630506bSToomas Soome COMMAND_SET(load_font, "loadfont", "load console font from file", command_font);
27683630506bSToomas Soome 
27693630506bSToomas Soome static int
command_font(int argc,char * argv[])27703630506bSToomas Soome command_font(int argc, char *argv[])
27713630506bSToomas Soome {
27723630506bSToomas Soome 	int i, c, rc;
27733630506bSToomas Soome 	struct fontlist *fl;
27743630506bSToomas Soome 	vt_font_bitmap_data_t *bd;
27753630506bSToomas Soome 	bool list;
27763630506bSToomas Soome 
27773630506bSToomas Soome 	list = false;
27783630506bSToomas Soome 	optind = 1;
27793630506bSToomas Soome 	optreset = 1;
27803630506bSToomas Soome 	rc = CMD_OK;
27813630506bSToomas Soome 
27823630506bSToomas Soome 	while ((c = getopt(argc, argv, "l")) != -1) {
27833630506bSToomas Soome 		switch (c) {
27843630506bSToomas Soome 		case 'l':
27853630506bSToomas Soome 			list = true;
27863630506bSToomas Soome 			break;
27873630506bSToomas Soome 		case '?':
27883630506bSToomas Soome 		default:
27893630506bSToomas Soome 			return (CMD_ERROR);
27903630506bSToomas Soome 		}
27913630506bSToomas Soome 	}
27923630506bSToomas Soome 
27933630506bSToomas Soome 	argc -= optind;
27943630506bSToomas Soome 	argv += optind;
27953630506bSToomas Soome 
27963630506bSToomas Soome 	if (argc > 1 || (list && argc != 0)) {
27973630506bSToomas Soome 		printf("Usage: loadfont [-l] | [file.fnt]\n");
27983630506bSToomas Soome 		return (CMD_ERROR);
27993630506bSToomas Soome 	}
28003630506bSToomas Soome 
28013630506bSToomas Soome 	if (list) {
28023630506bSToomas Soome 		STAILQ_FOREACH(fl, &fonts, font_next) {
28033630506bSToomas Soome 			printf("font %s: %dx%d%s\n", fl->font_name,
28043630506bSToomas Soome 			    fl->font_data->vfbd_width,
28053630506bSToomas Soome 			    fl->font_data->vfbd_height,
28063630506bSToomas Soome 			    fl->font_data->vfbd_font == NULL? "" : " loaded");
28073630506bSToomas Soome 		}
28083630506bSToomas Soome 		return (CMD_OK);
28093630506bSToomas Soome 	}
28103630506bSToomas Soome 
28113630506bSToomas Soome 	/* Clear scren */
28123630506bSToomas Soome 	cons_clear();
28133630506bSToomas Soome 
28143630506bSToomas Soome 	if (argc == 1) {
28153630506bSToomas Soome 		char *name = argv[0];
28163630506bSToomas Soome 
28173630506bSToomas Soome 		if (insert_font(name, FONT_MANUAL) == false) {
28183630506bSToomas Soome 			printf("loadfont error: failed to load: %s\n", name);
28193630506bSToomas Soome 			return (CMD_ERROR);
28203630506bSToomas Soome 		}
28213630506bSToomas Soome 
28223630506bSToomas Soome 		(void) cons_update_mode(gfx_state.tg_fb_type != FB_TEXT);
28233630506bSToomas Soome 		return (CMD_OK);
28243630506bSToomas Soome 	}
28253630506bSToomas Soome 
28263630506bSToomas Soome 	if (argc == 0) {
28273630506bSToomas Soome 		/*
28283630506bSToomas Soome 		 * Walk entire font list, release any loaded font, and set
28293630506bSToomas Soome 		 * autoload flag. The font list does have at least the builtin
28303630506bSToomas Soome 		 * default font.
28313630506bSToomas Soome 		 */
28323630506bSToomas Soome 		STAILQ_FOREACH(fl, &fonts, font_next) {
28333630506bSToomas Soome 			if (fl->font_data->vfbd_font != NULL) {
28343630506bSToomas Soome 
28353630506bSToomas Soome 				bd = fl->font_data;
28363630506bSToomas Soome 				/*
28373630506bSToomas Soome 				 * Note the setup_font() is releasing
28383630506bSToomas Soome 				 * font bytes.
28393630506bSToomas Soome 				 */
28403630506bSToomas Soome 				for (i = 0; i < VFNT_MAPS; i++)
28413630506bSToomas Soome 					free(bd->vfbd_font->vf_map[i]);
28423630506bSToomas Soome 				free(fl->font_data->vfbd_font);
28433630506bSToomas Soome 				fl->font_data->vfbd_font = NULL;
28443630506bSToomas Soome 				fl->font_data->vfbd_uncompressed_size = 0;
28453630506bSToomas Soome 				fl->font_flags = FONT_AUTO;
28463630506bSToomas Soome 			}
28473630506bSToomas Soome 		}
28483630506bSToomas Soome 		(void) cons_update_mode(gfx_state.tg_fb_type != FB_TEXT);
28493630506bSToomas Soome 	}
28503630506bSToomas Soome 	return (rc);
28513630506bSToomas Soome }
28523630506bSToomas Soome 
28533630506bSToomas Soome bool
gfx_get_edid_resolution(struct vesa_edid_info * edid,edid_res_list_t * res)28543630506bSToomas Soome gfx_get_edid_resolution(struct vesa_edid_info *edid, edid_res_list_t *res)
28553630506bSToomas Soome {
28563630506bSToomas Soome 	struct resolution *rp, *p;
28573630506bSToomas Soome 
28583630506bSToomas Soome 	/*
28593630506bSToomas Soome 	 * Walk detailed timings tables (4).
28603630506bSToomas Soome 	 */
28613630506bSToomas Soome 	if ((edid->display.supported_features
28623630506bSToomas Soome 	    & EDID_FEATURE_PREFERRED_TIMING_MODE) != 0) {
28633630506bSToomas Soome 		/* Walk detailed timing descriptors (4) */
28643630506bSToomas Soome 		for (int i = 0; i < DET_TIMINGS; i++) {
28653630506bSToomas Soome 			/*
2866b5e0a701SGordon Bergling 			 * Reserved value 0 is not used for display descriptor.
28673630506bSToomas Soome 			 */
28683630506bSToomas Soome 			if (edid->detailed_timings[i].pixel_clock == 0)
28693630506bSToomas Soome 				continue;
28703630506bSToomas Soome 			if ((rp = malloc(sizeof(*rp))) == NULL)
28713630506bSToomas Soome 				continue;
28723630506bSToomas Soome 			rp->width = GET_EDID_INFO_WIDTH(edid, i);
28733630506bSToomas Soome 			rp->height = GET_EDID_INFO_HEIGHT(edid, i);
28743630506bSToomas Soome 			if (rp->width > 0 && rp->width <= EDID_MAX_PIXELS &&
28753630506bSToomas Soome 			    rp->height > 0 && rp->height <= EDID_MAX_LINES)
28763630506bSToomas Soome 				TAILQ_INSERT_TAIL(res, rp, next);
28773630506bSToomas Soome 			else
28783630506bSToomas Soome 				free(rp);
28793630506bSToomas Soome 		}
28803630506bSToomas Soome 	}
28813630506bSToomas Soome 
28823630506bSToomas Soome 	/*
28833630506bSToomas Soome 	 * Walk standard timings list (8).
28843630506bSToomas Soome 	 */
28853630506bSToomas Soome 	for (int i = 0; i < STD_TIMINGS; i++) {
28863630506bSToomas Soome 		/* Is this field unused? */
28873630506bSToomas Soome 		if (edid->standard_timings[i] == 0x0101)
28883630506bSToomas Soome 			continue;
28893630506bSToomas Soome 
28903630506bSToomas Soome 		if ((rp = malloc(sizeof(*rp))) == NULL)
28913630506bSToomas Soome 			continue;
28923630506bSToomas Soome 
28933630506bSToomas Soome 		rp->width = HSIZE(edid->standard_timings[i]);
28943630506bSToomas Soome 		switch (RATIO(edid->standard_timings[i])) {
28953630506bSToomas Soome 		case RATIO1_1:
28963630506bSToomas Soome 			rp->height = HSIZE(edid->standard_timings[i]);
28973630506bSToomas Soome 			if (edid->header.version > 1 ||
28983630506bSToomas Soome 			    edid->header.revision > 2) {
28993630506bSToomas Soome 				rp->height = rp->height * 10 / 16;
29003630506bSToomas Soome 			}
29013630506bSToomas Soome 			break;
29023630506bSToomas Soome 		case RATIO4_3:
29033630506bSToomas Soome 			rp->height = HSIZE(edid->standard_timings[i]) * 3 / 4;
29043630506bSToomas Soome 			break;
29053630506bSToomas Soome 		case RATIO5_4:
29063630506bSToomas Soome 			rp->height = HSIZE(edid->standard_timings[i]) * 4 / 5;
29073630506bSToomas Soome 			break;
29083630506bSToomas Soome 		case RATIO16_9:
29093630506bSToomas Soome 			rp->height = HSIZE(edid->standard_timings[i]) * 9 / 16;
29103630506bSToomas Soome 			break;
29113630506bSToomas Soome 		}
29123630506bSToomas Soome 
29133630506bSToomas Soome 		/*
29143630506bSToomas Soome 		 * Create resolution list in decreasing order, except keep
29153630506bSToomas Soome 		 * first entry (preferred timing mode).
29163630506bSToomas Soome 		 */
29173630506bSToomas Soome 		TAILQ_FOREACH(p, res, next) {
29183630506bSToomas Soome 			if (p->width * p->height < rp->width * rp->height) {
29193630506bSToomas Soome 				/* Keep preferred mode first */
29203630506bSToomas Soome 				if (TAILQ_FIRST(res) == p)
29213630506bSToomas Soome 					TAILQ_INSERT_AFTER(res, p, rp, next);
29223630506bSToomas Soome 				else
29233630506bSToomas Soome 					TAILQ_INSERT_BEFORE(p, rp, next);
29243630506bSToomas Soome 				break;
29253630506bSToomas Soome 			}
29263630506bSToomas Soome 			if (TAILQ_NEXT(p, next) == NULL) {
29273630506bSToomas Soome 				TAILQ_INSERT_TAIL(res, rp, next);
29283630506bSToomas Soome 				break;
29293630506bSToomas Soome 			}
29303630506bSToomas Soome 		}
29313630506bSToomas Soome 	}
29323630506bSToomas Soome 	return (!TAILQ_EMPTY(res));
29333630506bSToomas Soome }
293418968b82SWarner Losh 
293518968b82SWarner Losh vm_offset_t
build_font_module(vm_offset_t addr)293618968b82SWarner Losh build_font_module(vm_offset_t addr)
293718968b82SWarner Losh {
293818968b82SWarner Losh 	vt_font_bitmap_data_t *bd;
293918968b82SWarner Losh 	struct vt_font *fd;
294018968b82SWarner Losh 	struct preloaded_file *fp;
294118968b82SWarner Losh 	size_t size;
294218968b82SWarner Losh 	uint32_t checksum;
294318968b82SWarner Losh 	int i;
294418968b82SWarner Losh 	struct font_info fi;
294518968b82SWarner Losh 	struct fontlist *fl;
294618968b82SWarner Losh 	uint64_t fontp;
294718968b82SWarner Losh 
294818968b82SWarner Losh 	if (STAILQ_EMPTY(&fonts))
294918968b82SWarner Losh 		return (addr);
295018968b82SWarner Losh 
295118968b82SWarner Losh 	/* We can't load first */
295218968b82SWarner Losh 	if ((file_findfile(NULL, NULL)) == NULL) {
295318968b82SWarner Losh 		printf("Can not load font module: %s\n",
295418968b82SWarner Losh 		    "the kernel is not loaded");
295518968b82SWarner Losh 		return (addr);
295618968b82SWarner Losh 	}
295718968b82SWarner Losh 
295818968b82SWarner Losh 	/* helper pointers */
295918968b82SWarner Losh 	bd = NULL;
296018968b82SWarner Losh 	STAILQ_FOREACH(fl, &fonts, font_next) {
296118968b82SWarner Losh 		if (gfx_state.tg_font.vf_width == fl->font_data->vfbd_width &&
296218968b82SWarner Losh 		    gfx_state.tg_font.vf_height == fl->font_data->vfbd_height) {
296318968b82SWarner Losh 			/*
296418968b82SWarner Losh 			 * Kernel does have better built in font.
296518968b82SWarner Losh 			 */
296618968b82SWarner Losh 			if (fl->font_flags == FONT_BUILTIN)
296718968b82SWarner Losh 				return (addr);
296818968b82SWarner Losh 
296918968b82SWarner Losh 			bd = fl->font_data;
297018968b82SWarner Losh 			break;
297118968b82SWarner Losh 		}
297218968b82SWarner Losh 	}
297318968b82SWarner Losh 	if (bd == NULL)
297418968b82SWarner Losh 		return (addr);
297518968b82SWarner Losh 	fd = bd->vfbd_font;
297618968b82SWarner Losh 
297718968b82SWarner Losh 	fi.fi_width = fd->vf_width;
297818968b82SWarner Losh 	checksum = fi.fi_width;
297918968b82SWarner Losh 	fi.fi_height = fd->vf_height;
298018968b82SWarner Losh 	checksum += fi.fi_height;
298118968b82SWarner Losh 	fi.fi_bitmap_size = bd->vfbd_uncompressed_size;
298218968b82SWarner Losh 	checksum += fi.fi_bitmap_size;
298318968b82SWarner Losh 
298418968b82SWarner Losh 	size = roundup2(sizeof (struct font_info), 8);
298518968b82SWarner Losh 	for (i = 0; i < VFNT_MAPS; i++) {
298618968b82SWarner Losh 		fi.fi_map_count[i] = fd->vf_map_count[i];
298718968b82SWarner Losh 		checksum += fi.fi_map_count[i];
298818968b82SWarner Losh 		size += fd->vf_map_count[i] * sizeof (struct vfnt_map);
298918968b82SWarner Losh 		size += roundup2(size, 8);
299018968b82SWarner Losh 	}
299118968b82SWarner Losh 	size += bd->vfbd_uncompressed_size;
299218968b82SWarner Losh 
299318968b82SWarner Losh 	fi.fi_checksum = -checksum;
299418968b82SWarner Losh 
299586077f4fSAhmad Khalifa 	fp = file_findfile(NULL, md_kerntype);
299618968b82SWarner Losh 	if (fp == NULL)
299718968b82SWarner Losh 		panic("can't find kernel file");
299818968b82SWarner Losh 
299918968b82SWarner Losh 	fontp = addr;
300018968b82SWarner Losh 	addr += archsw.arch_copyin(&fi, addr, sizeof (struct font_info));
300118968b82SWarner Losh 	addr = roundup2(addr, 8);
300218968b82SWarner Losh 
300318968b82SWarner Losh 	/* Copy maps. */
300418968b82SWarner Losh 	for (i = 0; i < VFNT_MAPS; i++) {
300518968b82SWarner Losh 		if (fd->vf_map_count[i] != 0) {
300618968b82SWarner Losh 			addr += archsw.arch_copyin(fd->vf_map[i], addr,
300718968b82SWarner Losh 			    fd->vf_map_count[i] * sizeof (struct vfnt_map));
300818968b82SWarner Losh 			addr = roundup2(addr, 8);
300918968b82SWarner Losh 		}
301018968b82SWarner Losh 	}
301118968b82SWarner Losh 
301218968b82SWarner Losh 	/* Copy the bitmap. */
301318968b82SWarner Losh 	addr += archsw.arch_copyin(fd->vf_bytes, addr, fi.fi_bitmap_size);
301418968b82SWarner Losh 
301518968b82SWarner Losh 	/* Looks OK so far; populate control structure */
301618968b82SWarner Losh 	file_addmetadata(fp, MODINFOMD_FONT, sizeof(fontp), &fontp);
301718968b82SWarner Losh 	return (addr);
301818968b82SWarner Losh }
301900460cc8SEmmanuel Vadot 
302000460cc8SEmmanuel Vadot vm_offset_t
build_splash_module(vm_offset_t addr)302100460cc8SEmmanuel Vadot build_splash_module(vm_offset_t addr)
302200460cc8SEmmanuel Vadot {
302300460cc8SEmmanuel Vadot 	struct preloaded_file *fp;
302400460cc8SEmmanuel Vadot 	struct splash_info si;
302500460cc8SEmmanuel Vadot 	const char *splash;
302600460cc8SEmmanuel Vadot 	png_t png;
302700460cc8SEmmanuel Vadot 	uint64_t splashp;
302800460cc8SEmmanuel Vadot 	int error;
302900460cc8SEmmanuel Vadot 
303000460cc8SEmmanuel Vadot 	/* We can't load first */
303100460cc8SEmmanuel Vadot 	if ((file_findfile(NULL, NULL)) == NULL) {
303200460cc8SEmmanuel Vadot 		printf("Can not load splash module: %s\n",
303300460cc8SEmmanuel Vadot 		    "the kernel is not loaded");
303400460cc8SEmmanuel Vadot 		return (addr);
303500460cc8SEmmanuel Vadot 	}
303600460cc8SEmmanuel Vadot 
303786077f4fSAhmad Khalifa 	fp = file_findfile(NULL, md_kerntype);
303800460cc8SEmmanuel Vadot 	if (fp == NULL)
303900460cc8SEmmanuel Vadot 		panic("can't find kernel file");
304000460cc8SEmmanuel Vadot 
304100460cc8SEmmanuel Vadot 	splash = getenv("splash");
304200460cc8SEmmanuel Vadot 	if (splash == NULL)
304300460cc8SEmmanuel Vadot 		return (addr);
304400460cc8SEmmanuel Vadot 
304500460cc8SEmmanuel Vadot 	/* Parse png */
304600460cc8SEmmanuel Vadot 	if ((error = png_open(&png, splash)) != PNG_NO_ERROR) {
304700460cc8SEmmanuel Vadot 		return (addr);
304800460cc8SEmmanuel Vadot 	}
304900460cc8SEmmanuel Vadot 
305000460cc8SEmmanuel Vadot 	si.si_width = png.width;
305100460cc8SEmmanuel Vadot 	si.si_height = png.height;
305200460cc8SEmmanuel Vadot 	si.si_depth = png.bpp;
305300460cc8SEmmanuel Vadot 	splashp = addr;
305400460cc8SEmmanuel Vadot 	addr += archsw.arch_copyin(&si, addr, sizeof (struct splash_info));
305500460cc8SEmmanuel Vadot 	addr = roundup2(addr, 8);
305600460cc8SEmmanuel Vadot 
305700460cc8SEmmanuel Vadot 	/* Copy the bitmap. */
305800460cc8SEmmanuel Vadot 	addr += archsw.arch_copyin(png.image, addr, png.png_datalen);
305900460cc8SEmmanuel Vadot 
306000460cc8SEmmanuel Vadot 	printf("Loading splash ok\n");
306100460cc8SEmmanuel Vadot 	file_addmetadata(fp, MODINFOMD_SPLASH, sizeof(splashp), &splashp);
306200460cc8SEmmanuel Vadot 	return (addr);
306300460cc8SEmmanuel Vadot }
3064