summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/common/ficl/emu/gfx_fb.c755
-rw-r--r--usr/src/common/ficl/emu/gfx_fb.h68
-rw-r--r--usr/src/common/ficl/emu/loader_emu.c37
-rw-r--r--usr/src/common/ficl/loader.c5
-rw-r--r--usr/src/lib/libficl/Makefile.com4
5 files changed, 864 insertions, 5 deletions
diff --git a/usr/src/common/ficl/emu/gfx_fb.c b/usr/src/common/ficl/emu/gfx_fb.c
new file mode 100644
index 0000000000..caed97f85f
--- /dev/null
+++ b/usr/src/common/ficl/emu/gfx_fb.c
@@ -0,0 +1,755 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2016 Toomas Soome <tsoome@me.com>
+ */
+
+/*
+ * Graphics support for loader emulation.
+ * The interface in loader and here needs some more development.
+ * We can get colormap from gfx_private, but loader is currently
+ * relying on tem fg/bg colors for drawing, once the menu code
+ * will get some facelift, we would need to provide colors as menu component
+ * attributes and stop depending on tem.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/fbio.h>
+#include <string.h>
+#include "gfx_fb.h"
+
+struct framebuffer fb;
+
+#define max(x, y) ((x) >= (y) ? (x) : (y))
+
+static void gfx_fb_cons_display(uint32_t, uint32_t,
+ uint32_t, uint32_t, uint8_t *);
+
+/* This colormap should be replaced by colormap query from kernel */
+typedef struct {
+ uint8_t red[16];
+ uint8_t green[16];
+ uint8_t blue[16];
+} text_cmap_t;
+
+text_cmap_t cmap4_to_24 = {
+/* BEGIN CSTYLED */
+/* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ Wh+ Bk Bl Gr Cy Rd Mg Br Wh Bk+ Bl+ Gr+ Cy+ Rd+ Mg+ Yw */
+ .red = {0xff,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x40,0x00,0x00,0x00,0xff,0xff,0xff},
+ .green = {0xff,0x00,0x00,0x80,0x80,0x00,0x00,0x80,0x80,0x40,0x00,0xff,0xff,0x00,0x00,0xff},
+ .blue = {0xff,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x40,0xff,0x00,0xff,0x00,0xff,0x00}
+/* END CSTYLED */
+};
+
+const uint8_t solaris_color_to_pc_color[16] = {
+ 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
+};
+
+void
+gfx_framework_init(void)
+{
+ struct fbgattr attr;
+ struct gfxfb_info *gfxfb_info;
+ char buf[10];
+
+ fb.fd = open("/dev/fb", O_RDWR);
+ if (fb.fd < 0)
+ return;
+
+ /* make sure we have GFX framebuffer */
+ if (ioctl(fb.fd, VIS_GETIDENTIFIER, &fb.ident) < 0 ||
+ strcmp(fb.ident.name, "illumos_fb") != 0) {
+ close(fb.fd);
+ fb.fd = -1;
+ return;
+ }
+
+ if (ioctl(fb.fd, FBIOGATTR, &attr) < 0) {
+ close(fb.fd);
+ fb.fd = -1;
+ return;
+ }
+ gfxfb_info = (struct gfxfb_info *)attr.sattr.dev_specific;
+
+ fb.fb_height = attr.fbtype.fb_height;
+ fb.fb_width = attr.fbtype.fb_width;
+ fb.fb_depth = attr.fbtype.fb_depth;
+ fb.fb_size = attr.fbtype.fb_size;
+ fb.fb_bpp = attr.fbtype.fb_depth >> 3;
+ if (attr.fbtype.fb_depth == 15)
+ fb.fb_bpp = 2;
+ fb.fb_pitch = gfxfb_info->pitch;
+ fb.terminal_origin_x = gfxfb_info->terminal_origin_x;
+ fb.terminal_origin_y = gfxfb_info->terminal_origin_y;
+ fb.font_width = gfxfb_info->font_width;
+ fb.font_height = gfxfb_info->font_height;
+
+ fb.red_mask_size = gfxfb_info->red_mask_size;
+ fb.red_field_position = gfxfb_info->red_field_position;
+ fb.green_mask_size = gfxfb_info->green_mask_size;
+ fb.green_field_position = gfxfb_info->green_field_position;
+ fb.blue_mask_size = gfxfb_info->blue_mask_size;
+ fb.blue_field_position = gfxfb_info->blue_field_position;
+
+ fb.fb_addr = (uint8_t *)mmap(0, fb.fb_size, (PROT_READ | PROT_WRITE),
+ MAP_SHARED, fb.fd, 0);
+
+ if (fb.fb_addr == NULL) {
+ close(fb.fd);
+ fb.fd = -1;
+ return;
+ }
+ snprintf(buf, sizeof (buf), "%d", fb.fb_height);
+ setenv("screen-height", buf, 1);
+ snprintf(buf, sizeof (buf), "%d", fb.fb_width);
+ setenv("screen-width", buf, 1);
+}
+
+void
+gfx_framework_fini(void)
+{
+ if (fb.fd < 0)
+ return;
+
+ (void) munmap((caddr_t)fb.fb_addr, fb.fb_size);
+ close(fb.fd);
+ fb.fd = -1;
+}
+
+static int
+isqrt(int num)
+{
+ int res = 0;
+ int bit = 1 << 30;
+
+ /* "bit" starts at the highest power of four <= the argument. */
+ while (bit > num)
+ bit >>= 2;
+
+ while (bit != 0) {
+ if (num >= res + bit) {
+ num -= res + bit;
+ res = (res >> 1) + bit;
+ } else {
+ res >>= 1;
+ }
+ bit >>= 2;
+ }
+ return (res);
+}
+
+void
+gfx_fb_setpixel(uint32_t x, uint32_t y)
+{
+ uint32_t c, offset;
+
+ if (fb.fd < 0)
+ return;
+ c = 0; /* black */
+
+ if (x >= fb.fb_width || y >= fb.fb_height)
+ return;
+
+ offset = y * fb.fb_pitch + x * fb.fb_bpp;
+ switch (fb.fb_depth) {
+ case 8:
+ fb.fb_addr[offset] = c & 0xff;
+ break;
+ case 15:
+ case 16:
+ *(uint16_t *)(fb.fb_addr + offset) = c & 0xffff;
+ break;
+ case 24:
+ fb.fb_addr[offset] = (c >> 16) & 0xff;
+ fb.fb_addr[offset + 1] = (c >> 8) & 0xff;
+ fb.fb_addr[offset + 2] = c & 0xff;
+ break;
+ case 32:
+ *(uint32_t *)(fb.fb_addr + offset) = c;
+ break;
+ }
+}
+
+void
+gfx_fb_drawrect(uint32_t x1, uint32_t y1, uint32_t x2, uint32_t y2,
+ uint32_t fill)
+{
+ int x, y;
+
+ if (fb.fd < 0)
+ return;
+
+ for (y = y1; y <= y2; y++) {
+ if (fill || (y == y1) || (y == y2)) {
+ for (x = x1; x <= x2; x++)
+ gfx_fb_setpixel(x, y);
+ } else {
+ gfx_fb_setpixel(x1, y);
+ gfx_fb_setpixel(x2, y);
+ }
+ }
+}
+
+void
+gfx_term_drawrect(uint32_t row1, uint32_t col1, uint32_t row2, uint32_t col2)
+{
+ int x1, y1, x2, y2;
+ int xshift, yshift;
+ int width, i;
+
+ if (fb.fd < 0)
+ return;
+
+ width = fb.font_width / 4; /* line width */
+ xshift = (fb.font_width - width) / 2;
+ yshift = (fb.font_height - width) / 2;
+ /* Terminal coordinates start from (1,1) */
+ row1--;
+ col1--;
+ row2--;
+ col2--;
+
+ /*
+ * Draw horizontal lines width points thick, shifted from outer edge.
+ */
+ x1 = (row1 + 1) * fb.font_width + fb.terminal_origin_x;
+ y1 = col1 * fb.font_height + fb.terminal_origin_y + yshift;
+ x2 = row2 * fb.font_width + fb.terminal_origin_x;
+ gfx_fb_drawrect(x1, y1, x2, y1 + width, 1);
+ y2 = col2 * fb.font_height + fb.terminal_origin_y;
+ y2 += fb.font_height - yshift - width;
+ gfx_fb_drawrect(x1, y2, x2, y2 + width, 1);
+
+ /*
+ * Draw vertical lines width points thick, shifted from outer edge.
+ */
+ x1 = row1 * fb.font_width + fb.terminal_origin_x + xshift;
+ y1 = col1 * fb.font_height + fb.terminal_origin_y;
+ y1 += fb.font_height;
+ y2 = col2 * fb.font_height + fb.terminal_origin_y;
+ gfx_fb_drawrect(x1, y1, x1 + width, y2, 1);
+ x1 = row2 * fb.font_width + fb.terminal_origin_x;
+ x1 += fb.font_width - xshift - width;
+ gfx_fb_drawrect(x1, y1, x1 + width, y2, 1);
+
+ /* Draw upper left corner. */
+ x1 = row1 * fb.font_width + fb.terminal_origin_x + xshift;
+ y1 = col1 * fb.font_height + fb.terminal_origin_y;
+ y1 += fb.font_height;
+
+ x2 = row1 * fb.font_width + fb.terminal_origin_x;
+ x2 += fb.font_width;
+ y2 = col1 * fb.font_height + fb.terminal_origin_y + yshift;
+ for (i = 0; i <= width; i++)
+ gfx_fb_bezier(x1 + i, y1, x1 + i, y2 + i, x2, y2 + i, width-i);
+
+ /* Draw lower left corner. */
+ x1 = row1 * fb.font_width + fb.terminal_origin_x;
+ x1 += fb.font_width;
+ y1 = col2 * fb.font_height + fb.terminal_origin_y;
+ y1 += fb.font_height - yshift;
+ x2 = row1 * fb.font_width + fb.terminal_origin_x + xshift;
+ y2 = col2 * fb.font_height + fb.terminal_origin_y;
+ for (i = 0; i <= width; i++)
+ gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i);
+
+ /* Draw upper right corner. */
+ x1 = row2 * fb.font_width + fb.terminal_origin_x;
+ y1 = col1 * fb.font_height + fb.terminal_origin_y + yshift;
+ x2 = row2 * fb.font_width + fb.terminal_origin_x;
+ x2 += fb.font_width - xshift - width;
+ y2 = col1 * fb.font_height + fb.terminal_origin_y;
+ y2 += fb.font_height;
+ for (i = 0; i <= width; i++)
+ gfx_fb_bezier(x1, y1 + i, x2 + i, y1 + i, x2 + i, y2, width-i);
+
+ /* Draw lower right corner. */
+ x1 = row2 * fb.font_width + fb.terminal_origin_x;
+ y1 = col2 * fb.font_height + fb.terminal_origin_y;
+ y1 += fb.font_height - yshift;
+ x2 = row2 * fb.font_width + fb.terminal_origin_x;
+ x2 += fb.font_width - xshift - width;
+ y2 = col2 * fb.font_height + fb.terminal_origin_y;
+ for (i = 0; i <= width; i++)
+ gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i);
+}
+
+void
+gfx_fb_line(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t width)
+{
+ int dx, sx, dy, sy;
+ int err, e2, x2, y2, ed;
+
+ if (fb.fd < 0)
+ return;
+
+ sx = x0 < x1? 1 : -1;
+ sy = y0 < y1? 1 : -1;
+ dx = abs(x1 - x0);
+ dy = abs(y1 - y0);
+ err = dx - dy;
+ ed = dx + dy == 0 ? 1 : isqrt(dx * dx + dy * dy);
+
+ if (dx != 0 && dy != 0)
+ width = (width + 1) >> 1;
+
+ for (;;) {
+ gfx_fb_setpixel(x0, y0);
+ e2 = err;
+ x2 = x0;
+ if ((e2 << 1) >= -dx) { /* x step */
+ e2 += dy;
+ y2 = y0;
+ while (e2 < ed * width && (y1 != y2 || dx > dy)) {
+ y2 += sy;
+ gfx_fb_setpixel(x0, y2);
+ e2 += dx;
+ }
+ if (x0 == x1)
+ break;
+ e2 = err;
+ err -= dy;
+ x0 += sx;
+ }
+ if ((e2 << 1) <= dy) { /* y step */
+ e2 = dx-e2;
+ while (e2 < ed * width && (x1 != x2 || dx < dy)) {
+ x2 += sx;
+ gfx_fb_setpixel(x2, y0);
+ e2 += dy;
+ }
+ if (y0 == y1)
+ break;
+ err += dx;
+ y0 += sy;
+ }
+ }
+}
+
+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)
+{
+ int sx, sy, xx, yy, xy, width;
+ int dx, dy, err, curvature;
+ int i;
+
+ if (fb.fd < 0)
+ return;
+
+ width = wd;
+ sx = x2 - x1;
+ sy = y2 - y1;
+ xx = x0 - x1;
+ yy = y0 - y1;
+ curvature = xx*sy - yy*sx;
+
+ if (sx * sx + sy * sy > xx * xx + yy * yy) {
+ x2 = x0;
+ x0 = sx + x1;
+ y2 = y0;
+ y0 = sy + y1;
+ curvature = -curvature;
+ }
+ if (curvature != 0) {
+ xx += sx;
+ sx = x0 < x2? 1 : -1;
+ xx *= sx;
+ yy += sy;
+ sy = y0 < y2? 1 : -1;
+ yy *= sy;
+ xy = 2 * xx * yy;
+ xx *= xx;
+ yy *= yy;
+ if (curvature * sx * sy < 0) {
+ xx = -xx;
+ yy = -yy;
+ xy = -xy;
+ curvature = -curvature;
+ }
+ dx = 4 * sy * curvature * (x1 - x0) + xx - xy;
+ dy = 4 * sx * curvature * (y0 - y1) + yy - xy;
+ xx += xx;
+ yy += yy;
+ err = dx + dy + xy;
+ do {
+ for (i = 0; i <= width; i++)
+ gfx_fb_setpixel(x0 + i, y0);
+ if (x0 == x2 && y0 == y2)
+ return; /* last pixel -> curve finished */
+ y1 = 2 * err < dx;
+ if (2 * err > dy) {
+ x0 += sx;
+ dx -= xy;
+ dy += yy;
+ err += dy;
+ }
+ if (y1 != 0) {
+ y0 += sy;
+ dy -= xy;
+ dx += xx;
+ err += dx;
+ }
+ } while (dy < dx); /* gradient negates -> algorithm fails */
+ }
+ gfx_fb_line(x0, y0, x2, y2, width);
+}
+
+#define FL_PUTIMAGE_BORDER 0x1
+#define FL_PUTIMAGE_NOSCROLL 0x2
+#define FL_PUTIMAGE_DEBUG 0x80
+
+int
+gfx_fb_putimage(png_t *png, uint32_t ux1, uint32_t uy1, uint32_t ux2,
+ uint32_t uy2, uint32_t flags)
+{
+ uint32_t i, j, x, y, fheight, fwidth, color;
+ uint8_t r, g, b, a, *p, *data;
+ bool scale = false;
+ bool trace = false;
+
+ trace = (flags & FL_PUTIMAGE_DEBUG) != 0;
+
+ if (fb.fd < 0) {
+ if (trace)
+ printf("Framebuffer not active.\n");
+ return (1);
+ }
+
+ if (png->color_type != PNG_TRUECOLOR_ALPHA) {
+ if (trace)
+ printf("Not truecolor image.\n");
+ return (1);
+ }
+
+ if (ux1 > fb.fb_width || uy1 > fb.fb_height) {
+ if (trace)
+ printf("Top left coordinate off screen.\n");
+ return (1);
+ }
+
+ if (png->width > UINT16_MAX || png->height > UINT16_MAX) {
+ if (trace)
+ printf("Image too large.\n");
+ return (1);
+ }
+
+ if (png->width < 1 || png->height < 1) {
+ if (trace)
+ printf("Image too small.\n");
+ return (1);
+ }
+
+ /*
+ * If 0 was passed for either ux2 or uy2, then calculate the missing
+ * part of the bottom right coordinate.
+ */
+ scale = true;
+ if (ux2 == 0 && uy2 == 0) {
+ /* Both 0, use the native resolution of the image */
+ ux2 = ux1 + png->width;
+ uy2 = uy1 + png->height;
+ scale = false;
+ } else if (ux2 == 0) {
+ /* Set ux2 from uy2/uy1 to maintain aspect ratio */
+ ux2 = ux1 + (png->width * (uy2 - uy1)) / png->height;
+ } else if (uy2 == 0) {
+ /* Set uy2 from ux2/ux1 to maintain aspect ratio */
+ uy2 = uy1 + (png->height * (ux2 - ux1)) / png->width;
+ }
+
+ if (ux2 > fb.fb_width || uy2 > fb.fb_height) {
+ if (trace)
+ printf("Bottom right coordinate off screen.\n");
+ return (1);
+ }
+
+ fwidth = ux2 - ux1;
+ fheight = uy2 - uy1;
+
+ /*
+ * If the original image dimensions have been passed explicitly,
+ * disable scaling.
+ */
+ if (fwidth == png->width && fheight == png->height)
+ scale = false;
+
+ if (ux1 == 0) {
+ /*
+ * No top left X co-ordinate (real coordinates start at 1),
+ * place as far right as it will fit.
+ */
+ ux2 = fb.fb_width - fb.terminal_origin_x;
+ ux1 = ux2 - fwidth;
+ }
+
+ if (uy1 == 0) {
+ /*
+ * No top left Y co-ordinate (real coordinates start at 1),
+ * place as far down as it will fit.
+ */
+ uy2 = fb.fb_height - fb.terminal_origin_y;
+ uy1 = uy2 - fheight;
+ }
+
+ if (ux1 >= ux2 || uy1 >= uy2) {
+ if (trace)
+ printf("Image dimensions reversed.\n");
+ return (1);
+ }
+
+ if (fwidth < 2 || fheight < 2) {
+ if (trace)
+ printf("Target area too small\n");
+ return (1);
+ }
+
+ if (trace)
+ printf("Image %ux%u -> %ux%u @%ux%u\n",
+ png->width, png->height, fwidth, fheight, ux1, uy1);
+
+ if ((flags & FL_PUTIMAGE_BORDER))
+ gfx_fb_drawrect(ux1, uy1, ux2, uy2, 0);
+
+ data = malloc(fwidth * fheight * fb.fb_bpp);
+ if (data == NULL) {
+ if (trace)
+ printf("Out of memory.\n");
+ return (1);
+ }
+
+ /*
+ * Build image for our framebuffer.
+ */
+
+ /* Helper to calculate the pixel index from the source png */
+#define GETPIXEL(xx, yy) (((yy) * png->width + (xx)) * png->bpp)
+
+ /*
+ * For each of the x and y directions, calculate the number of pixels
+ * in the source image that correspond to a single pixel in the target.
+ * Use fixed-point arithmetic with 16-bits for each of the integer and
+ * fractional parts.
+ */
+ const uint32_t wcstep = ((png->width - 1) << 16) / (fwidth - 1);
+ const uint32_t hcstep = ((png->height - 1) << 16) / (fheight - 1);
+
+ uint32_t hc = 0;
+ for (y = 0; y < fheight; y++) {
+ uint32_t hc2 = (hc >> 9) & 0x7f;
+ uint32_t hc1 = 0x80 - hc2;
+
+ uint32_t offset_y = hc >> 16;
+ uint32_t offset_y1 = offset_y + 1;
+
+ uint32_t wc = 0;
+ for (x = 0; x < fwidth; x++) {
+ uint32_t wc2 = (wc >> 9) & 0x7f;
+ uint32_t wc1 = 0x80 - wc2;
+
+ uint32_t offset_x = wc >> 16;
+ uint32_t offset_x1 = offset_x + 1;
+
+ /* Target pixel index */
+ j = (y * fwidth + x) * fb.fb_bpp;
+
+ if (!scale) {
+ i = GETPIXEL(x, y);
+ r = png->image[i];
+ g = png->image[i + 1];
+ b = png->image[i + 2];
+ a = png->image[i + 3];
+ } else {
+ uint8_t pixel[4];
+
+ uint32_t p00 = GETPIXEL(offset_x, offset_y);
+ uint32_t p01 = GETPIXEL(offset_x, offset_y1);
+ uint32_t p10 = GETPIXEL(offset_x1, offset_y);
+ uint32_t p11 = GETPIXEL(offset_x1, offset_y1);
+
+ /*
+ * Given a 2x2 array of pixels in the source
+ * image, combine them to produce a single
+ * value for the pixel in the target image.
+ * Each column of pixels is combined using
+ * a weighted average where the top and bottom
+ * pixels contribute hc1 and hc2 respectively.
+ * The calculation for bottom pixel pB and
+ * top pixel pT is:
+ * (pT * hc1 + pB * hc2) / (hc1 + hc2)
+ * Once the values are determined for the two
+ * columns of pixels, then the columns are
+ * averaged together in the same way but using
+ * wc1 and wc2 for the weightings.
+ *
+ * Since hc1 and hc2 are chosen so that
+ * hc1 + hc2 == 128 (and same for wc1 + wc2),
+ * the >> 14 below is a quick way to divide by
+ * (hc1 + hc2) * (wc1 + wc2)
+ */
+ for (i = 0; i < 4; i++)
+ pixel[i] = (
+ (png->image[p00 + i] * hc1 +
+ png->image[p01 + i] * hc2) * wc1 +
+ (png->image[p10 + i] * hc1 +
+ png->image[p11 + i] * hc2) * wc2)
+ >> 14;
+
+ r = pixel[0];
+ g = pixel[1];
+ b = pixel[2];
+ a = pixel[3];
+ }
+
+ color =
+ r >> (8 - fb.red_mask_size)
+ << fb.red_field_position |
+ g >> (8 - fb.green_mask_size)
+ << fb.green_field_position |
+ b >> (8 - fb.blue_mask_size)
+ << fb.blue_field_position;
+
+ switch (fb.fb_depth) {
+ case 8: {
+ uint32_t best, dist, k;
+ int diff;
+
+ color = 0;
+ best = 256 * 256 * 256;
+ for (k = 0; k < 16; k++) {
+ diff = r - cmap4_to_24.red[k];
+ dist = diff * diff;
+ diff = g - cmap4_to_24.green[k];
+ dist += diff * diff;
+ diff = b - cmap4_to_24.blue[k];
+ dist += diff * diff;
+
+ if (dist < best) {
+ color = k;
+ best = dist;
+ if (dist == 0)
+ break;
+ }
+ }
+ data[j] = solaris_color_to_pc_color[color];
+ break;
+ }
+ case 15:
+ case 16:
+ *(uint16_t *)(data+j) = color;
+ break;
+ case 24:
+ p = (uint8_t *)&color;
+ data[j] = p[0];
+ data[j+1] = p[1];
+ data[j+2] = p[2];
+ break;
+ case 32:
+ color |= a << 24;
+ *(uint32_t *)(data+j) = color;
+ break;
+ }
+ wc += wcstep;
+ }
+ hc += hcstep;
+ }
+
+ gfx_fb_cons_display(uy1, ux1, fwidth, fheight, data);
+ free(data);
+ return (0);
+}
+
+/*
+ * Implements alpha blending for RGBA data, could use pixels for arguments,
+ * but byte stream seems more generic.
+ * The generic alpha blending is:
+ * blend = alpha * fg + (1.0 - alpha) * bg.
+ * Since our alpha is not from range [0..1], we scale appropriately.
+ */
+static uint8_t
+alpha_blend(uint8_t fg, uint8_t bg, uint8_t alpha)
+{
+ uint16_t blend, h, l;
+
+ /* trivial corner cases */
+ if (alpha == 0)
+ return (bg);
+ if (alpha == 0xFF)
+ return (fg);
+ blend = (alpha * fg + (0xFF - alpha) * bg);
+ /* Division by 0xFF */
+ h = blend >> 8;
+ l = blend & 0xFF;
+ if (h + l >= 0xFF)
+ h++;
+ return (h);
+}
+
+/* Copy memory to framebuffer or to memory. */
+static void
+bitmap_cpy(uint8_t *dst, uint8_t *src, uint32_t len, int bpp)
+{
+ uint32_t i;
+ uint8_t a;
+
+ switch (bpp) {
+ case 4:
+ for (i = 0; i < len; i += bpp) {
+ a = src[i+3];
+ dst[i] = alpha_blend(src[i], dst[i], a);
+ dst[i+1] = alpha_blend(src[i+1], dst[i+1], a);
+ dst[i+2] = alpha_blend(src[i+2], dst[i+2], a);
+ dst[i+3] = a;
+ }
+ break;
+ default:
+ (void) memcpy(dst, src, len);
+ break;
+ }
+}
+
+/*
+ * gfx_fb_cons_display implements direct draw on frame buffer memory.
+ * It is needed till we have way to send bitmaps to tem, tem already has
+ * function to send data down to framebuffer.
+ */
+static void
+gfx_fb_cons_display(uint32_t row, uint32_t col,
+ uint32_t width, uint32_t height, uint8_t *data)
+{
+ uint32_t size; /* write size per scanline */
+ uint8_t *fbp; /* fb + calculated offset */
+ int i;
+
+ /* make sure we will not write past FB */
+ if (col >= fb.fb_width || row >= fb.fb_height ||
+ col + width > fb.fb_width || row + height > fb.fb_height)
+ return;
+
+ size = width * fb.fb_bpp;
+ fbp = fb.fb_addr + col * fb.fb_bpp + row * fb.fb_pitch;
+
+ /* write all scanlines in rectangle */
+ for (i = 0; i < height; i++) {
+ uint8_t *dest = fbp + i * fb.fb_pitch;
+ uint8_t *src = data + i * size;
+ bitmap_cpy(dest, src, size, fb.fb_bpp);
+ }
+}
diff --git a/usr/src/common/ficl/emu/gfx_fb.h b/usr/src/common/ficl/emu/gfx_fb.h
new file mode 100644
index 0000000000..4dfb386f5b
--- /dev/null
+++ b/usr/src/common/ficl/emu/gfx_fb.h
@@ -0,0 +1,68 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2016 Toomas Some <tsoome@me.com>
+ */
+
+#ifndef _GFX_FB_H
+#define _GFX_FB_H
+
+/*
+ * Graphics support for loader emulation.
+ */
+#include <sys/visual_io.h>
+#include <pnglite.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct framebuffer {
+ struct vis_identifier ident;
+ int fd; /* frame buffer device descriptor */
+ uint8_t *fb_addr; /* mapped framebuffer */
+
+ int fb_height; /* in pixels */
+ int fb_width; /* in pixels */
+ int fb_depth; /* bits per pixel */
+ int fb_bpp; /* bytes per pixel */
+ int fb_size; /* total size in bytes */
+ int fb_pitch; /* bytes per scanline */
+ uint16_t terminal_origin_x;
+ uint16_t terminal_origin_y;
+ uint16_t font_width;
+ uint16_t font_height;
+ uint8_t red_mask_size;
+ uint8_t red_field_position;
+ uint8_t green_mask_size;
+ uint8_t green_field_position;
+ uint8_t blue_mask_size;
+ uint8_t blue_field_position;
+};
+
+extern struct framebuffer fb;
+
+void gfx_framework_init(void);
+void gfx_framework_fini(void);
+void gfx_fb_setpixel(uint32_t, uint32_t);
+void gfx_fb_drawrect(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t);
+void gfx_term_drawrect(uint32_t, uint32_t, uint32_t, uint32_t);
+void gfx_fb_line(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t);
+void gfx_fb_bezier(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t,
+ uint32_t);
+int gfx_fb_putimage(png_t *, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GFX_FB_H */
diff --git a/usr/src/common/ficl/emu/loader_emu.c b/usr/src/common/ficl/emu/loader_emu.c
index 84653dca2d..f64f15255e 100644
--- a/usr/src/common/ficl/emu/loader_emu.c
+++ b/usr/src/common/ficl/emu/loader_emu.c
@@ -40,6 +40,7 @@
#include <sys/systeminfo.h>
#include <sys/queue.h>
#include <sys/mnttab.h>
+#include "gfx_fb.h"
#include "ficl.h"
/* Commands and return values; nonzero return sets command_errmsg != NULL */
@@ -104,6 +105,7 @@ static int command_unload(int argc, char *argv[]);
static int command_load(int argc, char *argv[]);
static int command_reboot(int argc, char *argv[]);
static int command_sifting(int argc, char *argv[]);
+static int command_framebuffer(int argc, char *argv[]);
#define BF_PARSE 100
#define BF_DICTSIZE 30000
@@ -753,6 +755,8 @@ bf_init(const char *rc, ficlOutputFunction out)
COMMAND_SET(cmdp, "reboot", "reboot the system", command_reboot);
STAILQ_INSERT_TAIL(&commands, cmdp, next);
COMMAND_SET(cmdp, "sifting", "find words", command_sifting);
+ COMMAND_SET(cmdp, "framebuffer", "framebuffer mode management",
+ command_framebuffer);
STAILQ_INSERT_TAIL(&commands, cmdp, next);
fsi = malloc(sizeof (ficlSystemInformation));
@@ -841,6 +845,7 @@ bf_init(const char *rc, ficlOutputFunction out)
}
}
+ gfx_framework_init();
return (bf_vm);
}
@@ -848,6 +853,7 @@ void
bf_fini(void)
{
ficlSystemDestroy(bf_sys);
+ gfx_framework_fini();
}
/*
@@ -1993,3 +1999,34 @@ command_sifting(int argc, char *argv[])
ficlPrimitiveSiftingImpl(bf_vm, argv[1]);
return (CMD_OK);
}
+
+/* Only implement get and list. Ignore arguments on, off and set. */
+static int
+command_framebuffer(int argc, char *argv[])
+{
+ if (fb.fd < 0) {
+ printf("Framebuffer is not available.\n");
+ return (CMD_OK);
+ }
+
+ if (argc == 2 && strcmp(argv[1], "get") == 0) {
+ printf("\nSystem frame buffer: %s\n", fb.ident.name);
+ printf("%dx%dx%d, stride=%d\n", fb.fb_width, fb.fb_height,
+ fb.fb_depth, (fb.fb_pitch << 3) / fb.fb_depth);
+ return (CMD_OK);
+ }
+ if (argc == 2 && strcmp(argv[1], "list") == 0) {
+ printf("0: %dx%dx%d\n", fb.fb_width, fb.fb_height, fb.fb_depth);
+ return (CMD_OK);
+ }
+ if (argc == 3 && strcmp(argv[1], "set") == 0)
+ return (CMD_OK);
+ if (argc == 2 && strcmp(argv[1], "on") == 0)
+ return (CMD_OK);
+ if (argc == 2 && strcmp(argv[1], "off") == 0)
+ return (CMD_OK);
+
+ snprintf(command_errbuf, sizeof (command_errbuf),
+ "usage: %s get | list", argv[0]);
+ return (CMD_ERROR);
+}
diff --git a/usr/src/common/ficl/loader.c b/usr/src/common/ficl/loader.c
index 1ce8373615..fe2268297b 100644
--- a/usr/src/common/ficl/loader.c
+++ b/usr/src/common/ficl/loader.c
@@ -51,6 +51,7 @@
#include <uuid/uuid.h>
#endif
#include <string.h>
+#include <gfx_fb.h>
#include "ficl.h"
/*
@@ -69,7 +70,6 @@
* .# ( value -- )
*/
-#ifdef _STANDALONE
/* ( flags x1 y1 x2 y2 -- flag ) */
void
ficl_fb_putimage(ficlVm *pVM)
@@ -177,7 +177,6 @@ ficl_term_drawrect(ficlVm *pVM)
x1 = ficlStackPopUnsigned(ficlVmGetDataStack(pVM));
gfx_term_drawrect(x1, y1, x2, y2);
}
-#endif /* _STANDALONE */
void
ficlSetenv(ficlVm *pVM)
@@ -1024,7 +1023,6 @@ ficlSystemCompilePlatform(ficlSystem *pSys)
FICL_WORD_DEFAULT);
ficlDictionarySetPrimitive(dp, "uuid-to-string", ficlUuidToString,
FICL_WORD_DEFAULT);
-#ifdef _STANDALONE
ficlDictionarySetPrimitive(dp, "fb-setpixel", ficl_fb_setpixel,
FICL_WORD_DEFAULT);
ficlDictionarySetPrimitive(dp, "fb-line", ficl_fb_line,
@@ -1037,6 +1035,7 @@ ficlSystemCompilePlatform(ficlSystem *pSys)
FICL_WORD_DEFAULT);
ficlDictionarySetPrimitive(dp, "term-drawrect", ficl_term_drawrect,
FICL_WORD_DEFAULT);
+#ifdef _STANDALONE
/* Register words from linker set. */
SET_FOREACH(fnpp, Xficl_compile_set)
(*fnpp)(pSys);
diff --git a/usr/src/lib/libficl/Makefile.com b/usr/src/lib/libficl/Makefile.com
index 2a077c47ee..d0ac5a8791 100644
--- a/usr/src/lib/libficl/Makefile.com
+++ b/usr/src/lib/libficl/Makefile.com
@@ -22,7 +22,7 @@ VERS=.$(MAJOR).$(MINOR)
OBJECTS= dictionary.o system.o fileaccess.o float.o double.o prefix.o search.o \
softcore.o stack.o tools.o vm.o primitives.o unix.o utility.o \
hash.o callback.o word.o loader.o pager.o extras.o \
- loader_emu.o pnglite.o lz4.o
+ loader_emu.o gfx_fb.o pnglite.o lz4.o
include $(SRC)/lib/Makefile.lib
@@ -31,7 +31,7 @@ LIBS= $(DYNLIB) $(LINTLIB)
FICLDIR= $(SRC)/common/ficl
CSTD= $(CSTD_GNU99)
PNGLITE= $(SRC)/common/pnglite
-CPPFLAGS += -I.. -I$(FICLDIR) -D_LARGEFILE64_SOURCE=1
+CPPFLAGS += -I.. -I$(FICLDIR) -I$(FICLDIR)/emu -D_LARGEFILE64_SOURCE=1
CPPFLAGS += -I$(PNGLITE)
# As variable "count" is marked volatile, gcc 4.4.4 will complain about