xref: /freebsd/stand/common/gfx_fb.c (revision 86077f4fd11070518a6d04eee7fdb93cbbfb1b52)
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 
105*86077f4fSAhmad Khalifa #include "modinfo.h"
106*86077f4fSAhmad 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 
1643630506bSToomas Soome struct text_pixel *screen_buffer;
1653630506bSToomas Soome #if defined(EFI)
1663630506bSToomas Soome static EFI_GRAPHICS_OUTPUT_BLT_PIXEL *GlyphBuffer;
1673630506bSToomas Soome #else
1683630506bSToomas Soome static struct paletteentry *GlyphBuffer;
1693630506bSToomas Soome #endif
1703630506bSToomas Soome static size_t GlyphBufferSize;
1713630506bSToomas Soome 
1723630506bSToomas Soome static bool insert_font(char *, FONT_FLAGS);
1733630506bSToomas Soome static int font_set(struct env_var *, int, const void *);
1743630506bSToomas Soome static void * allocate_glyphbuffer(uint32_t, uint32_t);
1753630506bSToomas Soome static void gfx_fb_cursor_draw(teken_gfx_t *, const teken_pos_t *, bool);
1763630506bSToomas Soome 
1773630506bSToomas Soome /*
1783630506bSToomas Soome  * Initialize gfx framework.
1793630506bSToomas Soome  */
1803630506bSToomas Soome void
gfx_framework_init(void)1813630506bSToomas Soome gfx_framework_init(void)
1823630506bSToomas Soome {
1833630506bSToomas Soome 	/*
1843630506bSToomas Soome 	 * Setup font list to have builtin font.
1853630506bSToomas Soome 	 */
1863630506bSToomas Soome 	(void) insert_font(NULL, FONT_BUILTIN);
1876faf55c8SWarner Losh 	gfx_interp_ref();	/* Draw in the gfx interpreter for this thing */
1883630506bSToomas Soome }
1893630506bSToomas Soome 
1903630506bSToomas Soome static uint8_t *
gfx_get_fb_address(void)1913630506bSToomas Soome gfx_get_fb_address(void)
1923630506bSToomas Soome {
1933630506bSToomas Soome 	return (ptov((uint32_t)gfx_state.tg_fb.fb_addr));
1943630506bSToomas Soome }
1953630506bSToomas Soome 
1963630506bSToomas Soome /*
1973630506bSToomas Soome  * Utility function to parse gfx mode line strings.
1983630506bSToomas Soome  */
1993630506bSToomas Soome bool
gfx_parse_mode_str(char * str,int * x,int * y,int * depth)2003630506bSToomas Soome gfx_parse_mode_str(char *str, int *x, int *y, int *depth)
2013630506bSToomas Soome {
2023630506bSToomas Soome 	char *p, *end;
2033630506bSToomas Soome 
2043630506bSToomas Soome 	errno = 0;
2053630506bSToomas Soome 	p = str;
2063630506bSToomas Soome 	*x = strtoul(p, &end, 0);
2073630506bSToomas Soome 	if (*x == 0 || errno != 0)
2083630506bSToomas Soome 		return (false);
2093630506bSToomas Soome 	if (*end != 'x')
2103630506bSToomas Soome 		return (false);
2113630506bSToomas Soome 	p = end + 1;
2123630506bSToomas Soome 	*y = strtoul(p, &end, 0);
2133630506bSToomas Soome 	if (*y == 0 || errno != 0)
2143630506bSToomas Soome 		return (false);
2153630506bSToomas Soome 	if (*end != 'x') {
2163630506bSToomas Soome 		*depth = -1;    /* auto select */
2173630506bSToomas Soome 	} else {
2183630506bSToomas Soome 		p = end + 1;
2193630506bSToomas Soome 		*depth = strtoul(p, &end, 0);
2203630506bSToomas Soome 		if (*depth == 0 || errno != 0 || *end != '\0')
2213630506bSToomas Soome 			return (false);
2223630506bSToomas Soome 	}
2233630506bSToomas Soome 
2243630506bSToomas Soome 	return (true);
2253630506bSToomas Soome }
2263630506bSToomas Soome 
2273630506bSToomas 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)2283630506bSToomas Soome rgb_color_map(uint8_t index, uint32_t rmax, int roffset,
2293630506bSToomas Soome     uint32_t gmax, int goffset, uint32_t bmax, int boffset)
2303630506bSToomas Soome {
2313630506bSToomas Soome 	uint32_t color, code, gray, level;
2323630506bSToomas Soome 
2333630506bSToomas Soome 	if (index < NCOLORS) {
2343630506bSToomas Soome #define	CF(_f, _i) ((_f ## max * color_def[(_i)]._f / 100) << _f ## offset)
2353630506bSToomas Soome 		return (CF(r, index) | CF(g, index) | CF(b, index));
2363630506bSToomas Soome #undef  CF
2373630506bSToomas Soome         }
2383630506bSToomas Soome 
2393630506bSToomas Soome #define	CF(_f, _c) ((_f ## max & _c) << _f ## offset)
2403630506bSToomas Soome         /* 6x6x6 color cube */
2413630506bSToomas Soome         if (index > 15 && index < 232) {
2423630506bSToomas Soome                 uint32_t red, green, blue;
2433630506bSToomas Soome 
2443630506bSToomas Soome                 for (red = 0; red < 6; red++) {
2453630506bSToomas Soome                         for (green = 0; green < 6; green++) {
2463630506bSToomas Soome                                 for (blue = 0; blue < 6; blue++) {
2473630506bSToomas Soome                                         code = 16 + (red * 36) +
2483630506bSToomas Soome                                             (green * 6) + blue;
2493630506bSToomas Soome                                         if (code != index)
2503630506bSToomas Soome                                                 continue;
2513630506bSToomas Soome                                         red = red ? (red * 40 + 55) : 0;
2523630506bSToomas Soome                                         green = green ? (green * 40 + 55) : 0;
2533630506bSToomas Soome                                         blue = blue ? (blue * 40 + 55) : 0;
2543630506bSToomas Soome                                         color = CF(r, red);
2553630506bSToomas Soome 					color |= CF(g, green);
2563630506bSToomas Soome 					color |= CF(b, blue);
2573630506bSToomas Soome 					return (color);
2583630506bSToomas Soome                                 }
2593630506bSToomas Soome                         }
2603630506bSToomas Soome                 }
2613630506bSToomas Soome         }
2623630506bSToomas Soome 
2633630506bSToomas Soome         /* colors 232-255 are a grayscale ramp */
2643630506bSToomas Soome         for (gray = 0; gray < 24; gray++) {
2653630506bSToomas Soome                 level = (gray * 10) + 8;
2663630506bSToomas Soome                 code = 232 + gray;
2673630506bSToomas Soome                 if (code == index)
2683630506bSToomas Soome                         break;
2693630506bSToomas Soome         }
2703630506bSToomas Soome         return (CF(r, level) | CF(g, level) | CF(b, level));
2713630506bSToomas Soome #undef  CF
2723630506bSToomas Soome }
2733630506bSToomas Soome 
2743630506bSToomas Soome /*
2753630506bSToomas Soome  * Support for color mapping.
2763630506bSToomas Soome  * For 8, 24 and 32 bit depth, use mask size 8.
2773630506bSToomas Soome  * 15/16 bit depth needs to use mask size from mode,
2783630506bSToomas Soome  * or we will lose color information from 32-bit to 15/16 bit translation.
2793630506bSToomas Soome  */
2803630506bSToomas Soome uint32_t
gfx_fb_color_map(uint8_t index)2813630506bSToomas Soome gfx_fb_color_map(uint8_t index)
2823630506bSToomas Soome {
2833630506bSToomas Soome 	int rmask, gmask, bmask;
2843630506bSToomas Soome 	int roff, goff, boff, bpp;
2853630506bSToomas Soome 
2863630506bSToomas Soome 	roff = ffs(gfx_state.tg_fb.fb_mask_red) - 1;
2873630506bSToomas Soome         goff = ffs(gfx_state.tg_fb.fb_mask_green) - 1;
2883630506bSToomas Soome         boff = ffs(gfx_state.tg_fb.fb_mask_blue) - 1;
2893630506bSToomas Soome 	bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3;
2903630506bSToomas Soome 
2913630506bSToomas Soome 	if (bpp == 2)
2923630506bSToomas Soome 		rmask = gfx_state.tg_fb.fb_mask_red >> roff;
2933630506bSToomas Soome 	else
2943630506bSToomas Soome 		rmask = 0xff;
2953630506bSToomas Soome 
2963630506bSToomas Soome 	if (bpp == 2)
2973630506bSToomas Soome 		gmask = gfx_state.tg_fb.fb_mask_green >> goff;
2983630506bSToomas Soome 	else
2993630506bSToomas Soome 		gmask = 0xff;
3003630506bSToomas Soome 
3013630506bSToomas Soome 	if (bpp == 2)
3023630506bSToomas Soome 		bmask = gfx_state.tg_fb.fb_mask_blue >> boff;
3033630506bSToomas Soome 	else
3043630506bSToomas Soome 		bmask = 0xff;
3053630506bSToomas Soome 
3063630506bSToomas Soome 	return (rgb_color_map(index, rmask, 16, gmask, 8, bmask, 0));
3073630506bSToomas Soome }
3083630506bSToomas Soome 
3094bbfe4bfSToomas Soome /*
3104bbfe4bfSToomas Soome  * Get indexed color from RGB. This function is used to write data to video
3114bbfe4bfSToomas Soome  * memory when the adapter is set to use indexed colors.
3124bbfe4bfSToomas Soome  * Since UEFI does only support 32-bit colors, we do not implement it for
3134bbfe4bfSToomas Soome  * UEFI because there is no need for it and we do not have palette array
3144bbfe4bfSToomas Soome  * for UEFI.
3154bbfe4bfSToomas Soome  */
3163630506bSToomas Soome static uint8_t
rgb_to_color_index(uint8_t r,uint8_t g,uint8_t b)3173630506bSToomas Soome rgb_to_color_index(uint8_t r, uint8_t g, uint8_t b)
3183630506bSToomas Soome {
3193630506bSToomas Soome #if !defined(EFI)
3203630506bSToomas Soome 	uint32_t color, best, dist, k;
3213630506bSToomas Soome 	int diff;
3223630506bSToomas Soome 
3233630506bSToomas Soome 	color = 0;
3241caed70cSToomas Soome 	best = 255 * 255 * 255;
3253630506bSToomas Soome 	for (k = 0; k < NCMAP; k++) {
3263630506bSToomas Soome 		diff = r - pe8[k].Red;
3273630506bSToomas Soome 		dist = diff * diff;
3283630506bSToomas Soome 		diff = g - pe8[k].Green;
3293630506bSToomas Soome 		dist += diff * diff;
3303630506bSToomas Soome 		diff = b - pe8[k].Blue;
3313630506bSToomas Soome 		dist += diff * diff;
3323630506bSToomas Soome 
3334bbfe4bfSToomas Soome 		/* Exact match, exit the loop */
3343630506bSToomas Soome 		if (dist == 0)
3353630506bSToomas Soome 			break;
3364bbfe4bfSToomas Soome 
3373630506bSToomas Soome 		if (dist < best) {
3383630506bSToomas Soome 			color = k;
3393630506bSToomas Soome 			best = dist;
3403630506bSToomas Soome 		}
3413630506bSToomas Soome 	}
3423630506bSToomas Soome 	if (k == NCMAP)
3433630506bSToomas Soome 		k = color;
3443630506bSToomas Soome 	return (k);
3453630506bSToomas Soome #else
3463630506bSToomas Soome 	(void) r;
3473630506bSToomas Soome 	(void) g;
3483630506bSToomas Soome 	(void) b;
3493630506bSToomas Soome 	return (0);
3503630506bSToomas Soome #endif
3513630506bSToomas Soome }
3523630506bSToomas Soome 
3533630506bSToomas 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)3543630506bSToomas Soome generate_cons_palette(uint32_t *palette, int format,
3553630506bSToomas Soome     uint32_t rmax, int roffset, uint32_t gmax, int goffset,
3563630506bSToomas Soome     uint32_t bmax, int boffset)
3573630506bSToomas Soome {
3583630506bSToomas Soome 	int i;
3593630506bSToomas Soome 
3603630506bSToomas Soome 	switch (format) {
3613630506bSToomas Soome 	case COLOR_FORMAT_VGA:
3623630506bSToomas Soome 		for (i = 0; i < NCOLORS; i++)
3633630506bSToomas Soome 			palette[i] = cons_to_vga_colors[i];
3643630506bSToomas Soome 		for (; i < NCMAP; i++)
3653630506bSToomas Soome 			palette[i] = i;
3663630506bSToomas Soome 		break;
3673630506bSToomas Soome 	case COLOR_FORMAT_RGB:
3683630506bSToomas Soome 		for (i = 0; i < NCMAP; i++)
3693630506bSToomas Soome 			palette[i] = rgb_color_map(i, rmax, roffset,
3703630506bSToomas Soome 			    gmax, goffset, bmax, boffset);
3713630506bSToomas Soome 		break;
3723630506bSToomas Soome 	default:
3733630506bSToomas Soome 		return (ENODEV);
3743630506bSToomas Soome 	}
3753630506bSToomas Soome 
3763630506bSToomas Soome 	return (0);
3773630506bSToomas Soome }
3783630506bSToomas Soome 
3793630506bSToomas Soome static void
gfx_mem_wr1(uint8_t * base,size_t size,uint32_t o,uint8_t v)3803630506bSToomas Soome gfx_mem_wr1(uint8_t *base, size_t size, uint32_t o, uint8_t v)
3813630506bSToomas Soome {
3823630506bSToomas Soome 
3833630506bSToomas Soome 	if (o >= size)
3843630506bSToomas Soome 		return;
3853630506bSToomas Soome 	*(uint8_t *)(base + o) = v;
3863630506bSToomas Soome }
3873630506bSToomas Soome 
3883630506bSToomas Soome static void
gfx_mem_wr2(uint8_t * base,size_t size,uint32_t o,uint16_t v)3893630506bSToomas Soome gfx_mem_wr2(uint8_t *base, size_t size, uint32_t o, uint16_t v)
3903630506bSToomas Soome {
3913630506bSToomas Soome 
3923630506bSToomas Soome 	if (o >= size)
3933630506bSToomas Soome 		return;
3943630506bSToomas Soome 	*(uint16_t *)(base + o) = v;
3953630506bSToomas Soome }
3963630506bSToomas Soome 
3973630506bSToomas Soome static void
gfx_mem_wr4(uint8_t * base,size_t size,uint32_t o,uint32_t v)3983630506bSToomas Soome gfx_mem_wr4(uint8_t *base, size_t size, uint32_t o, uint32_t v)
3993630506bSToomas Soome {
4003630506bSToomas Soome 
4013630506bSToomas Soome 	if (o >= size)
4023630506bSToomas Soome 		return;
4033630506bSToomas Soome 	*(uint32_t *)(base + o) = v;
4043630506bSToomas Soome }
4053630506bSToomas Soome 
gfxfb_blt_fill(void * BltBuffer,uint32_t DestinationX,uint32_t DestinationY,uint32_t Width,uint32_t Height)4063630506bSToomas Soome static int gfxfb_blt_fill(void *BltBuffer,
4073630506bSToomas Soome     uint32_t DestinationX, uint32_t DestinationY,
4083630506bSToomas Soome     uint32_t Width, uint32_t Height)
4093630506bSToomas Soome {
4103630506bSToomas Soome #if defined(EFI)
4113630506bSToomas Soome 	EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
4123630506bSToomas Soome #else
4133630506bSToomas Soome 	struct paletteentry *p;
4143630506bSToomas Soome #endif
4153630506bSToomas Soome 	uint32_t data, bpp, pitch, y, x;
4163630506bSToomas Soome 	int roff, goff, boff;
4173630506bSToomas Soome 	size_t size;
4183630506bSToomas Soome 	off_t off;
4193630506bSToomas Soome 	uint8_t *destination;
4203630506bSToomas Soome 
4213630506bSToomas Soome 	if (BltBuffer == NULL)
4223630506bSToomas Soome 		return (EINVAL);
4233630506bSToomas Soome 
4243630506bSToomas Soome 	if (DestinationY + Height > gfx_state.tg_fb.fb_height)
4253630506bSToomas Soome 		return (EINVAL);
4263630506bSToomas Soome 
4273630506bSToomas Soome 	if (DestinationX + Width > gfx_state.tg_fb.fb_width)
4283630506bSToomas Soome 		return (EINVAL);
4293630506bSToomas Soome 
4303630506bSToomas Soome 	if (Width == 0 || Height == 0)
4313630506bSToomas Soome 		return (EINVAL);
4323630506bSToomas Soome 
4333630506bSToomas Soome 	p = BltBuffer;
4343630506bSToomas Soome 	roff = ffs(gfx_state.tg_fb.fb_mask_red) - 1;
4353630506bSToomas Soome 	goff = ffs(gfx_state.tg_fb.fb_mask_green) - 1;
4363630506bSToomas Soome 	boff = ffs(gfx_state.tg_fb.fb_mask_blue) - 1;
4373630506bSToomas Soome 
4383630506bSToomas Soome 	if (gfx_state.tg_fb.fb_bpp == 8) {
4393630506bSToomas Soome 		data = rgb_to_color_index(p->Red, p->Green, p->Blue);
4403630506bSToomas Soome 	} else {
4413630506bSToomas Soome 		data = (p->Red &
4423630506bSToomas Soome 		    (gfx_state.tg_fb.fb_mask_red >> roff)) << roff;
4433630506bSToomas Soome 		data |= (p->Green &
4443630506bSToomas Soome 		    (gfx_state.tg_fb.fb_mask_green >> goff)) << goff;
4453630506bSToomas Soome 		data |= (p->Blue &
4463630506bSToomas Soome 		    (gfx_state.tg_fb.fb_mask_blue >> boff)) << boff;
4473630506bSToomas Soome 	}
4483630506bSToomas Soome 
4493630506bSToomas Soome 	bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3;
4503630506bSToomas Soome 	pitch = gfx_state.tg_fb.fb_stride * bpp;
4513630506bSToomas Soome 	destination = gfx_get_fb_address();
4523630506bSToomas Soome 	size = gfx_state.tg_fb.fb_size;
4533630506bSToomas Soome 
4543630506bSToomas Soome 	for (y = DestinationY; y < Height + DestinationY; y++) {
4553630506bSToomas Soome 		off = y * pitch + DestinationX * bpp;
4563630506bSToomas Soome 		for (x = 0; x < Width; x++) {
4573630506bSToomas Soome 			switch (bpp) {
4583630506bSToomas Soome 			case 1:
4593630506bSToomas Soome 				gfx_mem_wr1(destination, size, off,
4603630506bSToomas Soome 				    (data < NCOLORS) ?
4613630506bSToomas Soome 				    cons_to_vga_colors[data] : data);
4623630506bSToomas Soome 				break;
4633630506bSToomas Soome 			case 2:
4643630506bSToomas Soome 				gfx_mem_wr2(destination, size, off, data);
4653630506bSToomas Soome 				break;
4663630506bSToomas Soome 			case 3:
4673630506bSToomas Soome 				gfx_mem_wr1(destination, size, off,
4683630506bSToomas Soome 				    (data >> 16) & 0xff);
4693630506bSToomas Soome 				gfx_mem_wr1(destination, size, off + 1,
4703630506bSToomas Soome 				    (data >> 8) & 0xff);
4713630506bSToomas Soome 				gfx_mem_wr1(destination, size, off + 2,
4723630506bSToomas Soome 				    data & 0xff);
4733630506bSToomas Soome 				break;
4743630506bSToomas Soome 			case 4:
4753630506bSToomas Soome 				gfx_mem_wr4(destination, size, off, data);
4763630506bSToomas Soome 				break;
4771caed70cSToomas Soome 			default:
4781caed70cSToomas Soome 				return (EINVAL);
4793630506bSToomas Soome 			}
4803630506bSToomas Soome 			off += bpp;
4813630506bSToomas Soome 		}
4823630506bSToomas Soome 	}
4833630506bSToomas Soome 
4843630506bSToomas Soome 	return (0);
4853630506bSToomas Soome }
4863630506bSToomas Soome 
4873630506bSToomas 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)4883630506bSToomas Soome gfxfb_blt_video_to_buffer(void *BltBuffer, uint32_t SourceX, uint32_t SourceY,
4893630506bSToomas Soome     uint32_t DestinationX, uint32_t DestinationY,
4903630506bSToomas Soome     uint32_t Width, uint32_t Height, uint32_t Delta)
4913630506bSToomas Soome {
4923630506bSToomas Soome #if defined(EFI)
4933630506bSToomas Soome 	EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
4943630506bSToomas Soome #else
4953630506bSToomas Soome 	struct paletteentry *p;
4963630506bSToomas Soome #endif
4973630506bSToomas Soome 	uint32_t x, sy, dy;
4983630506bSToomas Soome 	uint32_t bpp, pitch, copybytes;
4993630506bSToomas Soome 	off_t off;
5001caed70cSToomas Soome 	uint8_t *source, *destination, *sb;
5013630506bSToomas Soome 	uint8_t rm, rp, gm, gp, bm, bp;
5023630506bSToomas Soome 	bool bgra;
5033630506bSToomas Soome 
5043630506bSToomas Soome 	if (BltBuffer == NULL)
5053630506bSToomas Soome 		return (EINVAL);
5063630506bSToomas Soome 
5073630506bSToomas Soome 	if (SourceY + Height >
5083630506bSToomas Soome 	    gfx_state.tg_fb.fb_height)
5093630506bSToomas Soome 		return (EINVAL);
5103630506bSToomas Soome 
5113630506bSToomas Soome 	if (SourceX + Width > gfx_state.tg_fb.fb_width)
5123630506bSToomas Soome 		return (EINVAL);
5133630506bSToomas Soome 
5143630506bSToomas Soome 	if (Width == 0 || Height == 0)
5153630506bSToomas Soome 		return (EINVAL);
5163630506bSToomas Soome 
5173630506bSToomas Soome 	if (Delta == 0)
5183630506bSToomas Soome 		Delta = Width * sizeof (*p);
5193630506bSToomas Soome 
5203630506bSToomas Soome 	bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3;
5213630506bSToomas Soome 	pitch = gfx_state.tg_fb.fb_stride * bpp;
5223630506bSToomas Soome 
5233630506bSToomas Soome 	copybytes = Width * bpp;
5243630506bSToomas Soome 
5253630506bSToomas Soome 	rp = ffs(gfx_state.tg_fb.fb_mask_red) - 1;
5263630506bSToomas Soome 	gp = ffs(gfx_state.tg_fb.fb_mask_green) - 1;
5273630506bSToomas Soome 	bp = ffs(gfx_state.tg_fb.fb_mask_blue) - 1;
5283630506bSToomas Soome 	rm = gfx_state.tg_fb.fb_mask_red >> rp;
5293630506bSToomas Soome 	gm = gfx_state.tg_fb.fb_mask_green >> gp;
5303630506bSToomas Soome 	bm = gfx_state.tg_fb.fb_mask_blue >> bp;
5313630506bSToomas Soome 
5323630506bSToomas Soome 	/* If FB pixel format is BGRA, we can use direct copy. */
5333630506bSToomas Soome 	bgra = bpp == 4 &&
5343630506bSToomas Soome 	    ffs(rm) - 1 == 8 && rp == 16 &&
5353630506bSToomas Soome 	    ffs(gm) - 1 == 8 && gp == 8 &&
5363630506bSToomas Soome 	    ffs(bm) - 1 == 8 && bp == 0;
5373630506bSToomas Soome 
5383630506bSToomas Soome 	for (sy = SourceY, dy = DestinationY; dy < Height + DestinationY;
5393630506bSToomas Soome 	    sy++, dy++) {
5403630506bSToomas Soome 		off = sy * pitch + SourceX * bpp;
5413630506bSToomas Soome 		source = gfx_get_fb_address() + off;
5423630506bSToomas Soome 		destination = (uint8_t *)BltBuffer + dy * Delta +
5433630506bSToomas Soome 		    DestinationX * sizeof (*p);
5443630506bSToomas Soome 
5451caed70cSToomas Soome 		if (bgra) {
5463630506bSToomas Soome 			bcopy(source, destination, copybytes);
5471caed70cSToomas Soome 		} else {
5483630506bSToomas Soome 			for (x = 0; x < Width; x++) {
5493630506bSToomas Soome 				uint32_t c = 0;
5503630506bSToomas Soome 
5511caed70cSToomas Soome 				p = (void *)(destination + x * sizeof (*p));
5521caed70cSToomas Soome 				sb = source + x * bpp;
5533630506bSToomas Soome 				switch (bpp) {
5543630506bSToomas Soome 				case 1:
5553630506bSToomas Soome 					c = *sb;
5563630506bSToomas Soome 					break;
5573630506bSToomas Soome 				case 2:
5583630506bSToomas Soome 					c = *(uint16_t *)sb;
5593630506bSToomas Soome 					break;
5603630506bSToomas Soome 				case 3:
5613630506bSToomas Soome 					c = sb[0] << 16 | sb[1] << 8 | sb[2];
5623630506bSToomas Soome 					break;
5633630506bSToomas Soome 				case 4:
5643630506bSToomas Soome 					c = *(uint32_t *)sb;
5653630506bSToomas Soome 					break;
5661caed70cSToomas Soome 				default:
5671caed70cSToomas Soome 					return (EINVAL);
5683630506bSToomas Soome 				}
5693630506bSToomas Soome 
5703630506bSToomas Soome 				if (bpp == 1) {
5713630506bSToomas Soome 					*(uint32_t *)p = gfx_fb_color_map(
5723630506bSToomas Soome 					    (c < 16) ?
5733630506bSToomas Soome 					    vga_to_cons_colors[c] : c);
5743630506bSToomas Soome 				} else {
5753630506bSToomas Soome 					p->Red = (c >> rp) & rm;
5763630506bSToomas Soome 					p->Green = (c >> gp) & gm;
5773630506bSToomas Soome 					p->Blue = (c >> bp) & bm;
5783630506bSToomas Soome 					p->Reserved = 0;
5793630506bSToomas Soome 				}
5803630506bSToomas Soome 			}
5813630506bSToomas Soome 		}
5823630506bSToomas Soome 	}
5833630506bSToomas Soome 
5843630506bSToomas Soome 	return (0);
5853630506bSToomas Soome }
5863630506bSToomas Soome 
5873630506bSToomas 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)5883630506bSToomas Soome gfxfb_blt_buffer_to_video(void *BltBuffer, uint32_t SourceX, uint32_t SourceY,
5893630506bSToomas Soome     uint32_t DestinationX, uint32_t DestinationY,
5903630506bSToomas Soome     uint32_t Width, uint32_t Height, uint32_t Delta)
5913630506bSToomas Soome {
5923630506bSToomas Soome #if defined(EFI)
5933630506bSToomas Soome 	EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
5943630506bSToomas Soome #else
5953630506bSToomas Soome 	struct paletteentry *p;
5963630506bSToomas Soome #endif
5973630506bSToomas Soome 	uint32_t x, sy, dy;
5983630506bSToomas Soome 	uint32_t bpp, pitch, copybytes;
5993630506bSToomas Soome 	off_t off;
6001caed70cSToomas Soome 	uint8_t *source, *destination;
6013630506bSToomas Soome 	uint8_t rm, rp, gm, gp, bm, bp;
6023630506bSToomas Soome 	bool bgra;
6033630506bSToomas Soome 
6043630506bSToomas Soome 	if (BltBuffer == NULL)
6053630506bSToomas Soome 		return (EINVAL);
6063630506bSToomas Soome 
6073630506bSToomas Soome 	if (DestinationY + Height >
6083630506bSToomas Soome 	    gfx_state.tg_fb.fb_height)
6093630506bSToomas Soome 		return (EINVAL);
6103630506bSToomas Soome 
6113630506bSToomas Soome 	if (DestinationX + Width > gfx_state.tg_fb.fb_width)
6123630506bSToomas Soome 		return (EINVAL);
6133630506bSToomas Soome 
6143630506bSToomas Soome 	if (Width == 0 || Height == 0)
6153630506bSToomas Soome 		return (EINVAL);
6163630506bSToomas Soome 
6173630506bSToomas Soome 	if (Delta == 0)
6183630506bSToomas Soome 		Delta = Width * sizeof (*p);
6193630506bSToomas Soome 
6203630506bSToomas Soome 	bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3;
6213630506bSToomas Soome 	pitch = gfx_state.tg_fb.fb_stride * bpp;
6223630506bSToomas Soome 
6233630506bSToomas Soome 	copybytes = Width * bpp;
6243630506bSToomas Soome 
6253630506bSToomas Soome 	rp = ffs(gfx_state.tg_fb.fb_mask_red) - 1;
6263630506bSToomas Soome 	gp = ffs(gfx_state.tg_fb.fb_mask_green) - 1;
6273630506bSToomas Soome 	bp = ffs(gfx_state.tg_fb.fb_mask_blue) - 1;
6283630506bSToomas Soome 	rm = gfx_state.tg_fb.fb_mask_red >> rp;
6293630506bSToomas Soome 	gm = gfx_state.tg_fb.fb_mask_green >> gp;
6303630506bSToomas Soome 	bm = gfx_state.tg_fb.fb_mask_blue >> bp;
6313630506bSToomas Soome 
6323630506bSToomas Soome 	/* If FB pixel format is BGRA, we can use direct copy. */
6333630506bSToomas Soome 	bgra = bpp == 4 &&
6343630506bSToomas Soome 	    ffs(rm) - 1 == 8 && rp == 16 &&
6353630506bSToomas Soome 	    ffs(gm) - 1 == 8 && gp == 8 &&
6363630506bSToomas Soome 	    ffs(bm) - 1 == 8 && bp == 0;
6373630506bSToomas Soome 
6383630506bSToomas Soome 	for (sy = SourceY, dy = DestinationY; sy < Height + SourceY;
6393630506bSToomas Soome 	    sy++, dy++) {
6403630506bSToomas Soome 		off = dy * pitch + DestinationX * bpp;
6413630506bSToomas Soome 		destination = gfx_get_fb_address() + off;
6423630506bSToomas Soome 
6433630506bSToomas Soome 		if (bgra) {
6443630506bSToomas Soome 			source = (uint8_t *)BltBuffer + sy * Delta +
6453630506bSToomas Soome 			    SourceX * sizeof (*p);
6461caed70cSToomas Soome 			bcopy(source, destination, copybytes);
6473630506bSToomas Soome 		} else {
6483630506bSToomas Soome 			for (x = 0; x < Width; x++) {
6493630506bSToomas Soome 				uint32_t c;
6503630506bSToomas Soome 
6513630506bSToomas Soome 				p = (void *)((uint8_t *)BltBuffer +
6523630506bSToomas Soome 				    sy * Delta +
6533630506bSToomas Soome 				    (SourceX + x) * sizeof (*p));
6543630506bSToomas Soome 				if (bpp == 1) {
6553630506bSToomas Soome 					c = rgb_to_color_index(p->Red,
6563630506bSToomas Soome 					    p->Green, p->Blue);
6573630506bSToomas Soome 				} else {
6583630506bSToomas Soome 					c = (p->Red & rm) << rp |
6593630506bSToomas Soome 					    (p->Green & gm) << gp |
6603630506bSToomas Soome 					    (p->Blue & bm) << bp;
6613630506bSToomas Soome 				}
6623630506bSToomas Soome 				off = x * bpp;
6633630506bSToomas Soome 				switch (bpp) {
6643630506bSToomas Soome 				case 1:
6651caed70cSToomas Soome 					gfx_mem_wr1(destination, copybytes,
6663630506bSToomas Soome 					    off, (c < 16) ?
6673630506bSToomas Soome 					    cons_to_vga_colors[c] : c);
6683630506bSToomas Soome 					break;
6693630506bSToomas Soome 				case 2:
6701caed70cSToomas Soome 					gfx_mem_wr2(destination, copybytes,
6713630506bSToomas Soome 					    off, c);
6723630506bSToomas Soome 					break;
6733630506bSToomas Soome 				case 3:
6741caed70cSToomas Soome 					gfx_mem_wr1(destination, copybytes,
6753630506bSToomas Soome 					    off, (c >> 16) & 0xff);
6761caed70cSToomas Soome 					gfx_mem_wr1(destination, copybytes,
6773630506bSToomas Soome 					    off + 1, (c >> 8) & 0xff);
6781caed70cSToomas Soome 					gfx_mem_wr1(destination, copybytes,
6793630506bSToomas Soome 					    off + 2, c & 0xff);
6803630506bSToomas Soome 					break;
6813630506bSToomas Soome 				case 4:
6821caed70cSToomas Soome 					gfx_mem_wr4(destination, copybytes,
6833630506bSToomas Soome 					    x * bpp, c);
6843630506bSToomas Soome 					break;
6851caed70cSToomas Soome 				default:
6861caed70cSToomas Soome 					return (EINVAL);
6873630506bSToomas Soome 				}
6883630506bSToomas Soome 			}
6891caed70cSToomas Soome 		}
6903630506bSToomas Soome 	}
6913630506bSToomas Soome 
6923630506bSToomas Soome 	return (0);
6933630506bSToomas Soome }
6943630506bSToomas Soome 
6953630506bSToomas 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)6963630506bSToomas Soome gfxfb_blt_video_to_video(uint32_t SourceX, uint32_t SourceY,
6973630506bSToomas Soome     uint32_t DestinationX, uint32_t DestinationY,
6983630506bSToomas Soome     uint32_t Width, uint32_t Height)
6993630506bSToomas Soome {
7003630506bSToomas Soome 	uint32_t bpp, copybytes;
7013630506bSToomas Soome 	int pitch;
7023630506bSToomas Soome 	uint8_t *source, *destination;
7033630506bSToomas Soome 	off_t off;
7043630506bSToomas Soome 
7053630506bSToomas Soome 	if (SourceY + Height >
7063630506bSToomas Soome 	    gfx_state.tg_fb.fb_height)
7073630506bSToomas Soome 		return (EINVAL);
7083630506bSToomas Soome 
7093630506bSToomas Soome 	if (SourceX + Width > gfx_state.tg_fb.fb_width)
7103630506bSToomas Soome 		return (EINVAL);
7113630506bSToomas Soome 
7123630506bSToomas Soome 	if (DestinationY + Height >
7133630506bSToomas Soome 	    gfx_state.tg_fb.fb_height)
7143630506bSToomas Soome 		return (EINVAL);
7153630506bSToomas Soome 
7163630506bSToomas Soome 	if (DestinationX + Width > gfx_state.tg_fb.fb_width)
7173630506bSToomas Soome 		return (EINVAL);
7183630506bSToomas Soome 
7193630506bSToomas Soome 	if (Width == 0 || Height == 0)
7203630506bSToomas Soome 		return (EINVAL);
7213630506bSToomas Soome 
7223630506bSToomas Soome 	bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3;
7233630506bSToomas Soome 	pitch = gfx_state.tg_fb.fb_stride * bpp;
7243630506bSToomas Soome 
7253630506bSToomas Soome 	copybytes = Width * bpp;
7263630506bSToomas Soome 
7273630506bSToomas Soome 	off = SourceY * pitch + SourceX * bpp;
7283630506bSToomas Soome 	source = gfx_get_fb_address() + off;
7293630506bSToomas Soome 	off = DestinationY * pitch + DestinationX * bpp;
7303630506bSToomas Soome 	destination = gfx_get_fb_address() + off;
7313630506bSToomas Soome 
7323630506bSToomas Soome 	if ((uintptr_t)destination > (uintptr_t)source) {
7333630506bSToomas Soome 		source += Height * pitch;
7343630506bSToomas Soome 		destination += Height * pitch;
7353630506bSToomas Soome 		pitch = -pitch;
7363630506bSToomas Soome 	}
7373630506bSToomas Soome 
7383630506bSToomas Soome 	while (Height-- > 0) {
7393630506bSToomas Soome 		bcopy(source, destination, copybytes);
7403630506bSToomas Soome 		source += pitch;
7413630506bSToomas Soome 		destination += pitch;
7423630506bSToomas Soome 	}
7433630506bSToomas Soome 
7443630506bSToomas Soome 	return (0);
7453630506bSToomas Soome }
7463630506bSToomas Soome 
7476102f43cSToomas Soome static void
gfxfb_shadow_fill(uint32_t * BltBuffer,uint32_t DestinationX,uint32_t DestinationY,uint32_t Width,uint32_t Height)7486102f43cSToomas Soome gfxfb_shadow_fill(uint32_t *BltBuffer,
7496102f43cSToomas Soome     uint32_t DestinationX, uint32_t DestinationY,
7506102f43cSToomas Soome     uint32_t Width, uint32_t Height)
7516102f43cSToomas Soome {
7526102f43cSToomas Soome 	uint32_t fbX, fbY;
7536102f43cSToomas Soome 
7546102f43cSToomas Soome 	if (gfx_state.tg_shadow_fb == NULL)
7556102f43cSToomas Soome 		return;
7566102f43cSToomas Soome 
7576102f43cSToomas Soome 	fbX = gfx_state.tg_fb.fb_width;
7586102f43cSToomas Soome 	fbY = gfx_state.tg_fb.fb_height;
7596102f43cSToomas Soome 
7606102f43cSToomas Soome 	if (BltBuffer == NULL)
7616102f43cSToomas Soome 		return;
7626102f43cSToomas Soome 
7636102f43cSToomas Soome 	if (DestinationX + Width > fbX)
7646102f43cSToomas Soome 		Width = fbX - DestinationX;
7656102f43cSToomas Soome 
7666102f43cSToomas Soome 	if (DestinationY + Height > fbY)
7676102f43cSToomas Soome 		Height = fbY - DestinationY;
7686102f43cSToomas Soome 
7696102f43cSToomas Soome 	uint32_t y2 = Height + DestinationY;
7706102f43cSToomas Soome 	for (uint32_t y1 = DestinationY; y1 < y2; y1++) {
7716102f43cSToomas Soome 		uint32_t off = y1 * fbX + DestinationX;
7726102f43cSToomas Soome 
7736102f43cSToomas Soome 		for (uint32_t x = 0; x < Width; x++) {
7746102f43cSToomas Soome 			gfx_state.tg_shadow_fb[off + x] = *BltBuffer;
7756102f43cSToomas Soome 		}
7766102f43cSToomas Soome 	}
7776102f43cSToomas Soome }
7786102f43cSToomas Soome 
7793630506bSToomas 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)7803630506bSToomas Soome gfxfb_blt(void *BltBuffer, GFXFB_BLT_OPERATION BltOperation,
7813630506bSToomas Soome     uint32_t SourceX, uint32_t SourceY,
7823630506bSToomas Soome     uint32_t DestinationX, uint32_t DestinationY,
7833630506bSToomas Soome     uint32_t Width, uint32_t Height, uint32_t Delta)
7843630506bSToomas Soome {
7853630506bSToomas Soome 	int rv;
7863630506bSToomas Soome #if defined(EFI)
7873630506bSToomas Soome 	EFI_STATUS status;
7883630506bSToomas Soome 	EFI_GRAPHICS_OUTPUT *gop = gfx_state.tg_private;
7894c7a3a70SToomas Soome 	EFI_TPL tpl;
7903630506bSToomas Soome 
7913ddf7eadSToomas Soome 	/*
792305ef653SWarner Losh 	 * We assume Blt() does work, if not, we will need to build exception
793305ef653SWarner Losh 	 * list case by case. We only have boot services during part of our
794305ef653SWarner Losh 	 * exectution. Once terminate boot services, these operations cannot be
795305ef653SWarner Losh 	 * done as they are provided by protocols that disappear when exit
796305ef653SWarner Losh 	 * boot services.
7973ddf7eadSToomas Soome 	 */
798305ef653SWarner Losh 	if (gop != NULL && boot_services_active) {
7994c7a3a70SToomas Soome 		tpl = BS->RaiseTPL(TPL_NOTIFY);
8003630506bSToomas Soome 		switch (BltOperation) {
8013630506bSToomas Soome 		case GfxFbBltVideoFill:
8026102f43cSToomas Soome 			gfxfb_shadow_fill(BltBuffer, DestinationX,
8036102f43cSToomas Soome 			    DestinationY, Width, Height);
8043630506bSToomas Soome 			status = gop->Blt(gop, BltBuffer, EfiBltVideoFill,
8053630506bSToomas Soome 			    SourceX, SourceY, DestinationX, DestinationY,
8063630506bSToomas Soome 			    Width, Height, Delta);
8073630506bSToomas Soome 			break;
8083630506bSToomas Soome 
8093630506bSToomas Soome 		case GfxFbBltVideoToBltBuffer:
8103630506bSToomas Soome 			status = gop->Blt(gop, BltBuffer,
8113630506bSToomas Soome 			    EfiBltVideoToBltBuffer,
8123630506bSToomas Soome 			    SourceX, SourceY, DestinationX, DestinationY,
8133630506bSToomas Soome 			    Width, Height, Delta);
8143630506bSToomas Soome 			break;
8153630506bSToomas Soome 
8163630506bSToomas Soome 		case GfxFbBltBufferToVideo:
8173630506bSToomas Soome 			status = gop->Blt(gop, BltBuffer, EfiBltBufferToVideo,
8183630506bSToomas Soome 			    SourceX, SourceY, DestinationX, DestinationY,
8193630506bSToomas Soome 			    Width, Height, Delta);
8203630506bSToomas Soome 			break;
8213630506bSToomas Soome 
8223630506bSToomas Soome 		case GfxFbBltVideoToVideo:
8233630506bSToomas Soome 			status = gop->Blt(gop, BltBuffer, EfiBltVideoToVideo,
8243630506bSToomas Soome 			    SourceX, SourceY, DestinationX, DestinationY,
8253630506bSToomas Soome 			    Width, Height, Delta);
8263630506bSToomas Soome 			break;
8273630506bSToomas Soome 
8283630506bSToomas Soome 		default:
8293630506bSToomas Soome 			status = EFI_INVALID_PARAMETER;
8303630506bSToomas Soome 			break;
8313630506bSToomas Soome 		}
8323630506bSToomas Soome 
8333630506bSToomas Soome 		switch (status) {
8343630506bSToomas Soome 		case EFI_SUCCESS:
8353630506bSToomas Soome 			rv = 0;
8363630506bSToomas Soome 			break;
8373630506bSToomas Soome 
8383630506bSToomas Soome 		case EFI_INVALID_PARAMETER:
8393630506bSToomas Soome 			rv = EINVAL;
8403630506bSToomas Soome 			break;
8413630506bSToomas Soome 
8423630506bSToomas Soome 		case EFI_DEVICE_ERROR:
8433630506bSToomas Soome 		default:
8443630506bSToomas Soome 			rv = EIO;
8453630506bSToomas Soome 			break;
8463630506bSToomas Soome 		}
8473630506bSToomas Soome 
8484c7a3a70SToomas Soome 		BS->RestoreTPL(tpl);
8493630506bSToomas Soome 		return (rv);
8503630506bSToomas Soome 	}
8513630506bSToomas Soome #endif
8523630506bSToomas Soome 
8533630506bSToomas Soome 	switch (BltOperation) {
8543630506bSToomas Soome 	case GfxFbBltVideoFill:
8556102f43cSToomas Soome 		gfxfb_shadow_fill(BltBuffer, DestinationX, DestinationY,
8566102f43cSToomas Soome 		    Width, Height);
8573630506bSToomas Soome 		rv = gfxfb_blt_fill(BltBuffer, DestinationX, DestinationY,
8583630506bSToomas Soome 		    Width, Height);
8593630506bSToomas Soome 		break;
8603630506bSToomas Soome 
8613630506bSToomas Soome 	case GfxFbBltVideoToBltBuffer:
8623630506bSToomas Soome 		rv = gfxfb_blt_video_to_buffer(BltBuffer, SourceX, SourceY,
8633630506bSToomas Soome 		    DestinationX, DestinationY, Width, Height, Delta);
8643630506bSToomas Soome 		break;
8653630506bSToomas Soome 
8663630506bSToomas Soome 	case GfxFbBltBufferToVideo:
8673630506bSToomas Soome 		rv = gfxfb_blt_buffer_to_video(BltBuffer, SourceX, SourceY,
8683630506bSToomas Soome 		    DestinationX, DestinationY, Width, Height, Delta);
8693630506bSToomas Soome 		break;
8703630506bSToomas Soome 
8713630506bSToomas Soome 	case GfxFbBltVideoToVideo:
8723630506bSToomas Soome 		rv = gfxfb_blt_video_to_video(SourceX, SourceY,
8733630506bSToomas Soome 		    DestinationX, DestinationY, Width, Height);
8743630506bSToomas Soome 		break;
8753630506bSToomas Soome 
8763630506bSToomas Soome 	default:
8773630506bSToomas Soome 		rv = EINVAL;
8783630506bSToomas Soome 		break;
8793630506bSToomas Soome 	}
8803630506bSToomas Soome 	return (rv);
8813630506bSToomas Soome }
8823630506bSToomas Soome 
8833630506bSToomas Soome void
gfx_bitblt_bitmap(teken_gfx_t * state,const uint8_t * glyph,const teken_attr_t * a,uint32_t alpha,bool cursor)8843630506bSToomas Soome gfx_bitblt_bitmap(teken_gfx_t *state, const uint8_t *glyph,
8853630506bSToomas Soome     const teken_attr_t *a, uint32_t alpha, bool cursor)
8863630506bSToomas Soome {
8873630506bSToomas Soome 	uint32_t width, height;
8883630506bSToomas Soome 	uint32_t fgc, bgc, bpl, cc, o;
8893630506bSToomas Soome 	int bpp, bit, byte;
8903630506bSToomas Soome 	bool invert = false;
8913630506bSToomas Soome 
8923630506bSToomas Soome 	bpp = 4;		/* We only generate BGRA */
8933630506bSToomas Soome 	width = state->tg_font.vf_width;
8943630506bSToomas Soome 	height = state->tg_font.vf_height;
8953630506bSToomas Soome 	bpl = (width + 7) / 8;  /* Bytes per source line. */
8963630506bSToomas Soome 
8973630506bSToomas Soome 	fgc = a->ta_fgcolor;
8983630506bSToomas Soome 	bgc = a->ta_bgcolor;
8993630506bSToomas Soome 	if (a->ta_format & TF_BOLD)
9003630506bSToomas Soome 		fgc |= TC_LIGHT;
9013630506bSToomas Soome 	if (a->ta_format & TF_BLINK)
9023630506bSToomas Soome 		bgc |= TC_LIGHT;
9033630506bSToomas Soome 
9043630506bSToomas Soome 	fgc = gfx_fb_color_map(fgc);
9053630506bSToomas Soome 	bgc = gfx_fb_color_map(bgc);
9063630506bSToomas Soome 
9073630506bSToomas Soome 	if (a->ta_format & TF_REVERSE)
9083630506bSToomas Soome 		invert = !invert;
9093630506bSToomas Soome 	if (cursor)
9103630506bSToomas Soome 		invert = !invert;
9113630506bSToomas Soome 	if (invert) {
9123630506bSToomas Soome 		uint32_t tmp;
9133630506bSToomas Soome 
9143630506bSToomas Soome 		tmp = fgc;
9153630506bSToomas Soome 		fgc = bgc;
9163630506bSToomas Soome 		bgc = tmp;
9173630506bSToomas Soome 	}
9183630506bSToomas Soome 
9193630506bSToomas Soome 	alpha = alpha << 24;
9203630506bSToomas Soome 	fgc |= alpha;
9213630506bSToomas Soome 	bgc |= alpha;
9223630506bSToomas Soome 
9233630506bSToomas Soome 	for (uint32_t y = 0; y < height; y++) {
9243630506bSToomas Soome 		for (uint32_t x = 0; x < width; x++) {
9253630506bSToomas Soome 			byte = y * bpl + x / 8;
9263630506bSToomas Soome 			bit = 0x80 >> (x % 8);
9273630506bSToomas Soome 			o = y * width * bpp + x * bpp;
9283630506bSToomas Soome 			cc = glyph[byte] & bit ? fgc : bgc;
9293630506bSToomas Soome 
9303630506bSToomas Soome 			gfx_mem_wr4(state->tg_glyph,
9313630506bSToomas Soome 			    state->tg_glyph_size, o, cc);
9323630506bSToomas Soome 		}
9333630506bSToomas Soome 	}
9343630506bSToomas Soome }
9353630506bSToomas Soome 
9363630506bSToomas Soome /*
9373630506bSToomas Soome  * Draw prepared glyph on terminal point p.
9383630506bSToomas Soome  */
9393630506bSToomas Soome static void
gfx_fb_printchar(teken_gfx_t * state,const teken_pos_t * p)9403630506bSToomas Soome gfx_fb_printchar(teken_gfx_t *state, const teken_pos_t *p)
9413630506bSToomas Soome {
9423630506bSToomas Soome 	unsigned x, y, width, height;
9433630506bSToomas Soome 
9443630506bSToomas Soome 	width = state->tg_font.vf_width;
9453630506bSToomas Soome 	height = state->tg_font.vf_height;
9463630506bSToomas Soome 	x = state->tg_origin.tp_col + p->tp_col * width;
9473630506bSToomas Soome 	y = state->tg_origin.tp_row + p->tp_row * height;
9483630506bSToomas Soome 
9493630506bSToomas Soome 	gfx_fb_cons_display(x, y, width, height, state->tg_glyph);
9503630506bSToomas Soome }
9513630506bSToomas Soome 
9523630506bSToomas Soome /*
9533630506bSToomas Soome  * Store char with its attribute to buffer and put it on screen.
9543630506bSToomas Soome  */
9553630506bSToomas Soome void
gfx_fb_putchar(void * arg,const teken_pos_t * p,teken_char_t c,const teken_attr_t * a)9563630506bSToomas Soome gfx_fb_putchar(void *arg, const teken_pos_t *p, teken_char_t c,
9573630506bSToomas Soome     const teken_attr_t *a)
9583630506bSToomas Soome {
9593630506bSToomas Soome 	teken_gfx_t *state = arg;
9603630506bSToomas Soome 	const uint8_t *glyph;
9613630506bSToomas Soome 	int idx;
9623630506bSToomas Soome 
9633630506bSToomas Soome 	idx = p->tp_col + p->tp_row * state->tg_tp.tp_col;
9643630506bSToomas Soome 	if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row)
9653630506bSToomas Soome 		return;
9663630506bSToomas Soome 
9673630506bSToomas Soome 	/* remove the cursor */
9683630506bSToomas Soome 	if (state->tg_cursor_visible)
9693630506bSToomas Soome 		gfx_fb_cursor_draw(state, &state->tg_cursor, false);
9703630506bSToomas Soome 
9713630506bSToomas Soome 	screen_buffer[idx].c = c;
9723630506bSToomas Soome 	screen_buffer[idx].a = *a;
9733630506bSToomas Soome 
9743630506bSToomas Soome 	glyph = font_lookup(&state->tg_font, c, a);
9753630506bSToomas Soome 	gfx_bitblt_bitmap(state, glyph, a, 0xff, false);
9763630506bSToomas Soome 	gfx_fb_printchar(state, p);
9773630506bSToomas Soome 
9783630506bSToomas Soome 	/* display the cursor */
9793630506bSToomas Soome 	if (state->tg_cursor_visible) {
9803630506bSToomas Soome 		const teken_pos_t *c;
9813630506bSToomas Soome 
9823630506bSToomas Soome 		c = teken_get_cursor(&state->tg_teken);
9833630506bSToomas Soome 		gfx_fb_cursor_draw(state, c, true);
9843630506bSToomas Soome 	}
9853630506bSToomas Soome }
9863630506bSToomas Soome 
9873630506bSToomas Soome void
gfx_fb_fill(void * arg,const teken_rect_t * r,teken_char_t c,const teken_attr_t * a)9883630506bSToomas Soome gfx_fb_fill(void *arg, const teken_rect_t *r, teken_char_t c,
9893630506bSToomas Soome     const teken_attr_t *a)
9903630506bSToomas Soome {
9913630506bSToomas Soome 	teken_gfx_t *state = arg;
9923630506bSToomas Soome 	const uint8_t *glyph;
9933630506bSToomas Soome 	teken_pos_t p;
9943630506bSToomas Soome 	struct text_pixel *row;
9953630506bSToomas Soome 
9963630506bSToomas Soome 	/* remove the cursor */
9973630506bSToomas Soome 	if (state->tg_cursor_visible)
9983630506bSToomas Soome 		gfx_fb_cursor_draw(state, &state->tg_cursor, false);
9993630506bSToomas Soome 
10003630506bSToomas Soome 	glyph = font_lookup(&state->tg_font, c, a);
10013630506bSToomas Soome 	gfx_bitblt_bitmap(state, glyph, a, 0xff, false);
10023630506bSToomas Soome 
10033630506bSToomas Soome 	for (p.tp_row = r->tr_begin.tp_row; p.tp_row < r->tr_end.tp_row;
10043630506bSToomas Soome 	    p.tp_row++) {
10053630506bSToomas Soome 		row = &screen_buffer[p.tp_row * state->tg_tp.tp_col];
10063630506bSToomas Soome 		for (p.tp_col = r->tr_begin.tp_col;
10073630506bSToomas Soome 		    p.tp_col < r->tr_end.tp_col; p.tp_col++) {
10083630506bSToomas Soome 			row[p.tp_col].c = c;
10093630506bSToomas Soome 			row[p.tp_col].a = *a;
10103630506bSToomas Soome 			gfx_fb_printchar(state, &p);
10113630506bSToomas Soome 		}
10123630506bSToomas Soome 	}
10133630506bSToomas Soome 
10143630506bSToomas Soome 	/* display the cursor */
10153630506bSToomas Soome 	if (state->tg_cursor_visible) {
10163630506bSToomas Soome 		const teken_pos_t *c;
10173630506bSToomas Soome 
10183630506bSToomas Soome 		c = teken_get_cursor(&state->tg_teken);
10193630506bSToomas Soome 		gfx_fb_cursor_draw(state, c, true);
10203630506bSToomas Soome 	}
10213630506bSToomas Soome }
10223630506bSToomas Soome 
10233630506bSToomas Soome static void
gfx_fb_cursor_draw(teken_gfx_t * state,const teken_pos_t * pos,bool on)1024e5a50b03SToomas Soome gfx_fb_cursor_draw(teken_gfx_t *state, const teken_pos_t *pos, bool on)
10253630506bSToomas Soome {
10263630506bSToomas Soome 	const uint8_t *glyph;
1027e5a50b03SToomas Soome 	teken_pos_t p;
10283630506bSToomas Soome 	int idx;
10293630506bSToomas Soome 
1030e5a50b03SToomas Soome 	p = *pos;
1031e5a50b03SToomas Soome 	if (p.tp_col >= state->tg_tp.tp_col)
1032e5a50b03SToomas Soome 		p.tp_col = state->tg_tp.tp_col - 1;
1033e5a50b03SToomas Soome 	if (p.tp_row >= state->tg_tp.tp_row)
1034e5a50b03SToomas Soome 		p.tp_row = state->tg_tp.tp_row - 1;
1035e5a50b03SToomas Soome 	idx = p.tp_col + p.tp_row * state->tg_tp.tp_col;
10363630506bSToomas Soome 	if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row)
10373630506bSToomas Soome 		return;
10383630506bSToomas Soome 
10393630506bSToomas Soome 	glyph = font_lookup(&state->tg_font, screen_buffer[idx].c,
10403630506bSToomas Soome 	    &screen_buffer[idx].a);
10413630506bSToomas Soome 	gfx_bitblt_bitmap(state, glyph, &screen_buffer[idx].a, 0xff, on);
1042e5a50b03SToomas Soome 	gfx_fb_printchar(state, &p);
1043d708f23eSToomas Soome 
1044e5a50b03SToomas Soome 	state->tg_cursor = p;
10453630506bSToomas Soome }
10463630506bSToomas Soome 
10473630506bSToomas Soome void
gfx_fb_cursor(void * arg,const teken_pos_t * p)10483630506bSToomas Soome gfx_fb_cursor(void *arg, const teken_pos_t *p)
10493630506bSToomas Soome {
10503630506bSToomas Soome 	teken_gfx_t *state = arg;
10513630506bSToomas Soome 
10523630506bSToomas Soome 	/* Switch cursor off in old location and back on in new. */
10533630506bSToomas Soome 	if (state->tg_cursor_visible) {
10543630506bSToomas Soome 		gfx_fb_cursor_draw(state, &state->tg_cursor, false);
10553630506bSToomas Soome 		gfx_fb_cursor_draw(state, p, true);
10563630506bSToomas Soome 	}
10573630506bSToomas Soome }
10583630506bSToomas Soome 
10593630506bSToomas Soome void
gfx_fb_param(void * arg,int cmd,unsigned int value)10603630506bSToomas Soome gfx_fb_param(void *arg, int cmd, unsigned int value)
10613630506bSToomas Soome {
10623630506bSToomas Soome 	teken_gfx_t *state = arg;
10633630506bSToomas Soome 	const teken_pos_t *c;
10643630506bSToomas Soome 
10653630506bSToomas Soome 	switch (cmd) {
10663630506bSToomas Soome 	case TP_SETLOCALCURSOR:
10673630506bSToomas Soome 		/*
10683630506bSToomas Soome 		 * 0 means normal (usually block), 1 means hidden, and
10693630506bSToomas Soome 		 * 2 means blinking (always block) for compatibility with
10703630506bSToomas Soome 		 * syscons.  We don't support any changes except hiding,
10713630506bSToomas Soome 		 * so must map 2 to 0.
10723630506bSToomas Soome 		 */
10733630506bSToomas Soome 		value = (value == 1) ? 0 : 1;
10743630506bSToomas Soome 		/* FALLTHROUGH */
10753630506bSToomas Soome 	case TP_SHOWCURSOR:
10763630506bSToomas Soome 		c = teken_get_cursor(&state->tg_teken);
10773630506bSToomas Soome 		gfx_fb_cursor_draw(state, c, true);
10783630506bSToomas Soome 		if (value != 0)
10793630506bSToomas Soome 			state->tg_cursor_visible = true;
10803630506bSToomas Soome 		else
10813630506bSToomas Soome 			state->tg_cursor_visible = false;
10823630506bSToomas Soome 		break;
10833630506bSToomas Soome 	default:
10843630506bSToomas Soome 		/* Not yet implemented */
10853630506bSToomas Soome 		break;
10863630506bSToomas Soome 	}
10873630506bSToomas Soome }
10883630506bSToomas Soome 
10893630506bSToomas Soome bool
is_same_pixel(struct text_pixel * px1,struct text_pixel * px2)10903630506bSToomas Soome is_same_pixel(struct text_pixel *px1, struct text_pixel *px2)
10913630506bSToomas Soome {
10923630506bSToomas Soome 	if (px1->c != px2->c)
10933630506bSToomas Soome 		return (false);
10943630506bSToomas Soome 
10953630506bSToomas Soome 	/* Is there image stored? */
10963630506bSToomas Soome 	if ((px1->a.ta_format & TF_IMAGE) ||
10973630506bSToomas Soome 	    (px2->a.ta_format & TF_IMAGE))
10983630506bSToomas Soome 		return (false);
10993630506bSToomas Soome 
11003630506bSToomas Soome 	if (px1->a.ta_format != px2->a.ta_format)
11013630506bSToomas Soome 		return (false);
11023630506bSToomas Soome 	if (px1->a.ta_fgcolor != px2->a.ta_fgcolor)
11033630506bSToomas Soome 		return (false);
11043630506bSToomas Soome 	if (px1->a.ta_bgcolor != px2->a.ta_bgcolor)
11053630506bSToomas Soome 		return (false);
11063630506bSToomas Soome 
11073630506bSToomas Soome 	return (true);
11083630506bSToomas Soome }
11093630506bSToomas Soome 
11103630506bSToomas Soome static void
gfx_fb_copy_area(teken_gfx_t * state,const teken_rect_t * s,const teken_pos_t * d)11113630506bSToomas Soome gfx_fb_copy_area(teken_gfx_t *state, const teken_rect_t *s,
11123630506bSToomas Soome     const teken_pos_t *d)
11133630506bSToomas Soome {
11143630506bSToomas Soome 	uint32_t sx, sy, dx, dy, width, height;
11156102f43cSToomas Soome 	uint32_t pitch, bytes;
11166102f43cSToomas Soome 	int step;
11173630506bSToomas Soome 
11183630506bSToomas Soome 	width = state->tg_font.vf_width;
11193630506bSToomas Soome 	height = state->tg_font.vf_height;
11203630506bSToomas Soome 
11216102f43cSToomas Soome 	sx = s->tr_begin.tp_col * width;
11226102f43cSToomas Soome 	sy = s->tr_begin.tp_row * height;
11236102f43cSToomas Soome 	dx = d->tp_col * width;
11246102f43cSToomas Soome 	dy = d->tp_row * height;
11253630506bSToomas Soome 
11263630506bSToomas Soome 	width *= (s->tr_end.tp_col - s->tr_begin.tp_col + 1);
11273630506bSToomas Soome 
11286102f43cSToomas Soome 	/*
11296102f43cSToomas Soome 	 * With no shadow fb, use video to video copy.
11306102f43cSToomas Soome 	 */
11316102f43cSToomas Soome 	if (state->tg_shadow_fb == NULL) {
11326102f43cSToomas Soome 		(void) gfxfb_blt(NULL, GfxFbBltVideoToVideo,
11336102f43cSToomas Soome 		    sx + state->tg_origin.tp_col,
11346102f43cSToomas Soome 		    sy + state->tg_origin.tp_row,
11356102f43cSToomas Soome 		    dx + state->tg_origin.tp_col,
11366102f43cSToomas Soome 		    dy + state->tg_origin.tp_row,
11373630506bSToomas Soome 		    width, height, 0);
11386102f43cSToomas Soome 		return;
11396102f43cSToomas Soome 	}
11406102f43cSToomas Soome 
11416102f43cSToomas Soome 	/*
11426102f43cSToomas Soome 	 * With shadow fb, we need to copy data on both shadow and video,
11436102f43cSToomas Soome 	 * to preserve the consistency. We only read data from shadow fb.
11446102f43cSToomas Soome 	 */
11456102f43cSToomas Soome 
11466102f43cSToomas Soome 	step = 1;
11476102f43cSToomas Soome 	pitch = state->tg_fb.fb_width;
11486102f43cSToomas Soome 	bytes = width * sizeof (*state->tg_shadow_fb);
11496102f43cSToomas Soome 
11506102f43cSToomas Soome 	/*
11516102f43cSToomas Soome 	 * To handle overlapping areas, set up reverse copy here.
11526102f43cSToomas Soome 	 */
11536102f43cSToomas Soome 	if (dy * pitch + dx > sy * pitch + sx) {
11546102f43cSToomas Soome 		sy += height;
11556102f43cSToomas Soome 		dy += height;
11566102f43cSToomas Soome 		step = -step;
11576102f43cSToomas Soome 	}
11586102f43cSToomas Soome 
11596102f43cSToomas Soome 	while (height-- > 0) {
11606102f43cSToomas Soome 		uint32_t *source = &state->tg_shadow_fb[sy * pitch + sx];
11616102f43cSToomas Soome 		uint32_t *destination = &state->tg_shadow_fb[dy * pitch + dx];
11626102f43cSToomas Soome 
11636102f43cSToomas Soome 		bcopy(source, destination, bytes);
11646102f43cSToomas Soome 		(void) gfxfb_blt(destination, GfxFbBltBufferToVideo,
11656102f43cSToomas Soome 		    0, 0, dx + state->tg_origin.tp_col,
11666102f43cSToomas Soome 		    dy + state->tg_origin.tp_row, width, 1, 0);
11676102f43cSToomas Soome 
11686102f43cSToomas Soome 		sy += step;
11696102f43cSToomas Soome 		dy += step;
11706102f43cSToomas Soome 	}
11713630506bSToomas Soome }
11723630506bSToomas Soome 
11733630506bSToomas Soome static void
gfx_fb_copy_line(teken_gfx_t * state,int ncol,teken_pos_t * s,teken_pos_t * d)11743630506bSToomas Soome gfx_fb_copy_line(teken_gfx_t *state, int ncol, teken_pos_t *s, teken_pos_t *d)
11753630506bSToomas Soome {
11763630506bSToomas Soome 	teken_rect_t sr;
11773630506bSToomas Soome 	teken_pos_t dp;
11783630506bSToomas Soome 	unsigned soffset, doffset;
11793630506bSToomas Soome 	bool mark = false;
11803630506bSToomas Soome 	int x;
11813630506bSToomas Soome 
11823630506bSToomas Soome 	soffset = s->tp_col + s->tp_row * state->tg_tp.tp_col;
11833630506bSToomas Soome 	doffset = d->tp_col + d->tp_row * state->tg_tp.tp_col;
11843630506bSToomas Soome 
11853630506bSToomas Soome 	for (x = 0; x < ncol; x++) {
11863630506bSToomas Soome 		if (is_same_pixel(&screen_buffer[soffset + x],
11873630506bSToomas Soome 		    &screen_buffer[doffset + x])) {
11883630506bSToomas Soome 			if (mark) {
11893630506bSToomas Soome 				gfx_fb_copy_area(state, &sr, &dp);
11903630506bSToomas Soome 				mark = false;
11913630506bSToomas Soome 			}
11923630506bSToomas Soome 		} else {
11933630506bSToomas Soome 			screen_buffer[doffset + x] = screen_buffer[soffset + x];
11943630506bSToomas Soome 			if (mark) {
11953630506bSToomas Soome 				/* update end point */
1196798ea06fSElliott Mitchell 				sr.tr_end.tp_col = s->tp_col + x;
11973630506bSToomas Soome 			} else {
11983630506bSToomas Soome 				/* set up new rectangle */
11993630506bSToomas Soome 				mark = true;
12003630506bSToomas Soome 				sr.tr_begin.tp_col = s->tp_col + x;
12013630506bSToomas Soome 				sr.tr_begin.tp_row = s->tp_row;
12023630506bSToomas Soome 				sr.tr_end.tp_col = s->tp_col + x;
12033630506bSToomas Soome 				sr.tr_end.tp_row = s->tp_row;
12043630506bSToomas Soome 				dp.tp_col = d->tp_col + x;
12053630506bSToomas Soome 				dp.tp_row = d->tp_row;
12063630506bSToomas Soome 			}
12073630506bSToomas Soome 		}
12083630506bSToomas Soome 	}
12093630506bSToomas Soome 	if (mark) {
12103630506bSToomas Soome 		gfx_fb_copy_area(state, &sr, &dp);
12113630506bSToomas Soome 	}
12123630506bSToomas Soome }
12133630506bSToomas Soome 
12143630506bSToomas Soome void
gfx_fb_copy(void * arg,const teken_rect_t * r,const teken_pos_t * p)12153630506bSToomas Soome gfx_fb_copy(void *arg, const teken_rect_t *r, const teken_pos_t *p)
12163630506bSToomas Soome {
12173630506bSToomas Soome 	teken_gfx_t *state = arg;
12183630506bSToomas Soome 	unsigned doffset, soffset;
12193630506bSToomas Soome 	teken_pos_t d, s;
12203630506bSToomas Soome 	int nrow, ncol, y; /* Has to be signed - >= 0 comparison */
12213630506bSToomas Soome 
12223630506bSToomas Soome 	/*
12233630506bSToomas Soome 	 * Copying is a little tricky. We must make sure we do it in
12243630506bSToomas Soome 	 * correct order, to make sure we don't overwrite our own data.
12253630506bSToomas Soome 	 */
12263630506bSToomas Soome 
12273630506bSToomas Soome 	nrow = r->tr_end.tp_row - r->tr_begin.tp_row;
12283630506bSToomas Soome 	ncol = r->tr_end.tp_col - r->tr_begin.tp_col;
12293630506bSToomas Soome 
12303630506bSToomas Soome 	if (p->tp_row + nrow > state->tg_tp.tp_row ||
12313630506bSToomas Soome 	    p->tp_col + ncol > state->tg_tp.tp_col)
12323630506bSToomas Soome 		return;
12333630506bSToomas Soome 
12343630506bSToomas Soome 	soffset = r->tr_begin.tp_col + r->tr_begin.tp_row * state->tg_tp.tp_col;
12353630506bSToomas Soome 	doffset = p->tp_col + p->tp_row * state->tg_tp.tp_col;
12363630506bSToomas Soome 
12373630506bSToomas Soome 	/* remove the cursor */
12383630506bSToomas Soome 	if (state->tg_cursor_visible)
12393630506bSToomas Soome 		gfx_fb_cursor_draw(state, &state->tg_cursor, false);
12403630506bSToomas Soome 
12413630506bSToomas Soome 	/*
12423630506bSToomas Soome 	 * Copy line by line.
12433630506bSToomas Soome 	 */
12443630506bSToomas Soome 	if (doffset <= soffset) {
12453630506bSToomas Soome 		s = r->tr_begin;
12463630506bSToomas Soome 		d = *p;
12473630506bSToomas Soome 		for (y = 0; y < nrow; y++) {
12483630506bSToomas Soome 			s.tp_row = r->tr_begin.tp_row + y;
12493630506bSToomas Soome 			d.tp_row = p->tp_row + y;
12503630506bSToomas Soome 
12513630506bSToomas Soome 			gfx_fb_copy_line(state, ncol, &s, &d);
12523630506bSToomas Soome 		}
12533630506bSToomas Soome 	} else {
12543630506bSToomas Soome 		for (y = nrow - 1; y >= 0; y--) {
12553630506bSToomas Soome 			s.tp_row = r->tr_begin.tp_row + y;
12563630506bSToomas Soome 			d.tp_row = p->tp_row + y;
12573630506bSToomas Soome 
12583630506bSToomas Soome 			gfx_fb_copy_line(state, ncol, &s, &d);
12593630506bSToomas Soome 		}
12603630506bSToomas Soome 	}
12613630506bSToomas Soome 
12623630506bSToomas Soome 	/* display the cursor */
12633630506bSToomas Soome 	if (state->tg_cursor_visible) {
12643630506bSToomas Soome 		const teken_pos_t *c;
12653630506bSToomas Soome 
12663630506bSToomas Soome 		c = teken_get_cursor(&state->tg_teken);
12673630506bSToomas Soome 		gfx_fb_cursor_draw(state, c, true);
12683630506bSToomas Soome 	}
12693630506bSToomas Soome }
12703630506bSToomas Soome 
12713630506bSToomas Soome /*
12723630506bSToomas Soome  * Implements alpha blending for RGBA data, could use pixels for arguments,
12733630506bSToomas Soome  * but byte stream seems more generic.
12743630506bSToomas Soome  * The generic alpha blending is:
12753630506bSToomas Soome  * blend = alpha * fg + (1.0 - alpha) * bg.
12763630506bSToomas Soome  * Since our alpha is not from range [0..1], we scale appropriately.
12773630506bSToomas Soome  */
12783630506bSToomas Soome static uint8_t
alpha_blend(uint8_t fg,uint8_t bg,uint8_t alpha)12793630506bSToomas Soome alpha_blend(uint8_t fg, uint8_t bg, uint8_t alpha)
12803630506bSToomas Soome {
12813630506bSToomas Soome 	uint16_t blend, h, l;
12823630506bSToomas Soome 
12833630506bSToomas Soome 	/* trivial corner cases */
12843630506bSToomas Soome 	if (alpha == 0)
12853630506bSToomas Soome 		return (bg);
12863630506bSToomas Soome 	if (alpha == 0xFF)
12873630506bSToomas Soome 		return (fg);
12883630506bSToomas Soome 	blend = (alpha * fg + (0xFF - alpha) * bg);
12893630506bSToomas Soome 	/* Division by 0xFF */
12903630506bSToomas Soome 	h = blend >> 8;
12913630506bSToomas Soome 	l = blend & 0xFF;
12923630506bSToomas Soome 	if (h + l >= 0xFF)
12933630506bSToomas Soome 		h++;
12943630506bSToomas Soome 	return (h);
12953630506bSToomas Soome }
12963630506bSToomas Soome 
12973630506bSToomas Soome /*
12983630506bSToomas Soome  * Implements alpha blending for RGBA data, could use pixels for arguments,
12993630506bSToomas Soome  * but byte stream seems more generic.
13003630506bSToomas Soome  * The generic alpha blending is:
13013630506bSToomas Soome  * blend = alpha * fg + (1.0 - alpha) * bg.
13023630506bSToomas Soome  * Since our alpha is not from range [0..1], we scale appropriately.
13033630506bSToomas Soome  */
13043630506bSToomas Soome static void
bitmap_cpy(void * dst,void * src,uint32_t size)13053630506bSToomas Soome bitmap_cpy(void *dst, void *src, uint32_t size)
13063630506bSToomas Soome {
13073630506bSToomas Soome #if defined(EFI)
13083630506bSToomas Soome 	EFI_GRAPHICS_OUTPUT_BLT_PIXEL *ps, *pd;
13093630506bSToomas Soome #else
13103630506bSToomas Soome 	struct paletteentry *ps, *pd;
13113630506bSToomas Soome #endif
13123630506bSToomas Soome 	uint32_t i;
13133630506bSToomas Soome 	uint8_t a;
13143630506bSToomas Soome 
13153630506bSToomas Soome 	ps = src;
13163630506bSToomas Soome 	pd = dst;
13173630506bSToomas Soome 
13183630506bSToomas Soome 	/*
13193630506bSToomas Soome 	 * we only implement alpha blending for depth 32.
13203630506bSToomas Soome 	 */
13213630506bSToomas Soome 	for (i = 0; i < size; i ++) {
13223630506bSToomas Soome 		a = ps[i].Reserved;
13233630506bSToomas Soome 		pd[i].Red = alpha_blend(ps[i].Red, pd[i].Red, a);
13243630506bSToomas Soome 		pd[i].Green = alpha_blend(ps[i].Green, pd[i].Green, a);
13253630506bSToomas Soome 		pd[i].Blue = alpha_blend(ps[i].Blue, pd[i].Blue, a);
13263630506bSToomas Soome 		pd[i].Reserved = a;
13273630506bSToomas Soome 	}
13283630506bSToomas Soome }
13293630506bSToomas Soome 
13303630506bSToomas Soome static void *
allocate_glyphbuffer(uint32_t width,uint32_t height)13313630506bSToomas Soome allocate_glyphbuffer(uint32_t width, uint32_t height)
13323630506bSToomas Soome {
13333630506bSToomas Soome 	size_t size;
13343630506bSToomas Soome 
13353630506bSToomas Soome 	size = sizeof (*GlyphBuffer) * width * height;
13363630506bSToomas Soome 	if (size != GlyphBufferSize) {
13373630506bSToomas Soome 		free(GlyphBuffer);
13383630506bSToomas Soome 		GlyphBuffer = malloc(size);
13393630506bSToomas Soome 		if (GlyphBuffer == NULL)
13403630506bSToomas Soome 			return (NULL);
13413630506bSToomas Soome 		GlyphBufferSize = size;
13423630506bSToomas Soome 	}
13433630506bSToomas Soome 	return (GlyphBuffer);
13443630506bSToomas Soome }
13453630506bSToomas Soome 
13463630506bSToomas Soome void
gfx_fb_cons_display(uint32_t x,uint32_t y,uint32_t width,uint32_t height,void * data)13473630506bSToomas Soome gfx_fb_cons_display(uint32_t x, uint32_t y, uint32_t width, uint32_t height,
13483630506bSToomas Soome     void *data)
13493630506bSToomas Soome {
13503630506bSToomas Soome #if defined(EFI)
13516102f43cSToomas Soome 	EFI_GRAPHICS_OUTPUT_BLT_PIXEL *buf, *p;
13523630506bSToomas Soome #else
13536102f43cSToomas Soome 	struct paletteentry *buf, *p;
13543630506bSToomas Soome #endif
13553630506bSToomas Soome 	size_t size;
13563630506bSToomas Soome 
13576102f43cSToomas Soome 	/*
13586102f43cSToomas Soome 	 * If we do have shadow fb, we will use shadow to render data,
13596102f43cSToomas Soome 	 * and copy shadow to video.
13606102f43cSToomas Soome 	 */
13616102f43cSToomas Soome 	if (gfx_state.tg_shadow_fb != NULL) {
13626102f43cSToomas Soome 		uint32_t pitch = gfx_state.tg_fb.fb_width;
13636102f43cSToomas Soome 
13646102f43cSToomas Soome 		/* Copy rectangle line by line. */
13656102f43cSToomas Soome 		p = data;
13666102f43cSToomas Soome 		for (uint32_t sy = 0; sy < height; sy++) {
13676102f43cSToomas Soome 			buf = (void *)(gfx_state.tg_shadow_fb +
13686102f43cSToomas Soome 			    (y - gfx_state.tg_origin.tp_row) * pitch +
13696102f43cSToomas Soome 			    x - gfx_state.tg_origin.tp_col);
13706102f43cSToomas Soome 			bitmap_cpy(buf, &p[sy * width], width);
13716102f43cSToomas Soome 			(void) gfxfb_blt(buf, GfxFbBltBufferToVideo,
13726102f43cSToomas Soome 			    0, 0, x, y, width, 1, 0);
13736102f43cSToomas Soome 			y++;
13746102f43cSToomas Soome 		}
13756102f43cSToomas Soome 		return;
13766102f43cSToomas Soome 	}
13773630506bSToomas Soome 
13783630506bSToomas Soome 	/*
13793630506bSToomas Soome 	 * Common data to display is glyph, use preallocated
13803630506bSToomas Soome 	 * glyph buffer.
13813630506bSToomas Soome 	 */
13823630506bSToomas Soome         if (gfx_state.tg_glyph_size != GlyphBufferSize)
13833630506bSToomas Soome                 (void) allocate_glyphbuffer(width, height);
13843630506bSToomas Soome 
13856102f43cSToomas Soome 	size = width * height * sizeof(*buf);
13863630506bSToomas Soome 	if (size == GlyphBufferSize)
13873630506bSToomas Soome 		buf = GlyphBuffer;
13883630506bSToomas Soome 	else
13893630506bSToomas Soome 		buf = malloc(size);
13903630506bSToomas Soome 	if (buf == NULL)
13913630506bSToomas Soome 		return;
13923630506bSToomas Soome 
13933630506bSToomas Soome 	if (gfxfb_blt(buf, GfxFbBltVideoToBltBuffer, x, y, 0, 0,
13943630506bSToomas Soome 	    width, height, 0) == 0) {
13953630506bSToomas Soome 		bitmap_cpy(buf, data, width * height);
13963630506bSToomas Soome 		(void) gfxfb_blt(buf, GfxFbBltBufferToVideo, 0, 0, x, y,
13973630506bSToomas Soome 		    width, height, 0);
13983630506bSToomas Soome 	}
13993630506bSToomas Soome 	if (buf != GlyphBuffer)
14003630506bSToomas Soome 		free(buf);
14013630506bSToomas Soome }
14023630506bSToomas Soome 
14033630506bSToomas Soome /*
14043630506bSToomas Soome  * Public graphics primitives.
14053630506bSToomas Soome  */
14063630506bSToomas Soome 
14073630506bSToomas Soome static int
isqrt(int num)14083630506bSToomas Soome isqrt(int num)
14093630506bSToomas Soome {
14103630506bSToomas Soome 	int res = 0;
14113630506bSToomas Soome 	int bit = 1 << 30;
14123630506bSToomas Soome 
14133630506bSToomas Soome 	/* "bit" starts at the highest power of four <= the argument. */
14143630506bSToomas Soome 	while (bit > num)
14153630506bSToomas Soome 		bit >>= 2;
14163630506bSToomas Soome 
14173630506bSToomas Soome 	while (bit != 0) {
14183630506bSToomas Soome 		if (num >= res + bit) {
14193630506bSToomas Soome 			num -= res + bit;
14203630506bSToomas Soome 			res = (res >> 1) + bit;
14213630506bSToomas Soome 		} else {
14223630506bSToomas Soome 			res >>= 1;
14233630506bSToomas Soome 		}
14243630506bSToomas Soome 		bit >>= 2;
14253630506bSToomas Soome 	}
14263630506bSToomas Soome 	return (res);
14273630506bSToomas Soome }
14283630506bSToomas Soome 
14295365af66SToomas Soome static uint32_t
gfx_fb_getcolor(void)14305365af66SToomas Soome gfx_fb_getcolor(void)
14313630506bSToomas Soome {
14323630506bSToomas Soome 	uint32_t c;
14333630506bSToomas Soome 	const teken_attr_t *ap;
14343630506bSToomas Soome 
14353630506bSToomas Soome 	ap = teken_get_curattr(&gfx_state.tg_teken);
14363630506bSToomas Soome         if (ap->ta_format & TF_REVERSE) {
14373630506bSToomas Soome 		c = ap->ta_bgcolor;
14383630506bSToomas Soome 		if (ap->ta_format & TF_BLINK)
14393630506bSToomas Soome 			c |= TC_LIGHT;
14403630506bSToomas Soome 	} else {
14413630506bSToomas Soome 		c = ap->ta_fgcolor;
14423630506bSToomas Soome 		if (ap->ta_format & TF_BOLD)
14433630506bSToomas Soome 			c |= TC_LIGHT;
14443630506bSToomas Soome 	}
14453630506bSToomas Soome 
14465365af66SToomas Soome 	return (gfx_fb_color_map(c));
14475365af66SToomas Soome }
14485365af66SToomas Soome 
14495365af66SToomas Soome /* set pixel in framebuffer using gfx coordinates */
14505365af66SToomas Soome void
gfx_fb_setpixel(uint32_t x,uint32_t y)14515365af66SToomas Soome gfx_fb_setpixel(uint32_t x, uint32_t y)
14525365af66SToomas Soome {
14535365af66SToomas Soome 	uint32_t c;
14545365af66SToomas Soome 
14555365af66SToomas Soome 	if (gfx_state.tg_fb_type == FB_TEXT)
14565365af66SToomas Soome 		return;
14575365af66SToomas Soome 
14585365af66SToomas Soome 	c = gfx_fb_getcolor();
14593630506bSToomas Soome 
14603630506bSToomas Soome 	if (x >= gfx_state.tg_fb.fb_width ||
14613630506bSToomas Soome 	    y >= gfx_state.tg_fb.fb_height)
14623630506bSToomas Soome 		return;
14633630506bSToomas Soome 
14643630506bSToomas Soome 	gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x, y, 1, 1, 0);
14653630506bSToomas Soome }
14663630506bSToomas Soome 
14673630506bSToomas Soome /*
14683630506bSToomas Soome  * draw rectangle in framebuffer using gfx coordinates.
14693630506bSToomas Soome  */
14703630506bSToomas Soome void
gfx_fb_drawrect(uint32_t x1,uint32_t y1,uint32_t x2,uint32_t y2,uint32_t fill)14713630506bSToomas Soome gfx_fb_drawrect(uint32_t x1, uint32_t y1, uint32_t x2, uint32_t y2,
14723630506bSToomas Soome     uint32_t fill)
14733630506bSToomas Soome {
14745365af66SToomas Soome 	uint32_t c;
14753630506bSToomas Soome 
14763630506bSToomas Soome 	if (gfx_state.tg_fb_type == FB_TEXT)
14773630506bSToomas Soome 		return;
14783630506bSToomas Soome 
14795365af66SToomas Soome 	c = gfx_fb_getcolor();
14805365af66SToomas Soome 
14815365af66SToomas Soome 	if (fill != 0) {
14825365af66SToomas Soome 		gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x1, y1, x2 - x1,
14835365af66SToomas Soome 		    y2 - y1, 0);
14843630506bSToomas Soome 	} else {
14855365af66SToomas Soome 		gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x1, y1, x2 - x1, 1, 0);
14865365af66SToomas Soome 		gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x1, y2, x2 - x1, 1, 0);
14875365af66SToomas Soome 		gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x1, y1, 1, y2 - y1, 0);
14885365af66SToomas Soome 		gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x2, y1, 1, y2 - y1, 0);
14893630506bSToomas Soome 	}
14903630506bSToomas Soome }
14913630506bSToomas Soome 
14923630506bSToomas Soome void
gfx_fb_line(uint32_t x0,uint32_t y0,uint32_t x1,uint32_t y1,uint32_t wd)14933630506bSToomas Soome gfx_fb_line(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t wd)
14943630506bSToomas Soome {
14953630506bSToomas Soome 	int dx, sx, dy, sy;
14963630506bSToomas Soome 	int err, e2, x2, y2, ed, width;
14973630506bSToomas Soome 
14983630506bSToomas Soome 	if (gfx_state.tg_fb_type == FB_TEXT)
14993630506bSToomas Soome 		return;
15003630506bSToomas Soome 
15013630506bSToomas Soome 	width = wd;
15023630506bSToomas Soome 	sx = x0 < x1? 1 : -1;
15033630506bSToomas Soome 	sy = y0 < y1? 1 : -1;
15043630506bSToomas Soome 	dx = x1 > x0? x1 - x0 : x0 - x1;
15053630506bSToomas Soome 	dy = y1 > y0? y1 - y0 : y0 - y1;
15063630506bSToomas Soome 	err = dx + dy;
15073630506bSToomas Soome 	ed = dx + dy == 0 ? 1: isqrt(dx * dx + dy * dy);
15083630506bSToomas Soome 
15093630506bSToomas Soome 	for (;;) {
15103630506bSToomas Soome 		gfx_fb_setpixel(x0, y0);
15113630506bSToomas Soome 		e2 = err;
15123630506bSToomas Soome 		x2 = x0;
15133630506bSToomas Soome 		if ((e2 << 1) >= -dx) {		/* x step */
15143630506bSToomas Soome 			e2 += dy;
15153630506bSToomas Soome 			y2 = y0;
15163630506bSToomas Soome 			while (e2 < ed * width &&
15173630506bSToomas Soome 			    (y1 != (uint32_t)y2 || dx > dy)) {
15183630506bSToomas Soome 				y2 += sy;
15193630506bSToomas Soome 				gfx_fb_setpixel(x0, y2);
15203630506bSToomas Soome 				e2 += dx;
15213630506bSToomas Soome 			}
15223630506bSToomas Soome 			if (x0 == x1)
15233630506bSToomas Soome 				break;
15243630506bSToomas Soome 			e2 = err;
15253630506bSToomas Soome 			err -= dy;
15263630506bSToomas Soome 			x0 += sx;
15273630506bSToomas Soome 		}
15283630506bSToomas Soome 		if ((e2 << 1) <= dy) {		/* y step */
15293630506bSToomas Soome 			e2 = dx-e2;
15303630506bSToomas Soome 			while (e2 < ed * width &&
15313630506bSToomas Soome 			    (x1 != (uint32_t)x2 || dx < dy)) {
15323630506bSToomas Soome 				x2 += sx;
15333630506bSToomas Soome 				gfx_fb_setpixel(x2, y0);
15343630506bSToomas Soome 				e2 += dy;
15353630506bSToomas Soome 			}
15363630506bSToomas Soome 			if (y0 == y1)
15373630506bSToomas Soome 				break;
15383630506bSToomas Soome 			err += dx;
15393630506bSToomas Soome 			y0 += sy;
15403630506bSToomas Soome 		}
15413630506bSToomas Soome 	}
15423630506bSToomas Soome }
15433630506bSToomas Soome 
15443630506bSToomas Soome /*
15453630506bSToomas Soome  * quadratic Bézier curve limited to gradients without sign change.
15463630506bSToomas Soome  */
15473630506bSToomas 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)15483630506bSToomas Soome gfx_fb_bezier(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t x2,
15493630506bSToomas Soome     uint32_t y2, uint32_t wd)
15503630506bSToomas Soome {
15513630506bSToomas Soome 	int sx, sy, xx, yy, xy, width;
15523630506bSToomas Soome 	int dx, dy, err, curvature;
15533630506bSToomas Soome 	int i;
15543630506bSToomas Soome 
15553630506bSToomas Soome 	if (gfx_state.tg_fb_type == FB_TEXT)
15563630506bSToomas Soome 		return;
15573630506bSToomas Soome 
15583630506bSToomas Soome 	width = wd;
15593630506bSToomas Soome 	sx = x2 - x1;
15603630506bSToomas Soome 	sy = y2 - y1;
15613630506bSToomas Soome 	xx = x0 - x1;
15623630506bSToomas Soome 	yy = y0 - y1;
15633630506bSToomas Soome 	curvature = xx*sy - yy*sx;
15643630506bSToomas Soome 
15653630506bSToomas Soome 	if (sx*sx + sy*sy > xx*xx+yy*yy) {
15663630506bSToomas Soome 		x2 = x0;
15673630506bSToomas Soome 		x0 = sx + x1;
15683630506bSToomas Soome 		y2 = y0;
15693630506bSToomas Soome 		y0 = sy + y1;
15703630506bSToomas Soome 		curvature = -curvature;
15713630506bSToomas Soome 	}
15723630506bSToomas Soome 	if (curvature != 0) {
15733630506bSToomas Soome 		xx += sx;
15743630506bSToomas Soome 		sx = x0 < x2? 1 : -1;
15753630506bSToomas Soome 		xx *= sx;
15763630506bSToomas Soome 		yy += sy;
15773630506bSToomas Soome 		sy = y0 < y2? 1 : -1;
15783630506bSToomas Soome 		yy *= sy;
15793630506bSToomas Soome 		xy = (xx*yy) << 1;
15803630506bSToomas Soome 		xx *= xx;
15813630506bSToomas Soome 		yy *= yy;
15823630506bSToomas Soome 		if (curvature * sx * sy < 0) {
15833630506bSToomas Soome 			xx = -xx;
15843630506bSToomas Soome 			yy = -yy;
15853630506bSToomas Soome 			xy = -xy;
15863630506bSToomas Soome 			curvature = -curvature;
15873630506bSToomas Soome 		}
15883630506bSToomas Soome 		dx = 4 * sy * curvature * (x1 - x0) + xx - xy;
15893630506bSToomas Soome 		dy = 4 * sx * curvature * (y0 - y1) + yy - xy;
15903630506bSToomas Soome 		xx += xx;
15913630506bSToomas Soome 		yy += yy;
15923630506bSToomas Soome 		err = dx + dy + xy;
15933630506bSToomas Soome 		do {
15943630506bSToomas Soome 			for (i = 0; i <= width; i++)
15953630506bSToomas Soome 				gfx_fb_setpixel(x0 + i, y0);
15963630506bSToomas Soome 			if (x0 == x2 && y0 == y2)
15973630506bSToomas Soome 				return;  /* last pixel -> curve finished */
15983630506bSToomas Soome 			y1 = 2 * err < dx;
15993630506bSToomas Soome 			if (2 * err > dy) {
16003630506bSToomas Soome 				x0 += sx;
16013630506bSToomas Soome 				dx -= xy;
16023630506bSToomas Soome 				dy += yy;
16033630506bSToomas Soome 				err += dy;
16043630506bSToomas Soome 			}
16053630506bSToomas Soome 			if (y1 != 0) {
16063630506bSToomas Soome 				y0 += sy;
16073630506bSToomas Soome 				dy -= xy;
16083630506bSToomas Soome 				dx += xx;
16093630506bSToomas Soome 				err += dx;
16103630506bSToomas Soome 			}
16113630506bSToomas Soome 		} while (dy < dx); /* gradient negates -> algorithm fails */
16123630506bSToomas Soome 	}
16133630506bSToomas Soome 	gfx_fb_line(x0, y0, x2, y2, width);
16143630506bSToomas Soome }
16153630506bSToomas Soome 
16163630506bSToomas Soome /*
16173630506bSToomas Soome  * draw rectangle using terminal coordinates and current foreground color.
16183630506bSToomas Soome  */
16193630506bSToomas Soome void
gfx_term_drawrect(uint32_t ux1,uint32_t uy1,uint32_t ux2,uint32_t uy2)16203630506bSToomas Soome gfx_term_drawrect(uint32_t ux1, uint32_t uy1, uint32_t ux2, uint32_t uy2)
16213630506bSToomas Soome {
16223630506bSToomas Soome 	int x1, y1, x2, y2;
16233630506bSToomas Soome 	int xshift, yshift;
16243630506bSToomas Soome 	int width, i;
16253630506bSToomas Soome 	uint32_t vf_width, vf_height;
16263630506bSToomas Soome 	teken_rect_t r;
16273630506bSToomas Soome 
16283630506bSToomas Soome 	if (gfx_state.tg_fb_type == FB_TEXT)
16293630506bSToomas Soome 		return;
16303630506bSToomas Soome 
16313630506bSToomas Soome 	vf_width = gfx_state.tg_font.vf_width;
16323630506bSToomas Soome 	vf_height = gfx_state.tg_font.vf_height;
16333630506bSToomas Soome 	width = vf_width / 4;			/* line width */
16343630506bSToomas Soome 	xshift = (vf_width - width) / 2;
16353630506bSToomas Soome 	yshift = (vf_height - width) / 2;
16363630506bSToomas Soome 
16373630506bSToomas Soome 	/* Shift coordinates */
16383630506bSToomas Soome 	if (ux1 != 0)
16393630506bSToomas Soome 		ux1--;
16403630506bSToomas Soome 	if (uy1 != 0)
16413630506bSToomas Soome 		uy1--;
16423630506bSToomas Soome 	ux2--;
16433630506bSToomas Soome 	uy2--;
16443630506bSToomas Soome 
16453630506bSToomas Soome 	/* mark area used in terminal */
16463630506bSToomas Soome 	r.tr_begin.tp_col = ux1;
16473630506bSToomas Soome 	r.tr_begin.tp_row = uy1;
16483630506bSToomas Soome 	r.tr_end.tp_col = ux2 + 1;
16493630506bSToomas Soome 	r.tr_end.tp_row = uy2 + 1;
16503630506bSToomas Soome 
16513630506bSToomas Soome 	term_image_display(&gfx_state, &r);
16523630506bSToomas Soome 
16533630506bSToomas Soome 	/*
16543630506bSToomas Soome 	 * Draw horizontal lines width points thick, shifted from outer edge.
16553630506bSToomas Soome 	 */
16563630506bSToomas Soome 	x1 = (ux1 + 1) * vf_width + gfx_state.tg_origin.tp_col;
16573630506bSToomas Soome 	y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row + yshift;
16583630506bSToomas Soome 	x2 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
16593630506bSToomas Soome 	gfx_fb_drawrect(x1, y1, x2, y1 + width, 1);
16603630506bSToomas Soome 	y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
16613630506bSToomas Soome 	y2 += vf_height - yshift - width;
16623630506bSToomas Soome 	gfx_fb_drawrect(x1, y2, x2, y2 + width, 1);
16633630506bSToomas Soome 
16643630506bSToomas Soome 	/*
16653630506bSToomas Soome 	 * Draw vertical lines width points thick, shifted from outer edge.
16663630506bSToomas Soome 	 */
16673630506bSToomas Soome 	x1 = ux1 * vf_width + gfx_state.tg_origin.tp_col + xshift;
16683630506bSToomas Soome 	y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row;
16693630506bSToomas Soome 	y1 += vf_height;
16703630506bSToomas Soome 	y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
16713630506bSToomas Soome 	gfx_fb_drawrect(x1, y1, x1 + width, y2, 1);
16723630506bSToomas Soome 	x1 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
16733630506bSToomas Soome 	x1 += vf_width - xshift - width;
16743630506bSToomas Soome 	gfx_fb_drawrect(x1, y1, x1 + width, y2, 1);
16753630506bSToomas Soome 
16763630506bSToomas Soome 	/* Draw upper left corner. */
16773630506bSToomas Soome 	x1 = ux1 * vf_width + gfx_state.tg_origin.tp_col + xshift;
16783630506bSToomas Soome 	y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row;
16793630506bSToomas Soome 	y1 += vf_height;
16803630506bSToomas Soome 
16813630506bSToomas Soome 	x2 = ux1 * vf_width + gfx_state.tg_origin.tp_col;
16823630506bSToomas Soome 	x2 += vf_width;
16833630506bSToomas Soome 	y2 = uy1 * vf_height + gfx_state.tg_origin.tp_row + yshift;
16843630506bSToomas Soome 	for (i = 0; i <= width; i++)
16853630506bSToomas Soome 		gfx_fb_bezier(x1 + i, y1, x1 + i, y2 + i, x2, y2 + i, width-i);
16863630506bSToomas Soome 
16873630506bSToomas Soome 	/* Draw lower left corner. */
16883630506bSToomas Soome 	x1 = ux1 * vf_width + gfx_state.tg_origin.tp_col;
16893630506bSToomas Soome 	x1 += vf_width;
16903630506bSToomas Soome 	y1 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
16913630506bSToomas Soome 	y1 += vf_height - yshift;
16923630506bSToomas Soome 	x2 = ux1 * vf_width + gfx_state.tg_origin.tp_col + xshift;
16933630506bSToomas Soome 	y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
16943630506bSToomas Soome 	for (i = 0; i <= width; i++)
16953630506bSToomas Soome 		gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i);
16963630506bSToomas Soome 
16973630506bSToomas Soome 	/* Draw upper right corner. */
16983630506bSToomas Soome 	x1 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
16993630506bSToomas Soome 	y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row + yshift;
17003630506bSToomas Soome 	x2 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
17013630506bSToomas Soome 	x2 += vf_width - xshift - width;
17023630506bSToomas Soome 	y2 = uy1 * vf_height + gfx_state.tg_origin.tp_row;
17033630506bSToomas Soome 	y2 += vf_height;
17043630506bSToomas Soome 	for (i = 0; i <= width; i++)
17053630506bSToomas Soome 		gfx_fb_bezier(x1, y1 + i, x2 + i, y1 + i, x2 + i, y2, width-i);
17063630506bSToomas Soome 
17073630506bSToomas Soome 	/* Draw lower right corner. */
17083630506bSToomas Soome 	x1 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
17093630506bSToomas Soome 	y1 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
17103630506bSToomas Soome 	y1 += vf_height - yshift;
17113630506bSToomas Soome 	x2 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
17123630506bSToomas Soome 	x2 += vf_width - xshift - width;
17133630506bSToomas Soome 	y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
17143630506bSToomas Soome 	for (i = 0; i <= width; i++)
17153630506bSToomas Soome 		gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i);
17163630506bSToomas Soome }
17173630506bSToomas Soome 
17183630506bSToomas Soome int
gfx_fb_putimage(png_t * png,uint32_t ux1,uint32_t uy1,uint32_t ux2,uint32_t uy2,uint32_t flags)17193630506bSToomas Soome gfx_fb_putimage(png_t *png, uint32_t ux1, uint32_t uy1, uint32_t ux2,
17203630506bSToomas Soome     uint32_t uy2, uint32_t flags)
17213630506bSToomas Soome {
17223630506bSToomas Soome #if defined(EFI)
17233630506bSToomas Soome 	EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
17243630506bSToomas Soome #else
17253630506bSToomas Soome 	struct paletteentry *p;
17263630506bSToomas Soome #endif
17273630506bSToomas Soome 	uint8_t *data;
17283630506bSToomas Soome 	uint32_t i, j, x, y, fheight, fwidth;
17293630506bSToomas Soome 	int rs, gs, bs;
17303630506bSToomas Soome 	uint8_t r, g, b, a;
17313630506bSToomas Soome 	bool scale = false;
17323630506bSToomas Soome 	bool trace = false;
17333630506bSToomas Soome 	teken_rect_t rect;
17343630506bSToomas Soome 
17353630506bSToomas Soome 	trace = (flags & FL_PUTIMAGE_DEBUG) != 0;
17363630506bSToomas Soome 
17373630506bSToomas Soome 	if (gfx_state.tg_fb_type == FB_TEXT) {
17383630506bSToomas Soome 		if (trace)
17393630506bSToomas Soome 			printf("Framebuffer not active.\n");
17403630506bSToomas Soome 		return (1);
17413630506bSToomas Soome 	}
17423630506bSToomas Soome 
17433630506bSToomas Soome 	if (png->color_type != PNG_TRUECOLOR_ALPHA) {
17443630506bSToomas Soome 		if (trace)
17453630506bSToomas Soome 			printf("Not truecolor image.\n");
17463630506bSToomas Soome 		return (1);
17473630506bSToomas Soome 	}
17483630506bSToomas Soome 
17493630506bSToomas Soome 	if (ux1 > gfx_state.tg_fb.fb_width ||
17503630506bSToomas Soome 	    uy1 > gfx_state.tg_fb.fb_height) {
17513630506bSToomas Soome 		if (trace)
17523630506bSToomas Soome 			printf("Top left coordinate off screen.\n");
17533630506bSToomas Soome 		return (1);
17543630506bSToomas Soome 	}
17553630506bSToomas Soome 
17563630506bSToomas Soome 	if (png->width > UINT16_MAX || png->height > UINT16_MAX) {
17573630506bSToomas Soome 		if (trace)
17583630506bSToomas Soome 			printf("Image too large.\n");
17593630506bSToomas Soome 		return (1);
17603630506bSToomas Soome 	}
17613630506bSToomas Soome 
17623630506bSToomas Soome 	if (png->width < 1 || png->height < 1) {
17633630506bSToomas Soome 		if (trace)
17643630506bSToomas Soome 			printf("Image too small.\n");
17653630506bSToomas Soome 		return (1);
17663630506bSToomas Soome 	}
17673630506bSToomas Soome 
17683630506bSToomas Soome 	/*
17693630506bSToomas Soome 	 * If 0 was passed for either ux2 or uy2, then calculate the missing
17703630506bSToomas Soome 	 * part of the bottom right coordinate.
17713630506bSToomas Soome 	 */
17723630506bSToomas Soome 	scale = true;
17733630506bSToomas Soome 	if (ux2 == 0 && uy2 == 0) {
17743630506bSToomas Soome 		/* Both 0, use the native resolution of the image */
17753630506bSToomas Soome 		ux2 = ux1 + png->width;
17763630506bSToomas Soome 		uy2 = uy1 + png->height;
17773630506bSToomas Soome 		scale = false;
17783630506bSToomas Soome 	} else if (ux2 == 0) {
17793630506bSToomas Soome 		/* Set ux2 from uy2/uy1 to maintain aspect ratio */
17803630506bSToomas Soome 		ux2 = ux1 + (png->width * (uy2 - uy1)) / png->height;
17813630506bSToomas Soome 	} else if (uy2 == 0) {
17823630506bSToomas Soome 		/* Set uy2 from ux2/ux1 to maintain aspect ratio */
17833630506bSToomas Soome 		uy2 = uy1 + (png->height * (ux2 - ux1)) / png->width;
17843630506bSToomas Soome 	}
17853630506bSToomas Soome 
17863630506bSToomas Soome 	if (ux2 > gfx_state.tg_fb.fb_width ||
17873630506bSToomas Soome 	    uy2 > gfx_state.tg_fb.fb_height) {
17883630506bSToomas Soome 		if (trace)
17893630506bSToomas Soome 			printf("Bottom right coordinate off screen.\n");
17903630506bSToomas Soome 		return (1);
17913630506bSToomas Soome 	}
17923630506bSToomas Soome 
17933630506bSToomas Soome 	fwidth = ux2 - ux1;
17943630506bSToomas Soome 	fheight = uy2 - uy1;
17953630506bSToomas Soome 
17963630506bSToomas Soome 	/*
17973630506bSToomas Soome 	 * If the original image dimensions have been passed explicitly,
17983630506bSToomas Soome 	 * disable scaling.
17993630506bSToomas Soome 	 */
18003630506bSToomas Soome 	if (fwidth == png->width && fheight == png->height)
18013630506bSToomas Soome 		scale = false;
18023630506bSToomas Soome 
18033630506bSToomas Soome 	if (ux1 == 0) {
18043630506bSToomas Soome 		/*
18053630506bSToomas Soome 		 * No top left X co-ordinate (real coordinates start at 1),
18063630506bSToomas Soome 		 * place as far right as it will fit.
18073630506bSToomas Soome 		 */
18083630506bSToomas Soome 		ux2 = gfx_state.tg_fb.fb_width - gfx_state.tg_origin.tp_col;
18093630506bSToomas Soome 		ux1 = ux2 - fwidth;
18103630506bSToomas Soome 	}
18113630506bSToomas Soome 
18123630506bSToomas Soome 	if (uy1 == 0) {
18133630506bSToomas Soome 		/*
18143630506bSToomas Soome 		 * No top left Y co-ordinate (real coordinates start at 1),
18153630506bSToomas Soome 		 * place as far down as it will fit.
18163630506bSToomas Soome 		 */
18173630506bSToomas Soome 		uy2 = gfx_state.tg_fb.fb_height - gfx_state.tg_origin.tp_row;
18183630506bSToomas Soome 		uy1 = uy2 - fheight;
18193630506bSToomas Soome 	}
18203630506bSToomas Soome 
18213630506bSToomas Soome 	if (ux1 >= ux2 || uy1 >= uy2) {
18223630506bSToomas Soome 		if (trace)
18233630506bSToomas Soome 			printf("Image dimensions reversed.\n");
18243630506bSToomas Soome 		return (1);
18253630506bSToomas Soome 	}
18263630506bSToomas Soome 
18273630506bSToomas Soome 	if (fwidth < 2 || fheight < 2) {
18283630506bSToomas Soome 		if (trace)
18293630506bSToomas Soome 			printf("Target area too small\n");
18303630506bSToomas Soome 		return (1);
18313630506bSToomas Soome 	}
18323630506bSToomas Soome 
18333630506bSToomas Soome 	if (trace)
18343630506bSToomas Soome 		printf("Image %ux%u -> %ux%u @%ux%u\n",
18353630506bSToomas Soome 		    png->width, png->height, fwidth, fheight, ux1, uy1);
18363630506bSToomas Soome 
18373630506bSToomas Soome 	rect.tr_begin.tp_col = ux1 / gfx_state.tg_font.vf_width;
18383630506bSToomas Soome 	rect.tr_begin.tp_row = uy1 / gfx_state.tg_font.vf_height;
18393630506bSToomas Soome 	rect.tr_end.tp_col = (ux1 + fwidth) / gfx_state.tg_font.vf_width;
18403630506bSToomas Soome 	rect.tr_end.tp_row = (uy1 + fheight) / gfx_state.tg_font.vf_height;
18413630506bSToomas Soome 
18423630506bSToomas Soome 	/*
18433630506bSToomas Soome 	 * mark area used in terminal
18443630506bSToomas Soome 	 */
18453630506bSToomas Soome 	if (!(flags & FL_PUTIMAGE_NOSCROLL))
18463630506bSToomas Soome 		term_image_display(&gfx_state, &rect);
18473630506bSToomas Soome 
18483630506bSToomas Soome 	if ((flags & FL_PUTIMAGE_BORDER))
18493630506bSToomas Soome 		gfx_fb_drawrect(ux1, uy1, ux2, uy2, 0);
18503630506bSToomas Soome 
18513630506bSToomas Soome 	data = malloc(fwidth * fheight * sizeof(*p));
18523630506bSToomas Soome 	p = (void *)data;
18533630506bSToomas Soome 	if (data == NULL) {
18543630506bSToomas Soome 		if (trace)
18553630506bSToomas Soome 			printf("Out of memory.\n");
18563630506bSToomas Soome 		return (1);
18573630506bSToomas Soome 	}
18583630506bSToomas Soome 
18593630506bSToomas Soome 	/*
18603630506bSToomas Soome 	 * Build image for our framebuffer.
18613630506bSToomas Soome 	 */
18623630506bSToomas Soome 
18633630506bSToomas Soome 	/* Helper to calculate the pixel index from the source png */
18643630506bSToomas Soome #define	GETPIXEL(xx, yy)	(((yy) * png->width + (xx)) * png->bpp)
18653630506bSToomas Soome 
18663630506bSToomas Soome 	/*
18673630506bSToomas Soome 	 * For each of the x and y directions, calculate the number of pixels
18683630506bSToomas Soome 	 * in the source image that correspond to a single pixel in the target.
18693630506bSToomas Soome 	 * Use fixed-point arithmetic with 16-bits for each of the integer and
18703630506bSToomas Soome 	 * fractional parts.
18713630506bSToomas Soome 	 */
18723630506bSToomas Soome 	const uint32_t wcstep = ((png->width - 1) << 16) / (fwidth - 1);
18733630506bSToomas Soome 	const uint32_t hcstep = ((png->height - 1) << 16) / (fheight - 1);
18743630506bSToomas Soome 
18753630506bSToomas Soome 	rs = 8 - (fls(gfx_state.tg_fb.fb_mask_red) -
18763630506bSToomas Soome 	    ffs(gfx_state.tg_fb.fb_mask_red) + 1);
18773630506bSToomas Soome 	gs = 8 - (fls(gfx_state.tg_fb.fb_mask_green) -
18783630506bSToomas Soome 	    ffs(gfx_state.tg_fb.fb_mask_green) + 1);
18793630506bSToomas Soome 	bs = 8 - (fls(gfx_state.tg_fb.fb_mask_blue) -
18803630506bSToomas Soome 	    ffs(gfx_state.tg_fb.fb_mask_blue) + 1);
18813630506bSToomas Soome 
18823630506bSToomas Soome 	uint32_t hc = 0;
18833630506bSToomas Soome 	for (y = 0; y < fheight; y++) {
18843630506bSToomas Soome 		uint32_t hc2 = (hc >> 9) & 0x7f;
18853630506bSToomas Soome 		uint32_t hc1 = 0x80 - hc2;
18863630506bSToomas Soome 
18873630506bSToomas Soome 		uint32_t offset_y = hc >> 16;
18883630506bSToomas Soome 		uint32_t offset_y1 = offset_y + 1;
18893630506bSToomas Soome 
18903630506bSToomas Soome 		uint32_t wc = 0;
18913630506bSToomas Soome 		for (x = 0; x < fwidth; x++) {
18923630506bSToomas Soome 			uint32_t wc2 = (wc >> 9) & 0x7f;
18933630506bSToomas Soome 			uint32_t wc1 = 0x80 - wc2;
18943630506bSToomas Soome 
18953630506bSToomas Soome 			uint32_t offset_x = wc >> 16;
18963630506bSToomas Soome 			uint32_t offset_x1 = offset_x + 1;
18973630506bSToomas Soome 
18983630506bSToomas Soome 			/* Target pixel index */
18993630506bSToomas Soome 			j = y * fwidth + x;
19003630506bSToomas Soome 
19013630506bSToomas Soome 			if (!scale) {
19023630506bSToomas Soome 				i = GETPIXEL(x, y);
19033630506bSToomas Soome 				r = png->image[i];
19043630506bSToomas Soome 				g = png->image[i + 1];
19053630506bSToomas Soome 				b = png->image[i + 2];
19063630506bSToomas Soome 				a = png->image[i + 3];
19073630506bSToomas Soome 			} else {
19083630506bSToomas Soome 				uint8_t pixel[4];
19093630506bSToomas Soome 
19103630506bSToomas Soome 				uint32_t p00 = GETPIXEL(offset_x, offset_y);
19113630506bSToomas Soome 				uint32_t p01 = GETPIXEL(offset_x, offset_y1);
19123630506bSToomas Soome 				uint32_t p10 = GETPIXEL(offset_x1, offset_y);
19133630506bSToomas Soome 				uint32_t p11 = GETPIXEL(offset_x1, offset_y1);
19143630506bSToomas Soome 
19153630506bSToomas Soome 				/*
19163630506bSToomas Soome 				 * Given a 2x2 array of pixels in the source
19173630506bSToomas Soome 				 * image, combine them to produce a single
19183630506bSToomas Soome 				 * value for the pixel in the target image.
19193630506bSToomas Soome 				 * Each column of pixels is combined using
19203630506bSToomas Soome 				 * a weighted average where the top and bottom
19213630506bSToomas Soome 				 * pixels contribute hc1 and hc2 respectively.
19223630506bSToomas Soome 				 * The calculation for bottom pixel pB and
19233630506bSToomas Soome 				 * top pixel pT is:
19243630506bSToomas Soome 				 *   (pT * hc1 + pB * hc2) / (hc1 + hc2)
19253630506bSToomas Soome 				 * Once the values are determined for the two
19263630506bSToomas Soome 				 * columns of pixels, then the columns are
19273630506bSToomas Soome 				 * averaged together in the same way but using
19283630506bSToomas Soome 				 * wc1 and wc2 for the weightings.
19293630506bSToomas Soome 				 *
19303630506bSToomas Soome 				 * Since hc1 and hc2 are chosen so that
19313630506bSToomas Soome 				 * hc1 + hc2 == 128 (and same for wc1 + wc2),
19323630506bSToomas Soome 				 * the >> 14 below is a quick way to divide by
19333630506bSToomas Soome 				 * (hc1 + hc2) * (wc1 + wc2)
19343630506bSToomas Soome 				 */
19353630506bSToomas Soome 				for (i = 0; i < 4; i++)
19363630506bSToomas Soome 					pixel[i] = (
19373630506bSToomas Soome 					    (png->image[p00 + i] * hc1 +
19383630506bSToomas Soome 					    png->image[p01 + i] * hc2) * wc1 +
19393630506bSToomas Soome 					    (png->image[p10 + i] * hc1 +
19403630506bSToomas Soome 					    png->image[p11 + i] * hc2) * wc2)
19413630506bSToomas Soome 					    >> 14;
19423630506bSToomas Soome 
19433630506bSToomas Soome 				r = pixel[0];
19443630506bSToomas Soome 				g = pixel[1];
19453630506bSToomas Soome 				b = pixel[2];
19463630506bSToomas Soome 				a = pixel[3];
19473630506bSToomas Soome 			}
19483630506bSToomas Soome 
19493630506bSToomas Soome 			if (trace)
19503630506bSToomas Soome 				printf("r/g/b: %x/%x/%x\n", r, g, b);
19513630506bSToomas Soome 			/*
19523630506bSToomas Soome 			 * Rough colorspace reduction for 15/16 bit colors.
19533630506bSToomas Soome 			 */
19543630506bSToomas Soome 			p[j].Red = r >> rs;
19553630506bSToomas Soome                         p[j].Green = g >> gs;
19563630506bSToomas Soome                         p[j].Blue = b >> bs;
19573630506bSToomas Soome                         p[j].Reserved = a;
19583630506bSToomas Soome 
19593630506bSToomas Soome 			wc += wcstep;
19603630506bSToomas Soome 		}
19613630506bSToomas Soome 		hc += hcstep;
19623630506bSToomas Soome 	}
19633630506bSToomas Soome 
19643630506bSToomas Soome 	gfx_fb_cons_display(ux1, uy1, fwidth, fheight, data);
19653630506bSToomas Soome 	free(data);
19663630506bSToomas Soome 	return (0);
19673630506bSToomas Soome }
19683630506bSToomas Soome 
19693630506bSToomas Soome /*
19703630506bSToomas Soome  * Reset font flags to FONT_AUTO.
19713630506bSToomas Soome  */
19723630506bSToomas Soome void
reset_font_flags(void)19733630506bSToomas Soome reset_font_flags(void)
19743630506bSToomas Soome {
19753630506bSToomas Soome 	struct fontlist *fl;
19763630506bSToomas Soome 
19773630506bSToomas Soome 	STAILQ_FOREACH(fl, &fonts, font_next) {
19783630506bSToomas Soome 		fl->font_flags = FONT_AUTO;
19793630506bSToomas Soome 	}
19803630506bSToomas Soome }
19813630506bSToomas Soome 
1982becaac39SToomas Soome /* Return  w^2 + h^2 or 0, if the dimensions are unknown */
1983becaac39SToomas Soome static unsigned
edid_diagonal_squared(void)1984becaac39SToomas Soome edid_diagonal_squared(void)
1985becaac39SToomas Soome {
1986becaac39SToomas Soome 	unsigned w, h;
1987becaac39SToomas Soome 
1988becaac39SToomas Soome 	if (edid_info == NULL)
1989becaac39SToomas Soome 		return (0);
1990becaac39SToomas Soome 
1991becaac39SToomas Soome 	w = edid_info->display.max_horizontal_image_size;
1992becaac39SToomas Soome 	h = edid_info->display.max_vertical_image_size;
1993becaac39SToomas Soome 
1994becaac39SToomas Soome 	/* If either one is 0, we have aspect ratio, not size */
1995becaac39SToomas Soome 	if (w == 0 || h == 0)
1996becaac39SToomas Soome 		return (0);
1997becaac39SToomas Soome 
1998becaac39SToomas Soome 	/*
1999becaac39SToomas Soome 	 * some monitors encode the aspect ratio instead of the physical size.
2000becaac39SToomas Soome 	 */
2001becaac39SToomas Soome 	if ((w == 16 && h == 9) || (w == 16 && h == 10) ||
2002becaac39SToomas Soome 	    (w == 4 && h == 3) || (w == 5 && h == 4))
2003becaac39SToomas Soome 		return (0);
2004becaac39SToomas Soome 
2005becaac39SToomas Soome 	/*
2006becaac39SToomas Soome 	 * translate cm to inch, note we scale by 100 here.
2007becaac39SToomas Soome 	 */
2008becaac39SToomas Soome 	w = w * 100 / 254;
2009becaac39SToomas Soome 	h = h * 100 / 254;
2010becaac39SToomas Soome 
2011becaac39SToomas Soome 	/* Return w^2 + h^2 */
2012becaac39SToomas Soome 	return (w * w + h * h);
2013becaac39SToomas Soome }
2014becaac39SToomas Soome 
2015becaac39SToomas Soome /*
2016becaac39SToomas Soome  * calculate pixels per inch.
2017becaac39SToomas Soome  */
2018becaac39SToomas Soome static unsigned
gfx_get_ppi(void)2019becaac39SToomas Soome gfx_get_ppi(void)
2020becaac39SToomas Soome {
2021becaac39SToomas Soome 	unsigned dp, di;
2022becaac39SToomas Soome 
2023becaac39SToomas Soome 	di = edid_diagonal_squared();
2024becaac39SToomas Soome 	if (di == 0)
2025becaac39SToomas Soome 		return (0);
2026becaac39SToomas Soome 
2027becaac39SToomas Soome 	dp = gfx_state.tg_fb.fb_width *
2028becaac39SToomas Soome 	    gfx_state.tg_fb.fb_width +
2029becaac39SToomas Soome 	    gfx_state.tg_fb.fb_height *
2030becaac39SToomas Soome 	    gfx_state.tg_fb.fb_height;
2031becaac39SToomas Soome 
2032becaac39SToomas Soome 	return (isqrt(dp / di));
2033becaac39SToomas Soome }
2034becaac39SToomas Soome 
2035becaac39SToomas Soome /*
2036becaac39SToomas Soome  * Calculate font size from density independent pixels (dp):
2037becaac39SToomas Soome  * ((16dp * ppi) / 160) * display_factor.
2038becaac39SToomas Soome  * Here we are using fixed constants: 1dp == 160 ppi and
2039becaac39SToomas Soome  * display_factor 2.
2040becaac39SToomas Soome  *
2041becaac39SToomas Soome  * We are rounding font size up and are searching for font which is
2042becaac39SToomas Soome  * not smaller than calculated size value.
2043becaac39SToomas Soome  */
2044becaac39SToomas Soome static vt_font_bitmap_data_t *
gfx_get_font(void)2045becaac39SToomas Soome gfx_get_font(void)
2046becaac39SToomas Soome {
2047becaac39SToomas Soome 	unsigned ppi, size;
2048becaac39SToomas Soome 	vt_font_bitmap_data_t *font = NULL;
2049becaac39SToomas Soome 	struct fontlist *fl, *next;
2050becaac39SToomas Soome 
2051becaac39SToomas Soome 	/* Text mode is not supported here. */
2052becaac39SToomas Soome 	if (gfx_state.tg_fb_type == FB_TEXT)
2053becaac39SToomas Soome 		return (NULL);
2054becaac39SToomas Soome 
2055becaac39SToomas Soome 	ppi = gfx_get_ppi();
2056becaac39SToomas Soome 	if (ppi == 0)
2057becaac39SToomas Soome 		return (NULL);
2058becaac39SToomas Soome 
2059becaac39SToomas Soome 	/*
2060becaac39SToomas Soome 	 * We will search for 16dp font.
2061becaac39SToomas Soome 	 * We are using scale up by 10 for roundup.
2062becaac39SToomas Soome 	 */
2063becaac39SToomas Soome 	size = (16 * ppi * 10) / 160;
2064becaac39SToomas Soome 	/* Apply display factor 2.  */
2065becaac39SToomas Soome 	size = roundup(size * 2, 10) / 10;
2066becaac39SToomas Soome 
2067becaac39SToomas Soome 	STAILQ_FOREACH(fl, &fonts, font_next) {
2068becaac39SToomas Soome 		next = STAILQ_NEXT(fl, font_next);
2069becaac39SToomas Soome 
2070becaac39SToomas Soome 		/*
2071becaac39SToomas Soome 		 * If this is last font or, if next font is smaller,
2072becaac39SToomas Soome 		 * we have our font. Make sure, it actually is loaded.
2073becaac39SToomas Soome 		 */
2074becaac39SToomas Soome 		if (next == NULL || next->font_data->vfbd_height < size) {
2075becaac39SToomas Soome 			font = fl->font_data;
2076becaac39SToomas Soome 			if (font->vfbd_font == NULL ||
2077becaac39SToomas Soome 			    fl->font_flags == FONT_RELOAD) {
2078becaac39SToomas Soome 				if (fl->font_load != NULL &&
2079becaac39SToomas Soome 				    fl->font_name != NULL)
2080becaac39SToomas Soome 					font = fl->font_load(fl->font_name);
2081becaac39SToomas Soome 			}
2082becaac39SToomas Soome 			break;
2083becaac39SToomas Soome 		}
2084becaac39SToomas Soome 	}
2085becaac39SToomas Soome 
2086becaac39SToomas Soome 	return (font);
2087becaac39SToomas Soome }
2088becaac39SToomas Soome 
20893630506bSToomas 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)20903630506bSToomas Soome set_font(teken_unit_t *rows, teken_unit_t *cols, teken_unit_t h, teken_unit_t w)
20913630506bSToomas Soome {
20923630506bSToomas Soome 	vt_font_bitmap_data_t *font = NULL;
20933630506bSToomas Soome 	struct fontlist *fl;
20943630506bSToomas Soome 	unsigned height = h;
20953630506bSToomas Soome 	unsigned width = w;
20963630506bSToomas Soome 
20973630506bSToomas Soome 	/*
20983630506bSToomas Soome 	 * First check for manually loaded font.
20993630506bSToomas Soome 	 */
21003630506bSToomas Soome 	STAILQ_FOREACH(fl, &fonts, font_next) {
21013630506bSToomas Soome 		if (fl->font_flags == FONT_MANUAL) {
21023630506bSToomas Soome 			font = fl->font_data;
21033630506bSToomas Soome 			if (font->vfbd_font == NULL && fl->font_load != NULL &&
21043630506bSToomas Soome 			    fl->font_name != NULL) {
21053630506bSToomas Soome 				font = fl->font_load(fl->font_name);
21063630506bSToomas Soome 			}
21073630506bSToomas Soome 			if (font == NULL || font->vfbd_font == NULL)
21083630506bSToomas Soome 				font = NULL;
21093630506bSToomas Soome 			break;
21103630506bSToomas Soome 		}
21113630506bSToomas Soome 	}
21123630506bSToomas Soome 
2113becaac39SToomas Soome 	if (font == NULL)
2114becaac39SToomas Soome 		font = gfx_get_font();
2115becaac39SToomas Soome 
21163630506bSToomas Soome 	if (font != NULL) {
211796bef205SToomas Soome 		*rows = height / font->vfbd_height;
211896bef205SToomas Soome 		*cols = width / font->vfbd_width;
21193630506bSToomas Soome 		return (font);
21203630506bSToomas Soome 	}
21213630506bSToomas Soome 
21223630506bSToomas Soome 	/*
2123a26f7358SToomas Soome 	 * Find best font for these dimensions, or use default.
2124a26f7358SToomas Soome 	 * If height >= VT_FB_MAX_HEIGHT and width >= VT_FB_MAX_WIDTH,
2125a26f7358SToomas Soome 	 * do not use smaller font than our DEFAULT_FONT_DATA.
21263630506bSToomas Soome 	 */
21273630506bSToomas Soome 	STAILQ_FOREACH(fl, &fonts, font_next) {
21283630506bSToomas Soome 		font = fl->font_data;
2129a26f7358SToomas Soome 		if ((*rows * font->vfbd_height <= height &&
2130a26f7358SToomas Soome 		    *cols * font->vfbd_width <= width) ||
2131a26f7358SToomas Soome 		    (height >= VT_FB_MAX_HEIGHT &&
2132a26f7358SToomas Soome 		    width >= VT_FB_MAX_WIDTH &&
2133a26f7358SToomas Soome 		    font->vfbd_height == DEFAULT_FONT_DATA.vfbd_height &&
2134a26f7358SToomas Soome 		    font->vfbd_width == DEFAULT_FONT_DATA.vfbd_width)) {
21353630506bSToomas Soome 			if (font->vfbd_font == NULL ||
21363630506bSToomas Soome 			    fl->font_flags == FONT_RELOAD) {
21373630506bSToomas Soome 				if (fl->font_load != NULL &&
21383630506bSToomas Soome 				    fl->font_name != NULL) {
21393630506bSToomas Soome 					font = fl->font_load(fl->font_name);
21403630506bSToomas Soome 				}
21413630506bSToomas Soome 				if (font == NULL)
21423630506bSToomas Soome 					continue;
21433630506bSToomas Soome 			}
214496bef205SToomas Soome 			*rows = height / font->vfbd_height;
214596bef205SToomas Soome 			*cols = width / font->vfbd_width;
21463630506bSToomas Soome 			break;
21473630506bSToomas Soome 		}
21483630506bSToomas Soome 		font = NULL;
21493630506bSToomas Soome 	}
21503630506bSToomas Soome 
21513630506bSToomas Soome 	if (font == NULL) {
21523630506bSToomas Soome 		/*
21533630506bSToomas Soome 		 * We have fonts sorted smallest last, try it before
21543630506bSToomas Soome 		 * falling back to builtin.
21553630506bSToomas Soome 		 */
21563630506bSToomas Soome 		fl = STAILQ_LAST(&fonts, fontlist, font_next);
21573630506bSToomas Soome 		if (fl != NULL && fl->font_load != NULL &&
21583630506bSToomas Soome 		    fl->font_name != NULL) {
21593630506bSToomas Soome 			font = fl->font_load(fl->font_name);
21603630506bSToomas Soome 		}
21613630506bSToomas Soome 		if (font == NULL)
21623630506bSToomas Soome 			font = &DEFAULT_FONT_DATA;
21633630506bSToomas Soome 
216496bef205SToomas Soome 		*rows = height / font->vfbd_height;
216596bef205SToomas Soome 		*cols = width / font->vfbd_width;
21663630506bSToomas Soome 	}
21673630506bSToomas Soome 
21683630506bSToomas Soome 	return (font);
21693630506bSToomas Soome }
21703630506bSToomas Soome 
21713630506bSToomas Soome static void
cons_clear(void)21723630506bSToomas Soome cons_clear(void)
21733630506bSToomas Soome {
21743630506bSToomas Soome 	char clear[] = { '\033', 'c' };
21753630506bSToomas Soome 
21763630506bSToomas Soome 	/* Reset terminal */
21773630506bSToomas Soome 	teken_input(&gfx_state.tg_teken, clear, sizeof(clear));
21783630506bSToomas Soome 	gfx_state.tg_functions->tf_param(&gfx_state, TP_SHOWCURSOR, 0);
21793630506bSToomas Soome }
21803630506bSToomas Soome 
21813630506bSToomas Soome void
setup_font(teken_gfx_t * state,teken_unit_t height,teken_unit_t width)21823630506bSToomas Soome setup_font(teken_gfx_t *state, teken_unit_t height, teken_unit_t width)
21833630506bSToomas Soome {
21843630506bSToomas Soome 	vt_font_bitmap_data_t *font_data;
21853630506bSToomas Soome 	teken_pos_t *tp = &state->tg_tp;
21863630506bSToomas Soome 	char env[8];
21873630506bSToomas Soome 	int i;
21883630506bSToomas Soome 
21893630506bSToomas Soome 	/*
21903630506bSToomas Soome 	 * set_font() will select a appropriate sized font for
21913630506bSToomas Soome 	 * the number of rows and columns selected.  If we don't
21923630506bSToomas Soome 	 * have a font that will fit, then it will use the
21933630506bSToomas Soome 	 * default builtin font and adjust the rows and columns
21943630506bSToomas Soome 	 * to fit on the screen.
21953630506bSToomas Soome 	 */
21963630506bSToomas Soome 	font_data = set_font(&tp->tp_row, &tp->tp_col, height, width);
21973630506bSToomas Soome 
21983630506bSToomas Soome         if (font_data == NULL)
21993630506bSToomas Soome 		panic("out of memory");
22003630506bSToomas Soome 
22013630506bSToomas Soome 	for (i = 0; i < VFNT_MAPS; i++) {
22023630506bSToomas Soome 		state->tg_font.vf_map[i] =
22033630506bSToomas Soome 		    font_data->vfbd_font->vf_map[i];
22043630506bSToomas Soome 		state->tg_font.vf_map_count[i] =
22053630506bSToomas Soome 		    font_data->vfbd_font->vf_map_count[i];
22063630506bSToomas Soome 	}
22073630506bSToomas Soome 
22083630506bSToomas Soome 	state->tg_font.vf_bytes = font_data->vfbd_font->vf_bytes;
22093630506bSToomas Soome 	state->tg_font.vf_height = font_data->vfbd_font->vf_height;
22103630506bSToomas Soome 	state->tg_font.vf_width = font_data->vfbd_font->vf_width;
22113630506bSToomas Soome 
22123630506bSToomas Soome 	snprintf(env, sizeof (env), "%ux%u",
22133630506bSToomas Soome 	    state->tg_font.vf_width, state->tg_font.vf_height);
22143630506bSToomas Soome 	env_setenv("screen.font", EV_VOLATILE | EV_NOHOOK,
22153630506bSToomas Soome 	    env, font_set, env_nounset);
22163630506bSToomas Soome }
22173630506bSToomas Soome 
22183630506bSToomas Soome /* Binary search for the glyph. Return 0 if not found. */
22193630506bSToomas Soome static uint16_t
font_bisearch(const vfnt_map_t * map,uint32_t len,teken_char_t src)22203630506bSToomas Soome font_bisearch(const vfnt_map_t *map, uint32_t len, teken_char_t src)
22213630506bSToomas Soome {
22223630506bSToomas Soome 	unsigned min, mid, max;
22233630506bSToomas Soome 
22243630506bSToomas Soome 	min = 0;
22253630506bSToomas Soome 	max = len - 1;
22263630506bSToomas Soome 
22273630506bSToomas Soome 	/* Empty font map. */
22283630506bSToomas Soome 	if (len == 0)
22293630506bSToomas Soome 		return (0);
22303630506bSToomas Soome 	/* Character below minimal entry. */
22313630506bSToomas Soome 	if (src < map[0].vfm_src)
22323630506bSToomas Soome 		return (0);
22333630506bSToomas Soome 	/* Optimization: ASCII characters occur very often. */
22343630506bSToomas Soome 	if (src <= map[0].vfm_src + map[0].vfm_len)
22353630506bSToomas Soome 		return (src - map[0].vfm_src + map[0].vfm_dst);
22363630506bSToomas Soome 	/* Character above maximum entry. */
22373630506bSToomas Soome 	if (src > map[max].vfm_src + map[max].vfm_len)
22383630506bSToomas Soome 		return (0);
22393630506bSToomas Soome 
22403630506bSToomas Soome 	/* Binary search. */
22413630506bSToomas Soome 	while (max >= min) {
22423630506bSToomas Soome 		mid = (min + max) / 2;
22433630506bSToomas Soome 		if (src < map[mid].vfm_src)
22443630506bSToomas Soome 			max = mid - 1;
22453630506bSToomas Soome 		else if (src > map[mid].vfm_src + map[mid].vfm_len)
22463630506bSToomas Soome 			min = mid + 1;
22473630506bSToomas Soome 		else
22483630506bSToomas Soome 			return (src - map[mid].vfm_src + map[mid].vfm_dst);
22493630506bSToomas Soome 	}
22503630506bSToomas Soome 
22513630506bSToomas Soome 	return (0);
22523630506bSToomas Soome }
22533630506bSToomas Soome 
22543630506bSToomas Soome /*
22553630506bSToomas Soome  * Return glyph bitmap. If glyph is not found, we will return bitmap
22563630506bSToomas Soome  * for the first (offset 0) glyph.
22573630506bSToomas Soome  */
22583630506bSToomas Soome uint8_t *
font_lookup(const struct vt_font * vf,teken_char_t c,const teken_attr_t * a)22593630506bSToomas Soome font_lookup(const struct vt_font *vf, teken_char_t c, const teken_attr_t *a)
22603630506bSToomas Soome {
22613630506bSToomas Soome 	uint16_t dst;
22623630506bSToomas Soome 	size_t stride;
22633630506bSToomas Soome 
22643630506bSToomas Soome 	/* Substitute bold with normal if not found. */
22653630506bSToomas Soome 	if (a->ta_format & TF_BOLD) {
22663630506bSToomas Soome 		dst = font_bisearch(vf->vf_map[VFNT_MAP_BOLD],
22673630506bSToomas Soome 		    vf->vf_map_count[VFNT_MAP_BOLD], c);
22683630506bSToomas Soome 		if (dst != 0)
22693630506bSToomas Soome 			goto found;
22703630506bSToomas Soome 	}
22713630506bSToomas Soome 	dst = font_bisearch(vf->vf_map[VFNT_MAP_NORMAL],
22723630506bSToomas Soome 	    vf->vf_map_count[VFNT_MAP_NORMAL], c);
22733630506bSToomas Soome 
22743630506bSToomas Soome found:
22753630506bSToomas Soome 	stride = howmany(vf->vf_width, 8) * vf->vf_height;
22763630506bSToomas Soome 	return (&vf->vf_bytes[dst * stride]);
22773630506bSToomas Soome }
22783630506bSToomas Soome 
22793630506bSToomas Soome static int
load_mapping(int fd,struct vt_font * fp,int n)22803630506bSToomas Soome load_mapping(int fd, struct vt_font *fp, int n)
22813630506bSToomas Soome {
22823630506bSToomas Soome 	size_t i, size;
22833630506bSToomas Soome 	ssize_t rv;
22843630506bSToomas Soome 	vfnt_map_t *mp;
22853630506bSToomas Soome 
22863630506bSToomas Soome 	if (fp->vf_map_count[n] == 0)
22873630506bSToomas Soome 		return (0);
22883630506bSToomas Soome 
22893630506bSToomas Soome 	size = fp->vf_map_count[n] * sizeof(*mp);
22903630506bSToomas Soome 	mp = malloc(size);
22913630506bSToomas Soome 	if (mp == NULL)
22923630506bSToomas Soome 		return (ENOMEM);
22933630506bSToomas Soome 	fp->vf_map[n] = mp;
22943630506bSToomas Soome 
22953630506bSToomas Soome 	rv = read(fd, mp, size);
22963630506bSToomas Soome 	if (rv < 0 || (size_t)rv != size) {
22973630506bSToomas Soome 		free(fp->vf_map[n]);
22983630506bSToomas Soome 		fp->vf_map[n] = NULL;
22993630506bSToomas Soome 		return (EIO);
23003630506bSToomas Soome 	}
23013630506bSToomas Soome 
23023630506bSToomas Soome 	for (i = 0; i < fp->vf_map_count[n]; i++) {
23033630506bSToomas Soome 		mp[i].vfm_src = be32toh(mp[i].vfm_src);
23043630506bSToomas Soome 		mp[i].vfm_dst = be16toh(mp[i].vfm_dst);
23053630506bSToomas Soome 		mp[i].vfm_len = be16toh(mp[i].vfm_len);
23063630506bSToomas Soome 	}
23073630506bSToomas Soome 	return (0);
23083630506bSToomas Soome }
23093630506bSToomas Soome 
23103630506bSToomas Soome static int
builtin_mapping(struct vt_font * fp,int n)23113630506bSToomas Soome builtin_mapping(struct vt_font *fp, int n)
23123630506bSToomas Soome {
23133630506bSToomas Soome 	size_t size;
23143630506bSToomas Soome 	struct vfnt_map *mp;
23153630506bSToomas Soome 
23163630506bSToomas Soome 	if (n >= VFNT_MAPS)
23173630506bSToomas Soome 		return (EINVAL);
23183630506bSToomas Soome 
23193630506bSToomas Soome 	if (fp->vf_map_count[n] == 0)
23203630506bSToomas Soome 		return (0);
23213630506bSToomas Soome 
23223630506bSToomas Soome 	size = fp->vf_map_count[n] * sizeof(*mp);
23233630506bSToomas Soome 	mp = malloc(size);
23243630506bSToomas Soome 	if (mp == NULL)
23253630506bSToomas Soome 		return (ENOMEM);
23263630506bSToomas Soome 	fp->vf_map[n] = mp;
23273630506bSToomas Soome 
23283630506bSToomas Soome 	memcpy(mp, DEFAULT_FONT_DATA.vfbd_font->vf_map[n], size);
23293630506bSToomas Soome 	return (0);
23303630506bSToomas Soome }
23313630506bSToomas Soome 
23323630506bSToomas Soome /*
23333630506bSToomas Soome  * Load font from builtin or from file.
23343630506bSToomas Soome  * We do need special case for builtin because the builtin font glyphs
23353630506bSToomas Soome  * are compressed and we do need to uncompress them.
23363630506bSToomas Soome  * Having single load_font() for both cases will help us to simplify
23373630506bSToomas Soome  * font switch handling.
23383630506bSToomas Soome  */
23393630506bSToomas Soome static vt_font_bitmap_data_t *
load_font(char * path)23403630506bSToomas Soome load_font(char *path)
23413630506bSToomas Soome {
23423630506bSToomas Soome 	int fd, i;
23433630506bSToomas Soome 	uint32_t glyphs;
23443630506bSToomas Soome 	struct font_header fh;
23453630506bSToomas Soome 	struct fontlist *fl;
23463630506bSToomas Soome 	vt_font_bitmap_data_t *bp;
23473630506bSToomas Soome 	struct vt_font *fp;
23483630506bSToomas Soome 	size_t size;
23493630506bSToomas Soome 	ssize_t rv;
23503630506bSToomas Soome 
23513630506bSToomas Soome 	/* Get our entry from the font list. */
23523630506bSToomas Soome 	STAILQ_FOREACH(fl, &fonts, font_next) {
23533630506bSToomas Soome 		if (strcmp(fl->font_name, path) == 0)
23543630506bSToomas Soome 			break;
23553630506bSToomas Soome 	}
23563630506bSToomas Soome 	if (fl == NULL)
23573630506bSToomas Soome 		return (NULL);	/* Should not happen. */
23583630506bSToomas Soome 
23593630506bSToomas Soome 	bp = fl->font_data;
23603630506bSToomas Soome 	if (bp->vfbd_font != NULL && fl->font_flags != FONT_RELOAD)
23613630506bSToomas Soome 		return (bp);
23623630506bSToomas Soome 
23633630506bSToomas Soome 	fd = -1;
23643630506bSToomas Soome 	/*
23653630506bSToomas Soome 	 * Special case for builtin font.
23663630506bSToomas Soome 	 * Builtin font is the very first font we load, we do not have
23673630506bSToomas Soome 	 * previous loads to be released.
23683630506bSToomas Soome 	 */
23693630506bSToomas Soome 	if (fl->font_flags == FONT_BUILTIN) {
23703630506bSToomas Soome 		if ((fp = calloc(1, sizeof(struct vt_font))) == NULL)
23713630506bSToomas Soome 			return (NULL);
23723630506bSToomas Soome 
23733630506bSToomas Soome 		fp->vf_width = DEFAULT_FONT_DATA.vfbd_width;
23743630506bSToomas Soome 		fp->vf_height = DEFAULT_FONT_DATA.vfbd_height;
23753630506bSToomas Soome 
23763630506bSToomas Soome 		fp->vf_bytes = malloc(DEFAULT_FONT_DATA.vfbd_uncompressed_size);
23773630506bSToomas Soome 		if (fp->vf_bytes == NULL) {
23783630506bSToomas Soome 			free(fp);
23793630506bSToomas Soome 			return (NULL);
23803630506bSToomas Soome 		}
23813630506bSToomas Soome 
23823630506bSToomas Soome 		bp->vfbd_uncompressed_size =
23833630506bSToomas Soome 		    DEFAULT_FONT_DATA.vfbd_uncompressed_size;
23843630506bSToomas Soome 		bp->vfbd_compressed_size =
23853630506bSToomas Soome 		    DEFAULT_FONT_DATA.vfbd_compressed_size;
23863630506bSToomas Soome 
23873630506bSToomas Soome 		if (lz4_decompress(DEFAULT_FONT_DATA.vfbd_compressed_data,
23883630506bSToomas Soome 		    fp->vf_bytes,
23893630506bSToomas Soome 		    DEFAULT_FONT_DATA.vfbd_compressed_size,
23903630506bSToomas Soome 		    DEFAULT_FONT_DATA.vfbd_uncompressed_size, 0) != 0) {
23913630506bSToomas Soome 			free(fp->vf_bytes);
23923630506bSToomas Soome 			free(fp);
23933630506bSToomas Soome 			return (NULL);
23943630506bSToomas Soome 		}
23953630506bSToomas Soome 
23963630506bSToomas Soome 		for (i = 0; i < VFNT_MAPS; i++) {
23973630506bSToomas Soome 			fp->vf_map_count[i] =
23983630506bSToomas Soome 			    DEFAULT_FONT_DATA.vfbd_font->vf_map_count[i];
23993630506bSToomas Soome 			if (builtin_mapping(fp, i) != 0)
24003630506bSToomas Soome 				goto free_done;
24013630506bSToomas Soome 		}
24023630506bSToomas Soome 
24033630506bSToomas Soome 		bp->vfbd_font = fp;
24043630506bSToomas Soome 		return (bp);
24053630506bSToomas Soome 	}
24063630506bSToomas Soome 
24073630506bSToomas Soome 	fd = open(path, O_RDONLY);
24083630506bSToomas Soome 	if (fd < 0)
24093630506bSToomas Soome 		return (NULL);
24103630506bSToomas Soome 
24113630506bSToomas Soome 	size = sizeof(fh);
24123630506bSToomas Soome 	rv = read(fd, &fh, size);
24133630506bSToomas Soome 	if (rv < 0 || (size_t)rv != size) {
24143630506bSToomas Soome 		bp = NULL;
24153630506bSToomas Soome 		goto done;
24163630506bSToomas Soome 	}
24173630506bSToomas Soome 	if (memcmp(fh.fh_magic, FONT_HEADER_MAGIC, sizeof(fh.fh_magic)) != 0) {
24183630506bSToomas Soome 		bp = NULL;
24193630506bSToomas Soome 		goto done;
24203630506bSToomas Soome 	}
24213630506bSToomas Soome 	if ((fp = calloc(1, sizeof(struct vt_font))) == NULL) {
24223630506bSToomas Soome 		bp = NULL;
24233630506bSToomas Soome 		goto done;
24243630506bSToomas Soome 	}
24253630506bSToomas Soome 	for (i = 0; i < VFNT_MAPS; i++)
24263630506bSToomas Soome 		fp->vf_map_count[i] = be32toh(fh.fh_map_count[i]);
24273630506bSToomas Soome 
24283630506bSToomas Soome 	glyphs = be32toh(fh.fh_glyph_count);
24293630506bSToomas Soome 	fp->vf_width = fh.fh_width;
24303630506bSToomas Soome 	fp->vf_height = fh.fh_height;
24313630506bSToomas Soome 
24323630506bSToomas Soome 	size = howmany(fp->vf_width, 8) * fp->vf_height * glyphs;
24333630506bSToomas Soome 	bp->vfbd_uncompressed_size = size;
24343630506bSToomas Soome 	if ((fp->vf_bytes = malloc(size)) == NULL)
24353630506bSToomas Soome 		goto free_done;
24363630506bSToomas Soome 
24373630506bSToomas Soome 	rv = read(fd, fp->vf_bytes, size);
24383630506bSToomas Soome 	if (rv < 0 || (size_t)rv != size)
24393630506bSToomas Soome 		goto free_done;
24403630506bSToomas Soome 	for (i = 0; i < VFNT_MAPS; i++) {
24413630506bSToomas Soome 		if (load_mapping(fd, fp, i) != 0)
24423630506bSToomas Soome 			goto free_done;
24433630506bSToomas Soome 	}
24443630506bSToomas Soome 
24453630506bSToomas Soome 	/*
24463630506bSToomas Soome 	 * Reset builtin flag now as we have full font loaded.
24473630506bSToomas Soome 	 */
24483630506bSToomas Soome 	if (fl->font_flags == FONT_BUILTIN)
24493630506bSToomas Soome 		fl->font_flags = FONT_AUTO;
24503630506bSToomas Soome 
24513630506bSToomas Soome 	/*
24523630506bSToomas Soome 	 * Release previously loaded entries. We can do this now, as
24533630506bSToomas Soome 	 * the new font is loaded. Note, there can be no console
24543630506bSToomas Soome 	 * output till the new font is in place and teken is notified.
24553630506bSToomas Soome 	 * We do need to keep fl->font_data for glyph dimensions.
24563630506bSToomas Soome 	 */
24573630506bSToomas Soome 	STAILQ_FOREACH(fl, &fonts, font_next) {
24583630506bSToomas Soome 		if (fl->font_data->vfbd_font == NULL)
24593630506bSToomas Soome 			continue;
24603630506bSToomas Soome 
24613630506bSToomas Soome 		for (i = 0; i < VFNT_MAPS; i++)
24623630506bSToomas Soome 			free(fl->font_data->vfbd_font->vf_map[i]);
24633630506bSToomas Soome 		free(fl->font_data->vfbd_font->vf_bytes);
24643630506bSToomas Soome 		free(fl->font_data->vfbd_font);
24653630506bSToomas Soome 		fl->font_data->vfbd_font = NULL;
24663630506bSToomas Soome 	}
24673630506bSToomas Soome 
24683630506bSToomas Soome 	bp->vfbd_font = fp;
24693630506bSToomas Soome 	bp->vfbd_compressed_size = 0;
24703630506bSToomas Soome 
24713630506bSToomas Soome done:
24723630506bSToomas Soome 	if (fd != -1)
24733630506bSToomas Soome 		close(fd);
24743630506bSToomas Soome 	return (bp);
24753630506bSToomas Soome 
24763630506bSToomas Soome free_done:
24773630506bSToomas Soome 	for (i = 0; i < VFNT_MAPS; i++)
24783630506bSToomas Soome 		free(fp->vf_map[i]);
24793630506bSToomas Soome 	free(fp->vf_bytes);
24803630506bSToomas Soome 	free(fp);
24813630506bSToomas Soome 	bp = NULL;
24823630506bSToomas Soome 	goto done;
24833630506bSToomas Soome }
24843630506bSToomas Soome 
24853630506bSToomas Soome struct name_entry {
24863630506bSToomas Soome 	char			*n_name;
24873630506bSToomas Soome 	SLIST_ENTRY(name_entry)	n_entry;
24883630506bSToomas Soome };
24893630506bSToomas Soome 
24903630506bSToomas Soome SLIST_HEAD(name_list, name_entry);
24913630506bSToomas Soome 
24923630506bSToomas Soome /* Read font names from index file. */
24933630506bSToomas Soome static struct name_list *
read_list(char * fonts)24943630506bSToomas Soome read_list(char *fonts)
24953630506bSToomas Soome {
24963630506bSToomas Soome 	struct name_list *nl;
24973630506bSToomas Soome 	struct name_entry *np;
24983630506bSToomas Soome 	char *dir, *ptr;
24993630506bSToomas Soome 	char buf[PATH_MAX];
25003630506bSToomas Soome 	int fd, len;
25013630506bSToomas Soome 
2502313724baSColin Percival 	TSENTER();
2503313724baSColin Percival 
25043630506bSToomas Soome 	dir = strdup(fonts);
25053630506bSToomas Soome 	if (dir == NULL)
25063630506bSToomas Soome 		return (NULL);
25073630506bSToomas Soome 
25083630506bSToomas Soome 	ptr = strrchr(dir, '/');
25093630506bSToomas Soome 	*ptr = '\0';
25103630506bSToomas Soome 
25113630506bSToomas Soome 	fd = open(fonts, O_RDONLY);
25123630506bSToomas Soome 	if (fd < 0)
25133630506bSToomas Soome 		return (NULL);
25143630506bSToomas Soome 
25153630506bSToomas Soome 	nl = malloc(sizeof(*nl));
25163630506bSToomas Soome 	if (nl == NULL) {
25173630506bSToomas Soome 		close(fd);
25183630506bSToomas Soome 		return (nl);
25193630506bSToomas Soome 	}
25203630506bSToomas Soome 
25213630506bSToomas Soome 	SLIST_INIT(nl);
25223630506bSToomas Soome 	while ((len = fgetstr(buf, sizeof (buf), fd)) >= 0) {
25233630506bSToomas Soome 		if (*buf == '#' || *buf == '\0')
25243630506bSToomas Soome 			continue;
25253630506bSToomas Soome 
25263630506bSToomas Soome 		if (bcmp(buf, "MENU", 4) == 0)
25273630506bSToomas Soome 			continue;
25283630506bSToomas Soome 
25293630506bSToomas Soome 		if (bcmp(buf, "FONT", 4) == 0)
25303630506bSToomas Soome 			continue;
25313630506bSToomas Soome 
25323630506bSToomas Soome 		ptr = strchr(buf, ':');
25333630506bSToomas Soome 		if (ptr == NULL)
25343630506bSToomas Soome 			continue;
25353630506bSToomas Soome 		else
25363630506bSToomas Soome 			*ptr = '\0';
25373630506bSToomas Soome 
25383630506bSToomas Soome 		np = malloc(sizeof(*np));
25393630506bSToomas Soome 		if (np == NULL) {
25403630506bSToomas Soome 			close(fd);
25413630506bSToomas Soome 			return (nl);	/* return what we have */
25423630506bSToomas Soome 		}
25433630506bSToomas Soome 		if (asprintf(&np->n_name, "%s/%s", dir, buf) < 0) {
25443630506bSToomas Soome 			free(np);
25453630506bSToomas Soome 			close(fd);
25463630506bSToomas Soome 			return (nl);    /* return what we have */
25473630506bSToomas Soome 		}
25483630506bSToomas Soome 		SLIST_INSERT_HEAD(nl, np, n_entry);
25493630506bSToomas Soome 	}
25503630506bSToomas Soome 	close(fd);
2551313724baSColin Percival 	TSEXIT();
25523630506bSToomas Soome 	return (nl);
25533630506bSToomas Soome }
25543630506bSToomas Soome 
25553630506bSToomas Soome /*
25563630506bSToomas Soome  * Read the font properties and insert new entry into the list.
25573630506bSToomas Soome  * The font list is built in descending order.
25583630506bSToomas Soome  */
25593630506bSToomas Soome static bool
insert_font(char * name,FONT_FLAGS flags)25603630506bSToomas Soome insert_font(char *name, FONT_FLAGS flags)
25613630506bSToomas Soome {
25623630506bSToomas Soome 	struct font_header fh;
25633630506bSToomas Soome 	struct fontlist *fp, *previous, *entry, *next;
25643630506bSToomas Soome 	size_t size;
25653630506bSToomas Soome 	ssize_t rv;
25663630506bSToomas Soome 	int fd;
25673630506bSToomas Soome 	char *font_name;
25683630506bSToomas Soome 
2569313724baSColin Percival 	TSENTER();
2570313724baSColin Percival 
25713630506bSToomas Soome 	font_name = NULL;
25723630506bSToomas Soome 	if (flags == FONT_BUILTIN) {
25733630506bSToomas Soome 		/*
25743630506bSToomas Soome 		 * We only install builtin font once, while setting up
25753630506bSToomas Soome 		 * initial console. Since this will happen very early,
25763630506bSToomas Soome 		 * we assume asprintf will not fail. Once we have access to
25773630506bSToomas Soome 		 * files, the builtin font will be replaced by font loaded
25783630506bSToomas Soome 		 * from file.
25793630506bSToomas Soome 		 */
25803630506bSToomas Soome 		if (!STAILQ_EMPTY(&fonts))
25813630506bSToomas Soome 			return (false);
25823630506bSToomas Soome 
25833630506bSToomas Soome 		fh.fh_width = DEFAULT_FONT_DATA.vfbd_width;
25843630506bSToomas Soome 		fh.fh_height = DEFAULT_FONT_DATA.vfbd_height;
25853630506bSToomas Soome 
25863630506bSToomas Soome 		(void) asprintf(&font_name, "%dx%d",
25873630506bSToomas Soome 		    DEFAULT_FONT_DATA.vfbd_width,
25883630506bSToomas Soome 		    DEFAULT_FONT_DATA.vfbd_height);
25893630506bSToomas Soome 	} else {
25903630506bSToomas Soome 		fd = open(name, O_RDONLY);
25913630506bSToomas Soome 		if (fd < 0)
25923630506bSToomas Soome 			return (false);
25933630506bSToomas Soome 		rv = read(fd, &fh, sizeof(fh));
25943630506bSToomas Soome 		close(fd);
25953630506bSToomas Soome 		if (rv < 0 || (size_t)rv != sizeof(fh))
25963630506bSToomas Soome 			return (false);
25973630506bSToomas Soome 
25983630506bSToomas Soome 		if (memcmp(fh.fh_magic, FONT_HEADER_MAGIC,
25993630506bSToomas Soome 		    sizeof(fh.fh_magic)) != 0)
26003630506bSToomas Soome 			return (false);
26013630506bSToomas Soome 		font_name = strdup(name);
26023630506bSToomas Soome 	}
26033630506bSToomas Soome 
26043630506bSToomas Soome 	if (font_name == NULL)
26053630506bSToomas Soome 		return (false);
26063630506bSToomas Soome 
26073630506bSToomas Soome 	/*
26083630506bSToomas Soome 	 * If we have an entry with the same glyph dimensions, replace
26093630506bSToomas Soome 	 * the file name and mark us. We only support unique dimensions.
26103630506bSToomas Soome 	 */
26113630506bSToomas Soome 	STAILQ_FOREACH(entry, &fonts, font_next) {
26123630506bSToomas Soome 		if (fh.fh_width == entry->font_data->vfbd_width &&
26133630506bSToomas Soome 		    fh.fh_height == entry->font_data->vfbd_height) {
26143630506bSToomas Soome 			free(entry->font_name);
26153630506bSToomas Soome 			entry->font_name = font_name;
26163630506bSToomas Soome 			entry->font_flags = FONT_RELOAD;
2617313724baSColin Percival 			TSEXIT();
26183630506bSToomas Soome 			return (true);
26193630506bSToomas Soome 		}
26203630506bSToomas Soome 	}
26213630506bSToomas Soome 
26223630506bSToomas Soome 	fp = calloc(sizeof(*fp), 1);
26233630506bSToomas Soome 	if (fp == NULL) {
26243630506bSToomas Soome 		free(font_name);
26253630506bSToomas Soome 		return (false);
26263630506bSToomas Soome 	}
26273630506bSToomas Soome 	fp->font_data = calloc(sizeof(*fp->font_data), 1);
26283630506bSToomas Soome 	if (fp->font_data == NULL) {
26293630506bSToomas Soome 		free(font_name);
26303630506bSToomas Soome 		free(fp);
26313630506bSToomas Soome 		return (false);
26323630506bSToomas Soome 	}
26333630506bSToomas Soome 	fp->font_name = font_name;
26343630506bSToomas Soome 	fp->font_flags = flags;
26353630506bSToomas Soome 	fp->font_load = load_font;
26363630506bSToomas Soome 	fp->font_data->vfbd_width = fh.fh_width;
26373630506bSToomas Soome 	fp->font_data->vfbd_height = fh.fh_height;
26383630506bSToomas Soome 
26393630506bSToomas Soome 	if (STAILQ_EMPTY(&fonts)) {
26403630506bSToomas Soome 		STAILQ_INSERT_HEAD(&fonts, fp, font_next);
2641313724baSColin Percival 		TSEXIT();
26423630506bSToomas Soome 		return (true);
26433630506bSToomas Soome 	}
26443630506bSToomas Soome 
26453630506bSToomas Soome 	previous = NULL;
26463630506bSToomas Soome 	size = fp->font_data->vfbd_width * fp->font_data->vfbd_height;
26473630506bSToomas Soome 
26483630506bSToomas Soome 	STAILQ_FOREACH(entry, &fonts, font_next) {
26493630506bSToomas Soome 		vt_font_bitmap_data_t *bd;
26503630506bSToomas Soome 
26513630506bSToomas Soome 		bd = entry->font_data;
26523630506bSToomas Soome 		/* Should fp be inserted before the entry? */
26533630506bSToomas Soome 		if (size > bd->vfbd_width * bd->vfbd_height) {
26543630506bSToomas Soome 			if (previous == NULL) {
26553630506bSToomas Soome 				STAILQ_INSERT_HEAD(&fonts, fp, font_next);
26563630506bSToomas Soome 			} else {
26573630506bSToomas Soome 				STAILQ_INSERT_AFTER(&fonts, previous, fp,
26583630506bSToomas Soome 				    font_next);
26593630506bSToomas Soome 			}
2660313724baSColin Percival 			TSEXIT();
26613630506bSToomas Soome 			return (true);
26623630506bSToomas Soome 		}
26633630506bSToomas Soome 		next = STAILQ_NEXT(entry, font_next);
26643630506bSToomas Soome 		if (next == NULL ||
26653630506bSToomas Soome 		    size > next->font_data->vfbd_width *
26663630506bSToomas Soome 		    next->font_data->vfbd_height) {
26673630506bSToomas Soome 			STAILQ_INSERT_AFTER(&fonts, entry, fp, font_next);
2668313724baSColin Percival 			TSEXIT();
26693630506bSToomas Soome 			return (true);
26703630506bSToomas Soome 		}
26713630506bSToomas Soome 		previous = entry;
26723630506bSToomas Soome 	}
2673313724baSColin Percival 	TSEXIT();
26743630506bSToomas Soome 	return (true);
26753630506bSToomas Soome }
26763630506bSToomas Soome 
26773630506bSToomas Soome static int
font_set(struct env_var * ev __unused,int flags __unused,const void * value)26783630506bSToomas Soome font_set(struct env_var *ev __unused, int flags __unused, const void *value)
26793630506bSToomas Soome {
26803630506bSToomas Soome 	struct fontlist *fl;
26813630506bSToomas Soome 	char *eptr;
26823630506bSToomas Soome 	unsigned long x = 0, y = 0;
26833630506bSToomas Soome 
26843630506bSToomas Soome 	/*
26853630506bSToomas Soome 	 * Attempt to extract values from "XxY" string. In case of error,
26863630506bSToomas Soome 	 * we have unmaching glyph dimensions and will just output the
26873630506bSToomas Soome 	 * available values.
26883630506bSToomas Soome 	 */
26893630506bSToomas Soome 	if (value != NULL) {
26903630506bSToomas Soome 		x = strtoul(value, &eptr, 10);
26913630506bSToomas Soome 		if (*eptr == 'x')
26923630506bSToomas Soome 			y = strtoul(eptr + 1, &eptr, 10);
26933630506bSToomas Soome 	}
26943630506bSToomas Soome 	STAILQ_FOREACH(fl, &fonts, font_next) {
26953630506bSToomas Soome 		if (fl->font_data->vfbd_width == x &&
26963630506bSToomas Soome 		    fl->font_data->vfbd_height == y)
26973630506bSToomas Soome 			break;
26983630506bSToomas Soome 	}
26993630506bSToomas Soome 	if (fl != NULL) {
27003630506bSToomas Soome 		/* Reset any FONT_MANUAL flag. */
27013630506bSToomas Soome 		reset_font_flags();
27023630506bSToomas Soome 
27033630506bSToomas Soome 		/* Mark this font manually loaded */
27043630506bSToomas Soome 		fl->font_flags = FONT_MANUAL;
27053630506bSToomas Soome 		cons_update_mode(gfx_state.tg_fb_type != FB_TEXT);
27063630506bSToomas Soome 		return (CMD_OK);
27073630506bSToomas Soome 	}
27083630506bSToomas Soome 
27093630506bSToomas Soome 	printf("Available fonts:\n");
27103630506bSToomas Soome 	STAILQ_FOREACH(fl, &fonts, font_next) {
27113630506bSToomas Soome 		printf("    %dx%d\n", fl->font_data->vfbd_width,
27123630506bSToomas Soome 		    fl->font_data->vfbd_height);
27133630506bSToomas Soome 	}
27143630506bSToomas Soome 	return (CMD_OK);
27153630506bSToomas Soome }
27163630506bSToomas Soome 
27173630506bSToomas Soome void
bios_text_font(bool use_vga_font)27183630506bSToomas Soome bios_text_font(bool use_vga_font)
27193630506bSToomas Soome {
27203630506bSToomas Soome 	if (use_vga_font)
27213630506bSToomas Soome 		(void) insert_font(VGA_8X16_FONT, FONT_MANUAL);
27223630506bSToomas Soome 	else
27233630506bSToomas Soome 		(void) insert_font(DEFAULT_8X16_FONT, FONT_MANUAL);
27243630506bSToomas Soome }
27253630506bSToomas Soome 
27263630506bSToomas Soome void
autoload_font(bool bios)27273630506bSToomas Soome autoload_font(bool bios)
27283630506bSToomas Soome {
27293630506bSToomas Soome 	struct name_list *nl;
27303630506bSToomas Soome 	struct name_entry *np;
27313630506bSToomas Soome 
2732313724baSColin Percival 	TSENTER();
2733313724baSColin Percival 
27343630506bSToomas Soome 	nl = read_list("/boot/fonts/INDEX.fonts");
27353630506bSToomas Soome 	if (nl == NULL)
27363630506bSToomas Soome 		return;
27373630506bSToomas Soome 
27383630506bSToomas Soome 	while (!SLIST_EMPTY(nl)) {
27393630506bSToomas Soome 		np = SLIST_FIRST(nl);
27403630506bSToomas Soome 		SLIST_REMOVE_HEAD(nl, n_entry);
27413630506bSToomas Soome 		if (insert_font(np->n_name, FONT_AUTO) == false)
27423630506bSToomas Soome 			printf("failed to add font: %s\n", np->n_name);
27433630506bSToomas Soome 		free(np->n_name);
27443630506bSToomas Soome 		free(np);
27453630506bSToomas Soome 	}
27463630506bSToomas Soome 
27473630506bSToomas Soome 	/*
27483630506bSToomas Soome 	 * If vga text mode was requested, load vga.font (8x16 bold) font.
27493630506bSToomas Soome 	 */
27503630506bSToomas Soome 	if (bios) {
27513630506bSToomas Soome 		bios_text_font(true);
27523630506bSToomas Soome 	}
27533630506bSToomas Soome 
27543630506bSToomas Soome 	(void) cons_update_mode(gfx_state.tg_fb_type != FB_TEXT);
2755313724baSColin Percival 
2756313724baSColin Percival 	TSEXIT();
27573630506bSToomas Soome }
27583630506bSToomas Soome 
27593630506bSToomas Soome COMMAND_SET(load_font, "loadfont", "load console font from file", command_font);
27603630506bSToomas Soome 
27613630506bSToomas Soome static int
command_font(int argc,char * argv[])27623630506bSToomas Soome command_font(int argc, char *argv[])
27633630506bSToomas Soome {
27643630506bSToomas Soome 	int i, c, rc;
27653630506bSToomas Soome 	struct fontlist *fl;
27663630506bSToomas Soome 	vt_font_bitmap_data_t *bd;
27673630506bSToomas Soome 	bool list;
27683630506bSToomas Soome 
27693630506bSToomas Soome 	list = false;
27703630506bSToomas Soome 	optind = 1;
27713630506bSToomas Soome 	optreset = 1;
27723630506bSToomas Soome 	rc = CMD_OK;
27733630506bSToomas Soome 
27743630506bSToomas Soome 	while ((c = getopt(argc, argv, "l")) != -1) {
27753630506bSToomas Soome 		switch (c) {
27763630506bSToomas Soome 		case 'l':
27773630506bSToomas Soome 			list = true;
27783630506bSToomas Soome 			break;
27793630506bSToomas Soome 		case '?':
27803630506bSToomas Soome 		default:
27813630506bSToomas Soome 			return (CMD_ERROR);
27823630506bSToomas Soome 		}
27833630506bSToomas Soome 	}
27843630506bSToomas Soome 
27853630506bSToomas Soome 	argc -= optind;
27863630506bSToomas Soome 	argv += optind;
27873630506bSToomas Soome 
27883630506bSToomas Soome 	if (argc > 1 || (list && argc != 0)) {
27893630506bSToomas Soome 		printf("Usage: loadfont [-l] | [file.fnt]\n");
27903630506bSToomas Soome 		return (CMD_ERROR);
27913630506bSToomas Soome 	}
27923630506bSToomas Soome 
27933630506bSToomas Soome 	if (list) {
27943630506bSToomas Soome 		STAILQ_FOREACH(fl, &fonts, font_next) {
27953630506bSToomas Soome 			printf("font %s: %dx%d%s\n", fl->font_name,
27963630506bSToomas Soome 			    fl->font_data->vfbd_width,
27973630506bSToomas Soome 			    fl->font_data->vfbd_height,
27983630506bSToomas Soome 			    fl->font_data->vfbd_font == NULL? "" : " loaded");
27993630506bSToomas Soome 		}
28003630506bSToomas Soome 		return (CMD_OK);
28013630506bSToomas Soome 	}
28023630506bSToomas Soome 
28033630506bSToomas Soome 	/* Clear scren */
28043630506bSToomas Soome 	cons_clear();
28053630506bSToomas Soome 
28063630506bSToomas Soome 	if (argc == 1) {
28073630506bSToomas Soome 		char *name = argv[0];
28083630506bSToomas Soome 
28093630506bSToomas Soome 		if (insert_font(name, FONT_MANUAL) == false) {
28103630506bSToomas Soome 			printf("loadfont error: failed to load: %s\n", name);
28113630506bSToomas Soome 			return (CMD_ERROR);
28123630506bSToomas Soome 		}
28133630506bSToomas Soome 
28143630506bSToomas Soome 		(void) cons_update_mode(gfx_state.tg_fb_type != FB_TEXT);
28153630506bSToomas Soome 		return (CMD_OK);
28163630506bSToomas Soome 	}
28173630506bSToomas Soome 
28183630506bSToomas Soome 	if (argc == 0) {
28193630506bSToomas Soome 		/*
28203630506bSToomas Soome 		 * Walk entire font list, release any loaded font, and set
28213630506bSToomas Soome 		 * autoload flag. The font list does have at least the builtin
28223630506bSToomas Soome 		 * default font.
28233630506bSToomas Soome 		 */
28243630506bSToomas Soome 		STAILQ_FOREACH(fl, &fonts, font_next) {
28253630506bSToomas Soome 			if (fl->font_data->vfbd_font != NULL) {
28263630506bSToomas Soome 
28273630506bSToomas Soome 				bd = fl->font_data;
28283630506bSToomas Soome 				/*
28293630506bSToomas Soome 				 * Note the setup_font() is releasing
28303630506bSToomas Soome 				 * font bytes.
28313630506bSToomas Soome 				 */
28323630506bSToomas Soome 				for (i = 0; i < VFNT_MAPS; i++)
28333630506bSToomas Soome 					free(bd->vfbd_font->vf_map[i]);
28343630506bSToomas Soome 				free(fl->font_data->vfbd_font);
28353630506bSToomas Soome 				fl->font_data->vfbd_font = NULL;
28363630506bSToomas Soome 				fl->font_data->vfbd_uncompressed_size = 0;
28373630506bSToomas Soome 				fl->font_flags = FONT_AUTO;
28383630506bSToomas Soome 			}
28393630506bSToomas Soome 		}
28403630506bSToomas Soome 		(void) cons_update_mode(gfx_state.tg_fb_type != FB_TEXT);
28413630506bSToomas Soome 	}
28423630506bSToomas Soome 	return (rc);
28433630506bSToomas Soome }
28443630506bSToomas Soome 
28453630506bSToomas Soome bool
gfx_get_edid_resolution(struct vesa_edid_info * edid,edid_res_list_t * res)28463630506bSToomas Soome gfx_get_edid_resolution(struct vesa_edid_info *edid, edid_res_list_t *res)
28473630506bSToomas Soome {
28483630506bSToomas Soome 	struct resolution *rp, *p;
28493630506bSToomas Soome 
28503630506bSToomas Soome 	/*
28513630506bSToomas Soome 	 * Walk detailed timings tables (4).
28523630506bSToomas Soome 	 */
28533630506bSToomas Soome 	if ((edid->display.supported_features
28543630506bSToomas Soome 	    & EDID_FEATURE_PREFERRED_TIMING_MODE) != 0) {
28553630506bSToomas Soome 		/* Walk detailed timing descriptors (4) */
28563630506bSToomas Soome 		for (int i = 0; i < DET_TIMINGS; i++) {
28573630506bSToomas Soome 			/*
2858b5e0a701SGordon Bergling 			 * Reserved value 0 is not used for display descriptor.
28593630506bSToomas Soome 			 */
28603630506bSToomas Soome 			if (edid->detailed_timings[i].pixel_clock == 0)
28613630506bSToomas Soome 				continue;
28623630506bSToomas Soome 			if ((rp = malloc(sizeof(*rp))) == NULL)
28633630506bSToomas Soome 				continue;
28643630506bSToomas Soome 			rp->width = GET_EDID_INFO_WIDTH(edid, i);
28653630506bSToomas Soome 			rp->height = GET_EDID_INFO_HEIGHT(edid, i);
28663630506bSToomas Soome 			if (rp->width > 0 && rp->width <= EDID_MAX_PIXELS &&
28673630506bSToomas Soome 			    rp->height > 0 && rp->height <= EDID_MAX_LINES)
28683630506bSToomas Soome 				TAILQ_INSERT_TAIL(res, rp, next);
28693630506bSToomas Soome 			else
28703630506bSToomas Soome 				free(rp);
28713630506bSToomas Soome 		}
28723630506bSToomas Soome 	}
28733630506bSToomas Soome 
28743630506bSToomas Soome 	/*
28753630506bSToomas Soome 	 * Walk standard timings list (8).
28763630506bSToomas Soome 	 */
28773630506bSToomas Soome 	for (int i = 0; i < STD_TIMINGS; i++) {
28783630506bSToomas Soome 		/* Is this field unused? */
28793630506bSToomas Soome 		if (edid->standard_timings[i] == 0x0101)
28803630506bSToomas Soome 			continue;
28813630506bSToomas Soome 
28823630506bSToomas Soome 		if ((rp = malloc(sizeof(*rp))) == NULL)
28833630506bSToomas Soome 			continue;
28843630506bSToomas Soome 
28853630506bSToomas Soome 		rp->width = HSIZE(edid->standard_timings[i]);
28863630506bSToomas Soome 		switch (RATIO(edid->standard_timings[i])) {
28873630506bSToomas Soome 		case RATIO1_1:
28883630506bSToomas Soome 			rp->height = HSIZE(edid->standard_timings[i]);
28893630506bSToomas Soome 			if (edid->header.version > 1 ||
28903630506bSToomas Soome 			    edid->header.revision > 2) {
28913630506bSToomas Soome 				rp->height = rp->height * 10 / 16;
28923630506bSToomas Soome 			}
28933630506bSToomas Soome 			break;
28943630506bSToomas Soome 		case RATIO4_3:
28953630506bSToomas Soome 			rp->height = HSIZE(edid->standard_timings[i]) * 3 / 4;
28963630506bSToomas Soome 			break;
28973630506bSToomas Soome 		case RATIO5_4:
28983630506bSToomas Soome 			rp->height = HSIZE(edid->standard_timings[i]) * 4 / 5;
28993630506bSToomas Soome 			break;
29003630506bSToomas Soome 		case RATIO16_9:
29013630506bSToomas Soome 			rp->height = HSIZE(edid->standard_timings[i]) * 9 / 16;
29023630506bSToomas Soome 			break;
29033630506bSToomas Soome 		}
29043630506bSToomas Soome 
29053630506bSToomas Soome 		/*
29063630506bSToomas Soome 		 * Create resolution list in decreasing order, except keep
29073630506bSToomas Soome 		 * first entry (preferred timing mode).
29083630506bSToomas Soome 		 */
29093630506bSToomas Soome 		TAILQ_FOREACH(p, res, next) {
29103630506bSToomas Soome 			if (p->width * p->height < rp->width * rp->height) {
29113630506bSToomas Soome 				/* Keep preferred mode first */
29123630506bSToomas Soome 				if (TAILQ_FIRST(res) == p)
29133630506bSToomas Soome 					TAILQ_INSERT_AFTER(res, p, rp, next);
29143630506bSToomas Soome 				else
29153630506bSToomas Soome 					TAILQ_INSERT_BEFORE(p, rp, next);
29163630506bSToomas Soome 				break;
29173630506bSToomas Soome 			}
29183630506bSToomas Soome 			if (TAILQ_NEXT(p, next) == NULL) {
29193630506bSToomas Soome 				TAILQ_INSERT_TAIL(res, rp, next);
29203630506bSToomas Soome 				break;
29213630506bSToomas Soome 			}
29223630506bSToomas Soome 		}
29233630506bSToomas Soome 	}
29243630506bSToomas Soome 	return (!TAILQ_EMPTY(res));
29253630506bSToomas Soome }
292618968b82SWarner Losh 
292718968b82SWarner Losh vm_offset_t
build_font_module(vm_offset_t addr)292818968b82SWarner Losh build_font_module(vm_offset_t addr)
292918968b82SWarner Losh {
293018968b82SWarner Losh 	vt_font_bitmap_data_t *bd;
293118968b82SWarner Losh 	struct vt_font *fd;
293218968b82SWarner Losh 	struct preloaded_file *fp;
293318968b82SWarner Losh 	size_t size;
293418968b82SWarner Losh 	uint32_t checksum;
293518968b82SWarner Losh 	int i;
293618968b82SWarner Losh 	struct font_info fi;
293718968b82SWarner Losh 	struct fontlist *fl;
293818968b82SWarner Losh 	uint64_t fontp;
293918968b82SWarner Losh 
294018968b82SWarner Losh 	if (STAILQ_EMPTY(&fonts))
294118968b82SWarner Losh 		return (addr);
294218968b82SWarner Losh 
294318968b82SWarner Losh 	/* We can't load first */
294418968b82SWarner Losh 	if ((file_findfile(NULL, NULL)) == NULL) {
294518968b82SWarner Losh 		printf("Can not load font module: %s\n",
294618968b82SWarner Losh 		    "the kernel is not loaded");
294718968b82SWarner Losh 		return (addr);
294818968b82SWarner Losh 	}
294918968b82SWarner Losh 
295018968b82SWarner Losh 	/* helper pointers */
295118968b82SWarner Losh 	bd = NULL;
295218968b82SWarner Losh 	STAILQ_FOREACH(fl, &fonts, font_next) {
295318968b82SWarner Losh 		if (gfx_state.tg_font.vf_width == fl->font_data->vfbd_width &&
295418968b82SWarner Losh 		    gfx_state.tg_font.vf_height == fl->font_data->vfbd_height) {
295518968b82SWarner Losh 			/*
295618968b82SWarner Losh 			 * Kernel does have better built in font.
295718968b82SWarner Losh 			 */
295818968b82SWarner Losh 			if (fl->font_flags == FONT_BUILTIN)
295918968b82SWarner Losh 				return (addr);
296018968b82SWarner Losh 
296118968b82SWarner Losh 			bd = fl->font_data;
296218968b82SWarner Losh 			break;
296318968b82SWarner Losh 		}
296418968b82SWarner Losh 	}
296518968b82SWarner Losh 	if (bd == NULL)
296618968b82SWarner Losh 		return (addr);
296718968b82SWarner Losh 	fd = bd->vfbd_font;
296818968b82SWarner Losh 
296918968b82SWarner Losh 	fi.fi_width = fd->vf_width;
297018968b82SWarner Losh 	checksum = fi.fi_width;
297118968b82SWarner Losh 	fi.fi_height = fd->vf_height;
297218968b82SWarner Losh 	checksum += fi.fi_height;
297318968b82SWarner Losh 	fi.fi_bitmap_size = bd->vfbd_uncompressed_size;
297418968b82SWarner Losh 	checksum += fi.fi_bitmap_size;
297518968b82SWarner Losh 
297618968b82SWarner Losh 	size = roundup2(sizeof (struct font_info), 8);
297718968b82SWarner Losh 	for (i = 0; i < VFNT_MAPS; i++) {
297818968b82SWarner Losh 		fi.fi_map_count[i] = fd->vf_map_count[i];
297918968b82SWarner Losh 		checksum += fi.fi_map_count[i];
298018968b82SWarner Losh 		size += fd->vf_map_count[i] * sizeof (struct vfnt_map);
298118968b82SWarner Losh 		size += roundup2(size, 8);
298218968b82SWarner Losh 	}
298318968b82SWarner Losh 	size += bd->vfbd_uncompressed_size;
298418968b82SWarner Losh 
298518968b82SWarner Losh 	fi.fi_checksum = -checksum;
298618968b82SWarner Losh 
2987*86077f4fSAhmad Khalifa 	fp = file_findfile(NULL, md_kerntype);
298818968b82SWarner Losh 	if (fp == NULL)
298918968b82SWarner Losh 		panic("can't find kernel file");
299018968b82SWarner Losh 
299118968b82SWarner Losh 	fontp = addr;
299218968b82SWarner Losh 	addr += archsw.arch_copyin(&fi, addr, sizeof (struct font_info));
299318968b82SWarner Losh 	addr = roundup2(addr, 8);
299418968b82SWarner Losh 
299518968b82SWarner Losh 	/* Copy maps. */
299618968b82SWarner Losh 	for (i = 0; i < VFNT_MAPS; i++) {
299718968b82SWarner Losh 		if (fd->vf_map_count[i] != 0) {
299818968b82SWarner Losh 			addr += archsw.arch_copyin(fd->vf_map[i], addr,
299918968b82SWarner Losh 			    fd->vf_map_count[i] * sizeof (struct vfnt_map));
300018968b82SWarner Losh 			addr = roundup2(addr, 8);
300118968b82SWarner Losh 		}
300218968b82SWarner Losh 	}
300318968b82SWarner Losh 
300418968b82SWarner Losh 	/* Copy the bitmap. */
300518968b82SWarner Losh 	addr += archsw.arch_copyin(fd->vf_bytes, addr, fi.fi_bitmap_size);
300618968b82SWarner Losh 
300718968b82SWarner Losh 	/* Looks OK so far; populate control structure */
300818968b82SWarner Losh 	file_addmetadata(fp, MODINFOMD_FONT, sizeof(fontp), &fontp);
300918968b82SWarner Losh 	return (addr);
301018968b82SWarner Losh }
301100460cc8SEmmanuel Vadot 
301200460cc8SEmmanuel Vadot vm_offset_t
build_splash_module(vm_offset_t addr)301300460cc8SEmmanuel Vadot build_splash_module(vm_offset_t addr)
301400460cc8SEmmanuel Vadot {
301500460cc8SEmmanuel Vadot 	struct preloaded_file *fp;
301600460cc8SEmmanuel Vadot 	struct splash_info si;
301700460cc8SEmmanuel Vadot 	const char *splash;
301800460cc8SEmmanuel Vadot 	png_t png;
301900460cc8SEmmanuel Vadot 	uint64_t splashp;
302000460cc8SEmmanuel Vadot 	int error;
302100460cc8SEmmanuel Vadot 
302200460cc8SEmmanuel Vadot 	/* We can't load first */
302300460cc8SEmmanuel Vadot 	if ((file_findfile(NULL, NULL)) == NULL) {
302400460cc8SEmmanuel Vadot 		printf("Can not load splash module: %s\n",
302500460cc8SEmmanuel Vadot 		    "the kernel is not loaded");
302600460cc8SEmmanuel Vadot 		return (addr);
302700460cc8SEmmanuel Vadot 	}
302800460cc8SEmmanuel Vadot 
3029*86077f4fSAhmad Khalifa 	fp = file_findfile(NULL, md_kerntype);
303000460cc8SEmmanuel Vadot 	if (fp == NULL)
303100460cc8SEmmanuel Vadot 		panic("can't find kernel file");
303200460cc8SEmmanuel Vadot 
303300460cc8SEmmanuel Vadot 	splash = getenv("splash");
303400460cc8SEmmanuel Vadot 	if (splash == NULL)
303500460cc8SEmmanuel Vadot 		return (addr);
303600460cc8SEmmanuel Vadot 
303700460cc8SEmmanuel Vadot 	/* Parse png */
303800460cc8SEmmanuel Vadot 	if ((error = png_open(&png, splash)) != PNG_NO_ERROR) {
303900460cc8SEmmanuel Vadot 		return (addr);
304000460cc8SEmmanuel Vadot 	}
304100460cc8SEmmanuel Vadot 
304200460cc8SEmmanuel Vadot 	si.si_width = png.width;
304300460cc8SEmmanuel Vadot 	si.si_height = png.height;
304400460cc8SEmmanuel Vadot 	si.si_depth = png.bpp;
304500460cc8SEmmanuel Vadot 	splashp = addr;
304600460cc8SEmmanuel Vadot 	addr += archsw.arch_copyin(&si, addr, sizeof (struct splash_info));
304700460cc8SEmmanuel Vadot 	addr = roundup2(addr, 8);
304800460cc8SEmmanuel Vadot 
304900460cc8SEmmanuel Vadot 	/* Copy the bitmap. */
305000460cc8SEmmanuel Vadot 	addr += archsw.arch_copyin(png.image, addr, png.png_datalen);
305100460cc8SEmmanuel Vadot 
305200460cc8SEmmanuel Vadot 	printf("Loading splash ok\n");
305300460cc8SEmmanuel Vadot 	file_addmetadata(fp, MODINFOMD_SPLASH, sizeof(splashp), &splashp);
305400460cc8SEmmanuel Vadot 	return (addr);
305500460cc8SEmmanuel Vadot }
3056