xref: /illumos-gate/usr/src/common/ficl/emu/gfx_fb.c (revision c0bb4f7308f9d7f3f31eb636c73ef07c6d19b5f3)
10bead3caSToomas Soome /*
20bead3caSToomas Soome  * This file and its contents are supplied under the terms of the
30bead3caSToomas Soome  * Common Development and Distribution License ("CDDL"), version 1.0.
40bead3caSToomas Soome  * You may only use this file in accordance with the terms of version
50bead3caSToomas Soome  * 1.0 of the CDDL.
60bead3caSToomas Soome  *
70bead3caSToomas Soome  * A full copy of the text of the CDDL should have accompanied this
80bead3caSToomas Soome  * source.  A copy of the CDDL is also available via the Internet at
90bead3caSToomas Soome  * http://www.illumos.org/license/CDDL.
100bead3caSToomas Soome  */
110bead3caSToomas Soome 
120bead3caSToomas Soome /*
130bead3caSToomas Soome  * Copyright 2016 Toomas Soome <tsoome@me.com>
140bead3caSToomas Soome  */
150bead3caSToomas Soome 
160bead3caSToomas Soome /*
170bead3caSToomas Soome  * Graphics support for loader emulation.
180bead3caSToomas Soome  * The interface in loader and here needs some more development.
190bead3caSToomas Soome  * We can get colormap from gfx_private, but loader is currently
200bead3caSToomas Soome  * relying on tem fg/bg colors for drawing, once the menu code
210bead3caSToomas Soome  * will get some facelift, we would need to provide colors as menu component
220bead3caSToomas Soome  * attributes and stop depending on tem.
230bead3caSToomas Soome  */
240bead3caSToomas Soome 
250bead3caSToomas Soome #include <stdio.h>
260bead3caSToomas Soome #include <stdlib.h>
270bead3caSToomas Soome #include <stdbool.h>
280bead3caSToomas Soome #include <sys/types.h>
290bead3caSToomas Soome #include <sys/stat.h>
300bead3caSToomas Soome #include <fcntl.h>
310bead3caSToomas Soome #include <unistd.h>
320bead3caSToomas Soome #include <sys/mman.h>
330bead3caSToomas Soome #include <sys/fbio.h>
340bead3caSToomas Soome #include <string.h>
350bead3caSToomas Soome #include "gfx_fb.h"
360bead3caSToomas Soome 
370bead3caSToomas Soome struct framebuffer fb;
380bead3caSToomas Soome 
390bead3caSToomas Soome #define	max(x, y)	((x) >= (y) ? (x) : (y))
400bead3caSToomas Soome 
410bead3caSToomas Soome static void gfx_fb_cons_display(uint32_t, uint32_t,
420bead3caSToomas Soome     uint32_t, uint32_t, uint8_t *);
430bead3caSToomas Soome 
440bead3caSToomas Soome /* This colormap should be replaced by colormap query from kernel */
450bead3caSToomas Soome typedef struct {
460bead3caSToomas Soome 	uint8_t red[16];
470bead3caSToomas Soome 	uint8_t green[16];
480bead3caSToomas Soome 	uint8_t blue[16];
490bead3caSToomas Soome } text_cmap_t;
500bead3caSToomas Soome 
510bead3caSToomas Soome text_cmap_t cmap4_to_24 = {
520bead3caSToomas Soome /* BEGIN CSTYLED */
530bead3caSToomas Soome /*             0    1    2    3    4    5    6    7    8    9   10   11   12   13   14   15
540bead3caSToomas Soome               Wh+  Bk   Bl   Gr   Cy   Rd   Mg   Br   Wh   Bk+  Bl+  Gr+  Cy+  Rd+  Mg+  Yw */
550bead3caSToomas Soome   .red   = {0xff,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x40,0x00,0x00,0x00,0xff,0xff,0xff},
560bead3caSToomas Soome   .green = {0xff,0x00,0x00,0x80,0x80,0x00,0x00,0x80,0x80,0x40,0x00,0xff,0xff,0x00,0x00,0xff},
570bead3caSToomas Soome   .blue  = {0xff,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x40,0xff,0x00,0xff,0x00,0xff,0x00}
580bead3caSToomas Soome /* END CSTYLED */
590bead3caSToomas Soome };
600bead3caSToomas Soome 
610bead3caSToomas Soome const uint8_t solaris_color_to_pc_color[16] = {
620bead3caSToomas Soome     15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
630bead3caSToomas Soome };
640bead3caSToomas Soome 
650bead3caSToomas Soome void
660bead3caSToomas Soome gfx_framework_init(void)
670bead3caSToomas Soome {
680bead3caSToomas Soome 	struct fbgattr attr;
690bead3caSToomas Soome 	struct gfxfb_info *gfxfb_info;
700bead3caSToomas Soome 	char buf[10];
710bead3caSToomas Soome 
720bead3caSToomas Soome 	fb.fd = open("/dev/fb", O_RDWR);
730bead3caSToomas Soome 	if (fb.fd < 0)
740bead3caSToomas Soome 		return;
750bead3caSToomas Soome 
760bead3caSToomas Soome 	/* make sure we have GFX framebuffer */
770bead3caSToomas Soome 	if (ioctl(fb.fd, VIS_GETIDENTIFIER, &fb.ident) < 0 ||
780bead3caSToomas Soome 	    strcmp(fb.ident.name, "illumos_fb") != 0) {
79*c0bb4f73SToomas Soome 		(void) close(fb.fd);
800bead3caSToomas Soome 		fb.fd = -1;
810bead3caSToomas Soome 		return;
820bead3caSToomas Soome 	}
830bead3caSToomas Soome 
840bead3caSToomas Soome 	if (ioctl(fb.fd, FBIOGATTR, &attr) < 0) {
85*c0bb4f73SToomas Soome 		(void) close(fb.fd);
860bead3caSToomas Soome 		fb.fd = -1;
870bead3caSToomas Soome 		return;
880bead3caSToomas Soome 	}
890bead3caSToomas Soome 	gfxfb_info = (struct gfxfb_info *)attr.sattr.dev_specific;
900bead3caSToomas Soome 
910bead3caSToomas Soome 	fb.fb_height = attr.fbtype.fb_height;
920bead3caSToomas Soome 	fb.fb_width = attr.fbtype.fb_width;
930bead3caSToomas Soome 	fb.fb_depth = attr.fbtype.fb_depth;
940bead3caSToomas Soome 	fb.fb_size = attr.fbtype.fb_size;
950bead3caSToomas Soome 	fb.fb_bpp = attr.fbtype.fb_depth >> 3;
960bead3caSToomas Soome 	if (attr.fbtype.fb_depth == 15)
970bead3caSToomas Soome 		fb.fb_bpp = 2;
980bead3caSToomas Soome 	fb.fb_pitch = gfxfb_info->pitch;
990bead3caSToomas Soome 	fb.terminal_origin_x = gfxfb_info->terminal_origin_x;
1000bead3caSToomas Soome 	fb.terminal_origin_y = gfxfb_info->terminal_origin_y;
1010bead3caSToomas Soome 	fb.font_width = gfxfb_info->font_width;
1020bead3caSToomas Soome 	fb.font_height = gfxfb_info->font_height;
1030bead3caSToomas Soome 
1040bead3caSToomas Soome 	fb.red_mask_size = gfxfb_info->red_mask_size;
1050bead3caSToomas Soome 	fb.red_field_position = gfxfb_info->red_field_position;
1060bead3caSToomas Soome 	fb.green_mask_size = gfxfb_info->green_mask_size;
1070bead3caSToomas Soome 	fb.green_field_position = gfxfb_info->green_field_position;
1080bead3caSToomas Soome 	fb.blue_mask_size = gfxfb_info->blue_mask_size;
1090bead3caSToomas Soome 	fb.blue_field_position = gfxfb_info->blue_field_position;
1100bead3caSToomas Soome 
1110bead3caSToomas Soome 	fb.fb_addr = (uint8_t *)mmap(0, fb.fb_size, (PROT_READ | PROT_WRITE),
1120bead3caSToomas Soome 	    MAP_SHARED, fb.fd, 0);
1130bead3caSToomas Soome 
1140bead3caSToomas Soome 	if (fb.fb_addr == NULL) {
115*c0bb4f73SToomas Soome 		(void) close(fb.fd);
1160bead3caSToomas Soome 		fb.fd = -1;
1170bead3caSToomas Soome 		return;
1180bead3caSToomas Soome 	}
119*c0bb4f73SToomas Soome 	(void) snprintf(buf, sizeof (buf), "%d", fb.fb_height);
120*c0bb4f73SToomas Soome 	(void) setenv("screen-height", buf, 1);
121*c0bb4f73SToomas Soome 	(void) snprintf(buf, sizeof (buf), "%d", fb.fb_width);
122*c0bb4f73SToomas Soome 	(void) setenv("screen-width", buf, 1);
1230bead3caSToomas Soome }
1240bead3caSToomas Soome 
1250bead3caSToomas Soome void
1260bead3caSToomas Soome gfx_framework_fini(void)
1270bead3caSToomas Soome {
1280bead3caSToomas Soome 	if (fb.fd < 0)
1290bead3caSToomas Soome 		return;
1300bead3caSToomas Soome 
1310bead3caSToomas Soome 	(void) munmap((caddr_t)fb.fb_addr, fb.fb_size);
132*c0bb4f73SToomas Soome 	(void) close(fb.fd);
1330bead3caSToomas Soome 	fb.fd = -1;
1340bead3caSToomas Soome }
1350bead3caSToomas Soome 
1360bead3caSToomas Soome static int
1370bead3caSToomas Soome isqrt(int num)
1380bead3caSToomas Soome {
1390bead3caSToomas Soome 	int res = 0;
1400bead3caSToomas Soome 	int bit = 1 << 30;
1410bead3caSToomas Soome 
1420bead3caSToomas Soome 	/* "bit" starts at the highest power of four <= the argument. */
1430bead3caSToomas Soome 	while (bit > num)
1440bead3caSToomas Soome 		bit >>= 2;
1450bead3caSToomas Soome 
1460bead3caSToomas Soome 	while (bit != 0) {
1470bead3caSToomas Soome 		if (num >= res + bit) {
1480bead3caSToomas Soome 			num -= res + bit;
1490bead3caSToomas Soome 			res = (res >> 1) + bit;
1500bead3caSToomas Soome 		} else {
1510bead3caSToomas Soome 			res >>= 1;
1520bead3caSToomas Soome 		}
1530bead3caSToomas Soome 		bit >>= 2;
1540bead3caSToomas Soome 	}
1550bead3caSToomas Soome 	return (res);
1560bead3caSToomas Soome }
1570bead3caSToomas Soome 
1580bead3caSToomas Soome void
1590bead3caSToomas Soome gfx_fb_setpixel(uint32_t x, uint32_t y)
1600bead3caSToomas Soome {
1610bead3caSToomas Soome 	uint32_t c, offset;
1620bead3caSToomas Soome 
1630bead3caSToomas Soome 	if (fb.fd < 0)
1640bead3caSToomas Soome 		return;
1650bead3caSToomas Soome 	c = 0;		/* black */
1660bead3caSToomas Soome 
1670bead3caSToomas Soome 	if (x >= fb.fb_width || y >= fb.fb_height)
1680bead3caSToomas Soome 		return;
1690bead3caSToomas Soome 
1700bead3caSToomas Soome 	offset = y * fb.fb_pitch + x * fb.fb_bpp;
1710bead3caSToomas Soome 	switch (fb.fb_depth) {
1720bead3caSToomas Soome 	case 8:
1730bead3caSToomas Soome 		fb.fb_addr[offset] = c & 0xff;
1740bead3caSToomas Soome 		break;
1750bead3caSToomas Soome 	case 15:
1760bead3caSToomas Soome 	case 16:
1770bead3caSToomas Soome 		*(uint16_t *)(fb.fb_addr + offset) = c & 0xffff;
1780bead3caSToomas Soome 		break;
1790bead3caSToomas Soome 	case 24:
1800bead3caSToomas Soome 		fb.fb_addr[offset] = (c >> 16) & 0xff;
1810bead3caSToomas Soome 		fb.fb_addr[offset + 1] = (c >> 8) & 0xff;
1820bead3caSToomas Soome 		fb.fb_addr[offset + 2] = c & 0xff;
1830bead3caSToomas Soome 		break;
1840bead3caSToomas Soome 	case 32:
1850bead3caSToomas Soome 		*(uint32_t *)(fb.fb_addr + offset) = c;
1860bead3caSToomas Soome 		break;
1870bead3caSToomas Soome 	}
1880bead3caSToomas Soome }
1890bead3caSToomas Soome 
1900bead3caSToomas Soome void
1910bead3caSToomas Soome gfx_fb_drawrect(uint32_t x1, uint32_t y1, uint32_t x2, uint32_t y2,
1920bead3caSToomas Soome     uint32_t fill)
1930bead3caSToomas Soome {
1940bead3caSToomas Soome 	int x, y;
1950bead3caSToomas Soome 
1960bead3caSToomas Soome 	if (fb.fd < 0)
1970bead3caSToomas Soome 		return;
1980bead3caSToomas Soome 
1990bead3caSToomas Soome 	for (y = y1; y <= y2; y++) {
2000bead3caSToomas Soome 		if (fill || (y == y1) || (y == y2)) {
2010bead3caSToomas Soome 			for (x = x1; x <= x2; x++)
2020bead3caSToomas Soome 				gfx_fb_setpixel(x, y);
2030bead3caSToomas Soome 		} else {
2040bead3caSToomas Soome 			gfx_fb_setpixel(x1, y);
2050bead3caSToomas Soome 			gfx_fb_setpixel(x2, y);
2060bead3caSToomas Soome 		}
2070bead3caSToomas Soome 	}
2080bead3caSToomas Soome }
2090bead3caSToomas Soome 
2100bead3caSToomas Soome void
2110bead3caSToomas Soome gfx_term_drawrect(uint32_t row1, uint32_t col1, uint32_t row2, uint32_t col2)
2120bead3caSToomas Soome {
2130bead3caSToomas Soome 	int x1, y1, x2, y2;
2140bead3caSToomas Soome 	int xshift, yshift;
2150bead3caSToomas Soome 	int width, i;
2160bead3caSToomas Soome 
2170bead3caSToomas Soome 	if (fb.fd < 0)
2180bead3caSToomas Soome 		return;
2190bead3caSToomas Soome 
2200bead3caSToomas Soome 	width = fb.font_width / 4;	/* line width */
2210bead3caSToomas Soome 	xshift = (fb.font_width - width) / 2;
2220bead3caSToomas Soome 	yshift = (fb.font_height - width) / 2;
2230bead3caSToomas Soome 	/* Terminal coordinates start from (1,1) */
2240bead3caSToomas Soome 	row1--;
2250bead3caSToomas Soome 	col1--;
2260bead3caSToomas Soome 	row2--;
2270bead3caSToomas Soome 	col2--;
2280bead3caSToomas Soome 
2290bead3caSToomas Soome 	/*
2300bead3caSToomas Soome 	 * Draw horizontal lines width points thick, shifted from outer edge.
2310bead3caSToomas Soome 	 */
2320bead3caSToomas Soome 	x1 = (row1 + 1) * fb.font_width + fb.terminal_origin_x;
2330bead3caSToomas Soome 	y1 = col1 * fb.font_height + fb.terminal_origin_y + yshift;
2340bead3caSToomas Soome 	x2 = row2 * fb.font_width + fb.terminal_origin_x;
2350bead3caSToomas Soome 	gfx_fb_drawrect(x1, y1, x2, y1 + width, 1);
2360bead3caSToomas Soome 	y2 = col2 * fb.font_height + fb.terminal_origin_y;
2370bead3caSToomas Soome 	y2 += fb.font_height - yshift - width;
2380bead3caSToomas Soome 	gfx_fb_drawrect(x1, y2, x2, y2 + width, 1);
2390bead3caSToomas Soome 
2400bead3caSToomas Soome 	/*
2410bead3caSToomas Soome 	 * Draw vertical lines width points thick, shifted from outer edge.
2420bead3caSToomas Soome 	 */
2430bead3caSToomas Soome 	x1 = row1 * fb.font_width + fb.terminal_origin_x + xshift;
2440bead3caSToomas Soome 	y1 = col1 * fb.font_height + fb.terminal_origin_y;
2450bead3caSToomas Soome 	y1 += fb.font_height;
2460bead3caSToomas Soome 	y2 = col2 * fb.font_height + fb.terminal_origin_y;
2470bead3caSToomas Soome 	gfx_fb_drawrect(x1, y1, x1 + width, y2, 1);
2480bead3caSToomas Soome 	x1 = row2 * fb.font_width + fb.terminal_origin_x;
2490bead3caSToomas Soome 	x1 += fb.font_width - xshift - width;
2500bead3caSToomas Soome 	gfx_fb_drawrect(x1, y1, x1 + width, y2, 1);
2510bead3caSToomas Soome 
2520bead3caSToomas Soome 	/* Draw upper left corner. */
2530bead3caSToomas Soome 	x1 = row1 * fb.font_width + fb.terminal_origin_x + xshift;
2540bead3caSToomas Soome 	y1 = col1 * fb.font_height + fb.terminal_origin_y;
2550bead3caSToomas Soome 	y1 += fb.font_height;
2560bead3caSToomas Soome 
2570bead3caSToomas Soome 	x2 = row1 * fb.font_width + fb.terminal_origin_x;
2580bead3caSToomas Soome 	x2 += fb.font_width;
2590bead3caSToomas Soome 	y2 = col1 * fb.font_height + fb.terminal_origin_y + yshift;
2600bead3caSToomas Soome 	for (i = 0; i <= width; i++)
2610bead3caSToomas Soome 		gfx_fb_bezier(x1 + i, y1, x1 + i, y2 + i, x2, y2 + i, width-i);
2620bead3caSToomas Soome 
2630bead3caSToomas Soome 	/* Draw lower left corner. */
2640bead3caSToomas Soome 	x1 = row1 * fb.font_width + fb.terminal_origin_x;
2650bead3caSToomas Soome 	x1 += fb.font_width;
2660bead3caSToomas Soome 	y1 = col2 * fb.font_height + fb.terminal_origin_y;
2670bead3caSToomas Soome 	y1 += fb.font_height - yshift;
2680bead3caSToomas Soome 	x2 = row1 * fb.font_width + fb.terminal_origin_x + xshift;
2690bead3caSToomas Soome 	y2 = col2 * fb.font_height + fb.terminal_origin_y;
2700bead3caSToomas Soome 	for (i = 0; i <= width; i++)
2710bead3caSToomas Soome 		gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i);
2720bead3caSToomas Soome 
2730bead3caSToomas Soome 	/* Draw upper right corner. */
2740bead3caSToomas Soome 	x1 = row2 * fb.font_width + fb.terminal_origin_x;
2750bead3caSToomas Soome 	y1 = col1 * fb.font_height + fb.terminal_origin_y + yshift;
2760bead3caSToomas Soome 	x2 = row2 * fb.font_width + fb.terminal_origin_x;
2770bead3caSToomas Soome 	x2 += fb.font_width - xshift - width;
2780bead3caSToomas Soome 	y2 = col1 * fb.font_height + fb.terminal_origin_y;
2790bead3caSToomas Soome 	y2 += fb.font_height;
2800bead3caSToomas Soome 	for (i = 0; i <= width; i++)
2810bead3caSToomas Soome 		gfx_fb_bezier(x1, y1 + i, x2 + i, y1 + i, x2 + i, y2, width-i);
2820bead3caSToomas Soome 
2830bead3caSToomas Soome 	/* Draw lower right corner. */
2840bead3caSToomas Soome 	x1 = row2 * fb.font_width + fb.terminal_origin_x;
2850bead3caSToomas Soome 	y1 = col2 * fb.font_height + fb.terminal_origin_y;
2860bead3caSToomas Soome 	y1 += fb.font_height - yshift;
2870bead3caSToomas Soome 	x2 = row2 * fb.font_width + fb.terminal_origin_x;
2880bead3caSToomas Soome 	x2 += fb.font_width - xshift - width;
2890bead3caSToomas Soome 	y2 = col2 * fb.font_height + fb.terminal_origin_y;
2900bead3caSToomas Soome 	for (i = 0; i <= width; i++)
2910bead3caSToomas Soome 		gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i);
2920bead3caSToomas Soome }
2930bead3caSToomas Soome 
2940bead3caSToomas Soome void
2950bead3caSToomas Soome gfx_fb_line(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t width)
2960bead3caSToomas Soome {
2970bead3caSToomas Soome 	int dx, sx, dy, sy;
2980bead3caSToomas Soome 	int err, e2, x2, y2, ed;
2990bead3caSToomas Soome 
3000bead3caSToomas Soome 	if (fb.fd < 0)
3010bead3caSToomas Soome 		return;
3020bead3caSToomas Soome 
3030bead3caSToomas Soome 	sx = x0 < x1? 1 : -1;
3040bead3caSToomas Soome 	sy = y0 < y1? 1 : -1;
3050bead3caSToomas Soome 	dx = abs(x1 - x0);
3060bead3caSToomas Soome 	dy = abs(y1 - y0);
3070bead3caSToomas Soome 	err = dx - dy;
3080bead3caSToomas Soome 	ed = dx + dy == 0 ? 1 : isqrt(dx * dx + dy * dy);
3090bead3caSToomas Soome 
3100bead3caSToomas Soome 	if (dx != 0 && dy != 0)
3110bead3caSToomas Soome 		width = (width + 1) >> 1;
3120bead3caSToomas Soome 
3130bead3caSToomas Soome 	for (;;) {
3140bead3caSToomas Soome 		gfx_fb_setpixel(x0, y0);
3150bead3caSToomas Soome 		e2 = err;
3160bead3caSToomas Soome 		x2 = x0;
3170bead3caSToomas Soome 		if ((e2 << 1) >= -dx) {		/* x step */
3180bead3caSToomas Soome 			e2 += dy;
3190bead3caSToomas Soome 			y2 = y0;
3200bead3caSToomas Soome 			while (e2 < ed * width && (y1 != y2 || dx > dy)) {
3210bead3caSToomas Soome 				y2 += sy;
3220bead3caSToomas Soome 				gfx_fb_setpixel(x0, y2);
3230bead3caSToomas Soome 				e2 += dx;
3240bead3caSToomas Soome 			}
3250bead3caSToomas Soome 			if (x0 == x1)
3260bead3caSToomas Soome 				break;
3270bead3caSToomas Soome 			e2 = err;
3280bead3caSToomas Soome 			err -= dy;
3290bead3caSToomas Soome 			x0 += sx;
3300bead3caSToomas Soome 		}
3310bead3caSToomas Soome 		if ((e2 << 1) <= dy) {		/* y step */
3320bead3caSToomas Soome 			e2 = dx-e2;
3330bead3caSToomas Soome 			while (e2 < ed * width && (x1 != x2 || dx < dy)) {
3340bead3caSToomas Soome 				x2 += sx;
3350bead3caSToomas Soome 				gfx_fb_setpixel(x2, y0);
3360bead3caSToomas Soome 				e2 += dy;
3370bead3caSToomas Soome 			}
3380bead3caSToomas Soome 			if (y0 == y1)
3390bead3caSToomas Soome 				break;
3400bead3caSToomas Soome 			err += dx;
3410bead3caSToomas Soome 			y0 += sy;
3420bead3caSToomas Soome 		}
3430bead3caSToomas Soome 	}
3440bead3caSToomas Soome }
3450bead3caSToomas Soome 
3460bead3caSToomas Soome void
3470bead3caSToomas Soome gfx_fb_bezier(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t x2,
3480bead3caSToomas Soome     uint32_t y2, uint32_t wd)
3490bead3caSToomas Soome {
3500bead3caSToomas Soome 	int sx, sy, xx, yy, xy, width;
3510bead3caSToomas Soome 	int dx, dy, err, curvature;
3520bead3caSToomas Soome 	int i;
3530bead3caSToomas Soome 
3540bead3caSToomas Soome 	if (fb.fd < 0)
3550bead3caSToomas Soome 		return;
3560bead3caSToomas Soome 
3570bead3caSToomas Soome 	width = wd;
3580bead3caSToomas Soome 	sx = x2 - x1;
3590bead3caSToomas Soome 	sy = y2 - y1;
3600bead3caSToomas Soome 	xx = x0 - x1;
3610bead3caSToomas Soome 	yy = y0 - y1;
3620bead3caSToomas Soome 	curvature = xx*sy - yy*sx;
3630bead3caSToomas Soome 
3640bead3caSToomas Soome 	if (sx * sx + sy * sy > xx * xx + yy * yy) {
3650bead3caSToomas Soome 		x2 = x0;
3660bead3caSToomas Soome 		x0 = sx + x1;
3670bead3caSToomas Soome 		y2 = y0;
3680bead3caSToomas Soome 		y0 = sy + y1;
3690bead3caSToomas Soome 		curvature = -curvature;
3700bead3caSToomas Soome 	}
3710bead3caSToomas Soome 	if (curvature != 0) {
3720bead3caSToomas Soome 		xx += sx;
3730bead3caSToomas Soome 		sx = x0 < x2? 1 : -1;
3740bead3caSToomas Soome 		xx *= sx;
3750bead3caSToomas Soome 		yy += sy;
3760bead3caSToomas Soome 		sy = y0 < y2? 1 : -1;
3770bead3caSToomas Soome 		yy *= sy;
3780bead3caSToomas Soome 		xy = 2 * xx * yy;
3790bead3caSToomas Soome 		xx *= xx;
3800bead3caSToomas Soome 		yy *= yy;
3810bead3caSToomas Soome 		if (curvature * sx * sy < 0) {
3820bead3caSToomas Soome 			xx = -xx;
3830bead3caSToomas Soome 			yy = -yy;
3840bead3caSToomas Soome 			xy = -xy;
3850bead3caSToomas Soome 			curvature = -curvature;
3860bead3caSToomas Soome 		}
3870bead3caSToomas Soome 		dx = 4 * sy * curvature * (x1 - x0) + xx - xy;
3880bead3caSToomas Soome 		dy = 4 * sx * curvature * (y0 - y1) + yy - xy;
3890bead3caSToomas Soome 		xx += xx;
3900bead3caSToomas Soome 		yy += yy;
3910bead3caSToomas Soome 		err = dx + dy + xy;
3920bead3caSToomas Soome 		do {
3930bead3caSToomas Soome 			for (i = 0; i <= width; i++)
3940bead3caSToomas Soome 				gfx_fb_setpixel(x0 + i, y0);
3950bead3caSToomas Soome 			if (x0 == x2 && y0 == y2)
3960bead3caSToomas Soome 				return;  /* last pixel -> curve finished */
3970bead3caSToomas Soome 			y1 = 2 * err < dx;
3980bead3caSToomas Soome 			if (2 * err > dy) {
3990bead3caSToomas Soome 				x0 += sx;
4000bead3caSToomas Soome 				dx -= xy;
4010bead3caSToomas Soome 				dy += yy;
4020bead3caSToomas Soome 				err += dy;
4030bead3caSToomas Soome 			}
4040bead3caSToomas Soome 			if (y1 != 0) {
4050bead3caSToomas Soome 				y0 += sy;
4060bead3caSToomas Soome 				dy -= xy;
4070bead3caSToomas Soome 				dx += xx;
4080bead3caSToomas Soome 				err += dx;
4090bead3caSToomas Soome 			}
4100bead3caSToomas Soome 		} while (dy < dx); /* gradient negates -> algorithm fails */
4110bead3caSToomas Soome 	}
4120bead3caSToomas Soome 	gfx_fb_line(x0, y0, x2, y2, width);
4130bead3caSToomas Soome }
4140bead3caSToomas Soome 
4150bead3caSToomas Soome #define	FL_PUTIMAGE_BORDER	0x1
4160bead3caSToomas Soome #define	FL_PUTIMAGE_NOSCROLL	0x2
4170bead3caSToomas Soome #define	FL_PUTIMAGE_DEBUG	0x80
4180bead3caSToomas Soome 
4190bead3caSToomas Soome int
4200bead3caSToomas Soome gfx_fb_putimage(png_t *png, uint32_t ux1, uint32_t uy1, uint32_t ux2,
4210bead3caSToomas Soome     uint32_t uy2, uint32_t flags)
4220bead3caSToomas Soome {
4230bead3caSToomas Soome 	uint32_t i, j, x, y, fheight, fwidth, color;
4240bead3caSToomas Soome 	uint8_t r, g, b, a, *p, *data;
4250bead3caSToomas Soome 	bool scale = false;
4260bead3caSToomas Soome 	bool trace = false;
4270bead3caSToomas Soome 
4280bead3caSToomas Soome 	trace = (flags & FL_PUTIMAGE_DEBUG) != 0;
4290bead3caSToomas Soome 
4300bead3caSToomas Soome 	if (fb.fd < 0) {
4310bead3caSToomas Soome 		if (trace)
4320bead3caSToomas Soome 			printf("Framebuffer not active.\n");
4330bead3caSToomas Soome 		return (1);
4340bead3caSToomas Soome 	}
4350bead3caSToomas Soome 
4360bead3caSToomas Soome 	if (png->color_type != PNG_TRUECOLOR_ALPHA) {
4370bead3caSToomas Soome 		if (trace)
4380bead3caSToomas Soome 			printf("Not truecolor image.\n");
4390bead3caSToomas Soome 		return (1);
4400bead3caSToomas Soome 	}
4410bead3caSToomas Soome 
4420bead3caSToomas Soome 	if (ux1 > fb.fb_width || uy1 > fb.fb_height) {
4430bead3caSToomas Soome 		if (trace)
4440bead3caSToomas Soome 			printf("Top left coordinate off screen.\n");
4450bead3caSToomas Soome 		return (1);
4460bead3caSToomas Soome 	}
4470bead3caSToomas Soome 
4480bead3caSToomas Soome 	if (png->width > UINT16_MAX || png->height > UINT16_MAX) {
4490bead3caSToomas Soome 		if (trace)
4500bead3caSToomas Soome 			printf("Image too large.\n");
4510bead3caSToomas Soome 		return (1);
4520bead3caSToomas Soome 	}
4530bead3caSToomas Soome 
4540bead3caSToomas Soome 	if (png->width < 1 || png->height < 1) {
4550bead3caSToomas Soome 		if (trace)
4560bead3caSToomas Soome 			printf("Image too small.\n");
4570bead3caSToomas Soome 		return (1);
4580bead3caSToomas Soome 	}
4590bead3caSToomas Soome 
4600bead3caSToomas Soome 	/*
4610bead3caSToomas Soome 	 * If 0 was passed for either ux2 or uy2, then calculate the missing
4620bead3caSToomas Soome 	 * part of the bottom right coordinate.
4630bead3caSToomas Soome 	 */
4640bead3caSToomas Soome 	scale = true;
4650bead3caSToomas Soome 	if (ux2 == 0 && uy2 == 0) {
4660bead3caSToomas Soome 		/* Both 0, use the native resolution of the image */
4670bead3caSToomas Soome 		ux2 = ux1 + png->width;
4680bead3caSToomas Soome 		uy2 = uy1 + png->height;
4690bead3caSToomas Soome 		scale = false;
4700bead3caSToomas Soome 	} else if (ux2 == 0) {
4710bead3caSToomas Soome 		/* Set ux2 from uy2/uy1 to maintain aspect ratio */
4720bead3caSToomas Soome 		ux2 = ux1 + (png->width * (uy2 - uy1)) / png->height;
4730bead3caSToomas Soome 	} else if (uy2 == 0) {
4740bead3caSToomas Soome 		/* Set uy2 from ux2/ux1 to maintain aspect ratio */
4750bead3caSToomas Soome 		uy2 = uy1 + (png->height * (ux2 - ux1)) / png->width;
4760bead3caSToomas Soome 	}
4770bead3caSToomas Soome 
4780bead3caSToomas Soome 	if (ux2 > fb.fb_width || uy2 > fb.fb_height) {
4790bead3caSToomas Soome 		if (trace)
4800bead3caSToomas Soome 			printf("Bottom right coordinate off screen.\n");
4810bead3caSToomas Soome 		return (1);
4820bead3caSToomas Soome 	}
4830bead3caSToomas Soome 
4840bead3caSToomas Soome 	fwidth = ux2 - ux1;
4850bead3caSToomas Soome 	fheight = uy2 - uy1;
4860bead3caSToomas Soome 
4870bead3caSToomas Soome 	/*
4880bead3caSToomas Soome 	 * If the original image dimensions have been passed explicitly,
4890bead3caSToomas Soome 	 * disable scaling.
4900bead3caSToomas Soome 	 */
4910bead3caSToomas Soome 	if (fwidth == png->width && fheight == png->height)
4920bead3caSToomas Soome 		scale = false;
4930bead3caSToomas Soome 
4940bead3caSToomas Soome 	if (ux1 == 0) {
4950bead3caSToomas Soome 		/*
4960bead3caSToomas Soome 		 * No top left X co-ordinate (real coordinates start at 1),
4970bead3caSToomas Soome 		 * place as far right as it will fit.
4980bead3caSToomas Soome 		 */
4990bead3caSToomas Soome 		ux2 = fb.fb_width - fb.terminal_origin_x;
5000bead3caSToomas Soome 		ux1 = ux2 - fwidth;
5010bead3caSToomas Soome 	}
5020bead3caSToomas Soome 
5030bead3caSToomas Soome 	if (uy1 == 0) {
5040bead3caSToomas Soome 		/*
5050bead3caSToomas Soome 		 * No top left Y co-ordinate (real coordinates start at 1),
5060bead3caSToomas Soome 		 * place as far down as it will fit.
5070bead3caSToomas Soome 		 */
5080bead3caSToomas Soome 		uy2 = fb.fb_height - fb.terminal_origin_y;
5090bead3caSToomas Soome 		uy1 = uy2 - fheight;
5100bead3caSToomas Soome 	}
5110bead3caSToomas Soome 
5120bead3caSToomas Soome 	if (ux1 >= ux2 || uy1 >= uy2) {
5130bead3caSToomas Soome 		if (trace)
5140bead3caSToomas Soome 			printf("Image dimensions reversed.\n");
5150bead3caSToomas Soome 		return (1);
5160bead3caSToomas Soome 	}
5170bead3caSToomas Soome 
5180bead3caSToomas Soome 	if (fwidth < 2 || fheight < 2) {
5190bead3caSToomas Soome 		if (trace)
5200bead3caSToomas Soome 			printf("Target area too small\n");
5210bead3caSToomas Soome 		return (1);
5220bead3caSToomas Soome 	}
5230bead3caSToomas Soome 
5240bead3caSToomas Soome 	if (trace)
5250bead3caSToomas Soome 		printf("Image %ux%u -> %ux%u @%ux%u\n",
5260bead3caSToomas Soome 		    png->width, png->height, fwidth, fheight, ux1, uy1);
5270bead3caSToomas Soome 
5280bead3caSToomas Soome 	if ((flags & FL_PUTIMAGE_BORDER))
5290bead3caSToomas Soome 		gfx_fb_drawrect(ux1, uy1, ux2, uy2, 0);
5300bead3caSToomas Soome 
5310bead3caSToomas Soome 	data = malloc(fwidth * fheight * fb.fb_bpp);
5320bead3caSToomas Soome 	if (data == NULL) {
5330bead3caSToomas Soome 		if (trace)
5340bead3caSToomas Soome 			printf("Out of memory.\n");
5350bead3caSToomas Soome 		return (1);
5360bead3caSToomas Soome 	}
5370bead3caSToomas Soome 
5380bead3caSToomas Soome 	/*
5390bead3caSToomas Soome 	 * Build image for our framebuffer.
5400bead3caSToomas Soome 	 */
5410bead3caSToomas Soome 
5420bead3caSToomas Soome 	/* Helper to calculate the pixel index from the source png */
5430bead3caSToomas Soome #define	GETPIXEL(xx, yy) (((yy) * png->width + (xx)) * png->bpp)
5440bead3caSToomas Soome 
5450bead3caSToomas Soome 	/*
5460bead3caSToomas Soome 	 * For each of the x and y directions, calculate the number of pixels
5470bead3caSToomas Soome 	 * in the source image that correspond to a single pixel in the target.
5480bead3caSToomas Soome 	 * Use fixed-point arithmetic with 16-bits for each of the integer and
5490bead3caSToomas Soome 	 * fractional parts.
5500bead3caSToomas Soome 	 */
5510bead3caSToomas Soome 	const uint32_t wcstep = ((png->width - 1) << 16) / (fwidth - 1);
5520bead3caSToomas Soome 	const uint32_t hcstep = ((png->height - 1) << 16) / (fheight - 1);
5530bead3caSToomas Soome 
5540bead3caSToomas Soome 	uint32_t hc = 0;
5550bead3caSToomas Soome 	for (y = 0; y < fheight; y++) {
5560bead3caSToomas Soome 		uint32_t hc2 = (hc >> 9) & 0x7f;
5570bead3caSToomas Soome 		uint32_t hc1 = 0x80 - hc2;
5580bead3caSToomas Soome 
5590bead3caSToomas Soome 		uint32_t offset_y = hc >> 16;
5600bead3caSToomas Soome 		uint32_t offset_y1 = offset_y + 1;
5610bead3caSToomas Soome 
5620bead3caSToomas Soome 		uint32_t wc = 0;
5630bead3caSToomas Soome 		for (x = 0; x < fwidth; x++) {
5640bead3caSToomas Soome 			uint32_t wc2 = (wc >> 9) & 0x7f;
5650bead3caSToomas Soome 			uint32_t wc1 = 0x80 - wc2;
5660bead3caSToomas Soome 
5670bead3caSToomas Soome 			uint32_t offset_x = wc >> 16;
5680bead3caSToomas Soome 			uint32_t offset_x1 = offset_x + 1;
5690bead3caSToomas Soome 
5700bead3caSToomas Soome 			/* Target pixel index */
5710bead3caSToomas Soome 			j = (y * fwidth + x) * fb.fb_bpp;
5720bead3caSToomas Soome 
5730bead3caSToomas Soome 			if (!scale) {
5740bead3caSToomas Soome 				i = GETPIXEL(x, y);
5750bead3caSToomas Soome 				r = png->image[i];
5760bead3caSToomas Soome 				g = png->image[i + 1];
5770bead3caSToomas Soome 				b = png->image[i + 2];
5780bead3caSToomas Soome 				a = png->image[i + 3];
5790bead3caSToomas Soome 			} else {
5800bead3caSToomas Soome 				uint8_t pixel[4];
5810bead3caSToomas Soome 
5820bead3caSToomas Soome 				uint32_t p00 = GETPIXEL(offset_x, offset_y);
5830bead3caSToomas Soome 				uint32_t p01 = GETPIXEL(offset_x, offset_y1);
5840bead3caSToomas Soome 				uint32_t p10 = GETPIXEL(offset_x1, offset_y);
5850bead3caSToomas Soome 				uint32_t p11 = GETPIXEL(offset_x1, offset_y1);
5860bead3caSToomas Soome 
5870bead3caSToomas Soome 				/*
5880bead3caSToomas Soome 				 * Given a 2x2 array of pixels in the source
5890bead3caSToomas Soome 				 * image, combine them to produce a single
5900bead3caSToomas Soome 				 * value for the pixel in the target image.
5910bead3caSToomas Soome 				 * Each column of pixels is combined using
5920bead3caSToomas Soome 				 * a weighted average where the top and bottom
5930bead3caSToomas Soome 				 * pixels contribute hc1 and hc2 respectively.
5940bead3caSToomas Soome 				 * The calculation for bottom pixel pB and
5950bead3caSToomas Soome 				 * top pixel pT is:
5960bead3caSToomas Soome 				 *   (pT * hc1 + pB * hc2) / (hc1 + hc2)
5970bead3caSToomas Soome 				 * Once the values are determined for the two
5980bead3caSToomas Soome 				 * columns of pixels, then the columns are
5990bead3caSToomas Soome 				 * averaged together in the same way but using
6000bead3caSToomas Soome 				 * wc1 and wc2 for the weightings.
6010bead3caSToomas Soome 				 *
6020bead3caSToomas Soome 				 * Since hc1 and hc2 are chosen so that
6030bead3caSToomas Soome 				 * hc1 + hc2 == 128 (and same for wc1 + wc2),
6040bead3caSToomas Soome 				 * the >> 14 below is a quick way to divide by
6050bead3caSToomas Soome 				 * (hc1 + hc2) * (wc1 + wc2)
6060bead3caSToomas Soome 				 */
6070bead3caSToomas Soome 				for (i = 0; i < 4; i++)
6080bead3caSToomas Soome 					pixel[i] = (
6090bead3caSToomas Soome 					    (png->image[p00 + i] * hc1 +
6100bead3caSToomas Soome 					    png->image[p01 + i] * hc2) * wc1 +
6110bead3caSToomas Soome 					    (png->image[p10 + i] * hc1 +
6120bead3caSToomas Soome 					    png->image[p11 + i] * hc2) * wc2)
6130bead3caSToomas Soome 					    >> 14;
6140bead3caSToomas Soome 
6150bead3caSToomas Soome 				r = pixel[0];
6160bead3caSToomas Soome 				g = pixel[1];
6170bead3caSToomas Soome 				b = pixel[2];
6180bead3caSToomas Soome 				a = pixel[3];
6190bead3caSToomas Soome 			}
6200bead3caSToomas Soome 
6210bead3caSToomas Soome 			color =
6220bead3caSToomas Soome 			    r >> (8 - fb.red_mask_size)
6230bead3caSToomas Soome 			    << fb.red_field_position |
6240bead3caSToomas Soome 			    g >> (8 - fb.green_mask_size)
6250bead3caSToomas Soome 			    << fb.green_field_position |
6260bead3caSToomas Soome 			    b >> (8 - fb.blue_mask_size)
6270bead3caSToomas Soome 			    << fb.blue_field_position;
6280bead3caSToomas Soome 
6290bead3caSToomas Soome 			switch (fb.fb_depth) {
6300bead3caSToomas Soome 			case 8: {
6310bead3caSToomas Soome 				uint32_t best, dist, k;
6320bead3caSToomas Soome 				int diff;
6330bead3caSToomas Soome 
6340bead3caSToomas Soome 				color = 0;
6350bead3caSToomas Soome 				best = 256 * 256 * 256;
6360bead3caSToomas Soome 				for (k = 0; k < 16; k++) {
6370bead3caSToomas Soome 					diff = r - cmap4_to_24.red[k];
6380bead3caSToomas Soome 					dist = diff * diff;
6390bead3caSToomas Soome 					diff = g - cmap4_to_24.green[k];
6400bead3caSToomas Soome 					dist += diff * diff;
6410bead3caSToomas Soome 					diff = b - cmap4_to_24.blue[k];
6420bead3caSToomas Soome 					dist += diff * diff;
6430bead3caSToomas Soome 
6440bead3caSToomas Soome 					if (dist < best) {
6450bead3caSToomas Soome 						color = k;
6460bead3caSToomas Soome 						best = dist;
6470bead3caSToomas Soome 						if (dist == 0)
6480bead3caSToomas Soome 							break;
6490bead3caSToomas Soome 					}
6500bead3caSToomas Soome 				}
6510bead3caSToomas Soome 				data[j] = solaris_color_to_pc_color[color];
6520bead3caSToomas Soome 				break;
6530bead3caSToomas Soome 			}
6540bead3caSToomas Soome 			case 15:
6550bead3caSToomas Soome 			case 16:
6560bead3caSToomas Soome 				*(uint16_t *)(data+j) = color;
6570bead3caSToomas Soome 				break;
6580bead3caSToomas Soome 			case 24:
6590bead3caSToomas Soome 				p = (uint8_t *)&color;
6600bead3caSToomas Soome 				data[j] = p[0];
6610bead3caSToomas Soome 				data[j+1] = p[1];
6620bead3caSToomas Soome 				data[j+2] = p[2];
6630bead3caSToomas Soome 				break;
6640bead3caSToomas Soome 			case 32:
6650bead3caSToomas Soome 				color |= a << 24;
6660bead3caSToomas Soome 				*(uint32_t *)(data+j) = color;
6670bead3caSToomas Soome 				break;
6680bead3caSToomas Soome 			}
6690bead3caSToomas Soome 			wc += wcstep;
6700bead3caSToomas Soome 		}
6710bead3caSToomas Soome 		hc += hcstep;
6720bead3caSToomas Soome 	}
6730bead3caSToomas Soome 
6740bead3caSToomas Soome 	gfx_fb_cons_display(uy1, ux1, fwidth, fheight, data);
6750bead3caSToomas Soome 	free(data);
6760bead3caSToomas Soome 	return (0);
6770bead3caSToomas Soome }
6780bead3caSToomas Soome 
6790bead3caSToomas Soome /*
6800bead3caSToomas Soome  * Implements alpha blending for RGBA data, could use pixels for arguments,
6810bead3caSToomas Soome  * but byte stream seems more generic.
6820bead3caSToomas Soome  * The generic alpha blending is:
6830bead3caSToomas Soome  * blend = alpha * fg + (1.0 - alpha) * bg.
6840bead3caSToomas Soome  * Since our alpha is not from range [0..1], we scale appropriately.
6850bead3caSToomas Soome  */
6860bead3caSToomas Soome static uint8_t
6870bead3caSToomas Soome alpha_blend(uint8_t fg, uint8_t bg, uint8_t alpha)
6880bead3caSToomas Soome {
6890bead3caSToomas Soome 	uint16_t blend, h, l;
6900bead3caSToomas Soome 
6910bead3caSToomas Soome 	/* trivial corner cases */
6920bead3caSToomas Soome 	if (alpha == 0)
6930bead3caSToomas Soome 		return (bg);
6940bead3caSToomas Soome 	if (alpha == 0xFF)
6950bead3caSToomas Soome 		return (fg);
6960bead3caSToomas Soome 	blend = (alpha * fg + (0xFF - alpha) * bg);
6970bead3caSToomas Soome 	/* Division by 0xFF */
6980bead3caSToomas Soome 	h = blend >> 8;
6990bead3caSToomas Soome 	l = blend & 0xFF;
7000bead3caSToomas Soome 	if (h + l >= 0xFF)
7010bead3caSToomas Soome 		h++;
7020bead3caSToomas Soome 	return (h);
7030bead3caSToomas Soome }
7040bead3caSToomas Soome 
7050bead3caSToomas Soome /* Copy memory to framebuffer or to memory. */
7060bead3caSToomas Soome static void
7070bead3caSToomas Soome bitmap_cpy(uint8_t *dst, uint8_t *src, uint32_t len, int bpp)
7080bead3caSToomas Soome {
7090bead3caSToomas Soome 	uint32_t i;
7100bead3caSToomas Soome 	uint8_t a;
7110bead3caSToomas Soome 
7120bead3caSToomas Soome 	switch (bpp) {
7130bead3caSToomas Soome 	case 4:
7140bead3caSToomas Soome 		for (i = 0; i < len; i += bpp) {
7150bead3caSToomas Soome 			a = src[i+3];
7160bead3caSToomas Soome 			dst[i] = alpha_blend(src[i], dst[i], a);
7170bead3caSToomas Soome 			dst[i+1] = alpha_blend(src[i+1], dst[i+1], a);
7180bead3caSToomas Soome 			dst[i+2] = alpha_blend(src[i+2], dst[i+2], a);
7190bead3caSToomas Soome 			dst[i+3] = a;
7200bead3caSToomas Soome 		}
7210bead3caSToomas Soome 		break;
7220bead3caSToomas Soome 	default:
7230bead3caSToomas Soome 		(void) memcpy(dst, src, len);
7240bead3caSToomas Soome 		break;
7250bead3caSToomas Soome 	}
7260bead3caSToomas Soome }
7270bead3caSToomas Soome 
7280bead3caSToomas Soome /*
7290bead3caSToomas Soome  * gfx_fb_cons_display implements direct draw on frame buffer memory.
7300bead3caSToomas Soome  * It is needed till we have way to send bitmaps to tem, tem already has
7310bead3caSToomas Soome  * function to send data down to framebuffer.
7320bead3caSToomas Soome  */
7330bead3caSToomas Soome static void
7340bead3caSToomas Soome gfx_fb_cons_display(uint32_t row, uint32_t col,
7350bead3caSToomas Soome     uint32_t width, uint32_t height, uint8_t *data)
7360bead3caSToomas Soome {
7370bead3caSToomas Soome 	uint32_t size;		/* write size per scanline */
7380bead3caSToomas Soome 	uint8_t *fbp;		/* fb + calculated offset */
7390bead3caSToomas Soome 	int i;
7400bead3caSToomas Soome 
7410bead3caSToomas Soome 	/* make sure we will not write past FB */
7420bead3caSToomas Soome 	if (col >= fb.fb_width || row >= fb.fb_height ||
7430bead3caSToomas Soome 	    col + width > fb.fb_width || row + height > fb.fb_height)
7440bead3caSToomas Soome 		return;
7450bead3caSToomas Soome 
7460bead3caSToomas Soome 	size = width * fb.fb_bpp;
7470bead3caSToomas Soome 	fbp = fb.fb_addr + col * fb.fb_bpp + row * fb.fb_pitch;
7480bead3caSToomas Soome 
7490bead3caSToomas Soome 	/* write all scanlines in rectangle */
7500bead3caSToomas Soome 	for (i = 0; i < height; i++) {
7510bead3caSToomas Soome 		uint8_t *dest = fbp + i * fb.fb_pitch;
7520bead3caSToomas Soome 		uint8_t *src = data + i * size;
7530bead3caSToomas Soome 		bitmap_cpy(dest, src, size, fb.fb_bpp);
7540bead3caSToomas Soome 	}
7550bead3caSToomas Soome }
756