view usr/src/common/font/font.c @ 19240:5cc8d6ae0443

11973 tem: we only can translate colors 0-7 Reviewed by: Robert Mustacchi <rm@fingolfin.org> Reviewed by: Garrett D'Amore <garrett@damore.org> Approved by: Dan McDonald <danmcd@joyent.com>
author Toomas Soome <tsoome@me.com>
date Thu, 14 Nov 2019 14:35:34 +0200
parents 0e60fb19154f
children ccfd88678127
line wrap: on
line source

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * Copyright 2017 Toomas Soome <tsoome@me.com>
 */

/*
 * Generic font related data and functions shared by early boot console
 * in dboot, kernel startup and full kernel.
 */
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/tem_impl.h>
#include <sys/rgb.h>
#include <sys/font.h>
#include <sys/sysmacros.h>

/*
 * To simplify my life, I am "temporarily" collecting the commonly used
 * color bits here. The bits shared between loader, dboot, early boot, tem.
 * This data would need some sort of API, but I am in no condition to figure
 * something out right now.
 */

/* ANSI color to sun color translation. */
/* BEGIN CSTYLED */
/*                                         Bk  Rd  Gr  Br  Bl  Mg  Cy  Wh */
const uint8_t dim_xlate[XLATE_NCOLORS] = {  1,  5,  3,  7,  2,  6,  4,  8 };
const uint8_t brt_xlate[XLATE_NCOLORS] = {  9, 13, 11, 15, 10, 14, 12,  0 };

const uint8_t solaris_color_to_pc_color[16] = {
	pc_brt_white,		/*  0 - brt_white	*/
	pc_black,		/*  1 - black		*/
	pc_blue,		/*  2 - blue		*/
	pc_green,		/*  3 - green		*/
	pc_cyan,		/*  4 - cyan		*/
	pc_red,			/*  5 - red		*/
	pc_magenta,		/*  6 - magenta		*/
	pc_brown,		/*  7 - brown		*/
	pc_white,		/*  8 - white		*/
	pc_grey,		/*  9 - gery		*/
	pc_brt_blue,		/* 10 - brt_blue	*/
	pc_brt_green,		/* 11 - brt_green	*/
	pc_brt_cyan,		/* 12 - brt_cyan	*/
	pc_brt_red,		/* 13 - brt_red		*/
	pc_brt_magenta,		/* 14 - brt_magenta	*/
	pc_yellow		/* 15 - yellow		*/
};

/* 4-bit to 24-bit color translation. */
const text_cmap_t cmap4_to_24 = {
/* 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 */

static uint32_t
rgb_to_color(const rgb_t *rgb, uint8_t r, uint8_t g, uint8_t b)
{
	uint32_t color;
	int pos, size;

	pos = rgb->red.pos;
	size = rgb->red.size;
	color = ((r >> (8 - size)) & ((1 << size) - 1)) << pos;

	pos = rgb->green.pos;
	size = rgb->green.size;
	color |= ((g >> (8 - size)) & ((1 << size) - 1)) << pos;

	pos = rgb->blue.pos;
	size = rgb->blue.size;
	color |= ((b >> (8 - size)) & ((1 << size) - 1)) << pos;

	return (color);
}

uint32_t
rgb_color_map(const rgb_t *rgb, uint8_t index)
{
	uint32_t color, code, gray, level;

	if (index < 16) {
		color = rgb_to_color(rgb, cmap4_to_24.red[index],
		    cmap4_to_24.green[index], cmap4_to_24.blue[index]);
		return (color);
	}

	/* 6x6x6 color cube */
	if (index > 15 && index < 232) {
		uint32_t red, green, blue;

		for (red = 0; red < 6; red++) {
			for (green = 0; green < 6; green++) {
				for (blue = 0; blue < 6; blue++) {
					code = 16 + (red * 36) +
					    (green * 6) + blue;
					if (code != index)
						continue;
					red = red ? (red * 40 + 55) : 0;
					green = green ? (green * 40 + 55) : 0;
					blue = blue ? (blue * 40 + 55) : 0;
					color = rgb_to_color(rgb, red, green,
					    blue);
					return (color);
				}
			}
		}
	}

	/* colors 232-255 are a grayscale ramp */
	for (gray = 0; gray < 24; gray++) {
		level = (gray * 10) + 8;
		code = 232 + gray;
		if (code == index)
			break;
	}
	return (rgb_to_color(rgb, level, level, level));
}
/*
 * Fonts are statically linked with this module. At some point an
 * RFE might be desireable to allow dynamic font loading.  The
 * original intention to facilitate dynamic fonts can be seen
 * by examining the data structures and set_font().  As much of
 * the original code is retained but modified to be suited for
 * traversing a list of static fonts.
 */

/*
 * Must be sorted by font size in descending order
 */
font_list_t fonts = STAILQ_HEAD_INITIALIZER(fonts);

/*
 * Reset font flags to FONT_AUTO.
 */
void
reset_font_flags(void)
{
	struct fontlist *fl;

	STAILQ_FOREACH(fl, &fonts, font_next) {
		fl->font_flags = FONT_AUTO;
	}
}

bitmap_data_t *
set_font(short *rows, short *cols, short h, short w)
{
	bitmap_data_t *font = NULL;
	struct fontlist	*fl;
	unsigned height = h;
	unsigned width = w;

	/*
	 * First check for manually loaded font.
	 */
	STAILQ_FOREACH(fl, &fonts, font_next) {
		if (fl->font_flags == FONT_MANUAL ||
		    fl->font_flags == FONT_BOOT) {
			font = fl->font_data;
			if (font->font == NULL && fl->font_load != NULL &&
			    fl->font_name != NULL) {
				font = fl->font_load(fl->font_name);
			}
			if (font == NULL || font->font == NULL)
				font = NULL;
			break;
		}
	}

	if (font != NULL) {
		*rows = (height - BORDER_PIXELS) / font->height;
		*cols = (width - BORDER_PIXELS) / font->width;
		return (font);
	}

	/*
	 * Find best font for these dimensions, or use default
	 *
	 * A 1 pixel border is the absolute minimum we could have
	 * as a border around the text window (BORDER_PIXELS = 2),
	 * however a slightly larger border not only looks better
	 * but for the fonts currently statically built into the
	 * emulator causes much better font selection for the
	 * normal range of screen resolutions.
	 */
	STAILQ_FOREACH(fl, &fonts, font_next) {
		font = fl->font_data;
		if ((((*rows * font->height) + BORDER_PIXELS) <= height) &&
		    (((*cols * font->width) + BORDER_PIXELS) <= width)) {
			if (font->font == NULL) {
				if (fl->font_load != NULL &&
				    fl->font_name != NULL) {
					font = fl->font_load(fl->font_name);
				}
				if (font == NULL)
					continue;
			}
			*rows = (height - BORDER_PIXELS) / font->height;
			*cols = (width - BORDER_PIXELS) / font->width;
			break;
		}
		font = NULL;
	}

	if (font == NULL) {
		/*
		 * We have fonts sorted smallest last, try it before
		 * falling back to builtin.
		 */
		fl = STAILQ_LAST(&fonts, fontlist, font_next);
		if (fl != NULL && fl->font_load != NULL &&
		    fl->font_name != NULL) {
			font = fl->font_load(fl->font_name);
		}
		if (font == NULL)
			font = &DEFAULT_FONT_DATA;

		*rows = (height - BORDER_PIXELS) / font->height;
		*cols = (width - BORDER_PIXELS) / font->width;
	}

	return (font);
}

/* Binary search for the glyph. Return 0 if not found. */
static uint16_t
font_bisearch(const struct font_map *map, uint32_t len, uint32_t src)
{
	unsigned min, mid, max;

	min = 0;
	max = len - 1;

	/* Empty font map. */
	if (len == 0)
		return (0);
	/* Character below minimal entry. */
	if (src < map[0].font_src)
		return (0);
	/* Optimization: ASCII characters occur very often. */
	if (src <= map[0].font_src + map[0].font_len)
		return (src - map[0].font_src + map[0].font_dst);
	/* Character above maximum entry. */
	if (src > map[max].font_src + map[max].font_len)
		return (0);

	/* Binary search. */
	while (max >= min) {
		mid = (min + max) / 2;
		if (src < map[mid].font_src)
			max = mid - 1;
		else if (src > map[mid].font_src + map[mid].font_len)
			min = mid + 1;
		else
			return (src - map[mid].font_src + map[mid].font_dst);
	}

	return (0);
}

/*
 * Return glyph bitmap. If glyph is not found, we will return bitmap
 * for the first (offset 0) glyph.
 */
const uint8_t *
font_lookup(const struct font *vf, uint32_t c)
{
	uint32_t src;
	uint16_t dst;
	size_t stride;

	src = TEM_CHAR(c);

	/* Substitute bold with normal if not found. */
	if (TEM_CHAR_ATTR(c) & TEM_ATTR_BOLD) {
		dst = font_bisearch(vf->vf_map[VFNT_MAP_BOLD],
		    vf->vf_map_count[VFNT_MAP_BOLD], src);
		if (dst != 0)
			goto found;
	}
	dst = font_bisearch(vf->vf_map[VFNT_MAP_NORMAL],
	    vf->vf_map_count[VFNT_MAP_NORMAL], src);

found:
	stride = howmany(vf->vf_width, 8) * vf->vf_height;
	return (&vf->vf_bytes[dst * stride]);
}

/*
 * bit_to_pix4 is for 4-bit frame buffers.  It will write one output byte
 * for each 2 bits of input bitmap.  It inverts the input bits before
 * doing the output translation, for reverse video.
 *
 * Assuming foreground is 0001 and background is 0000...
 * An input data byte of 0x53 will output the bit pattern
 * 00000001 00000001 00000000 00010001.
 */

void
font_bit_to_pix4(
    struct font *f,
    uint8_t *dest,
    uint32_t c,
    uint8_t fg_color,
    uint8_t bg_color)
{
	uint32_t row;
	int	byte;
	int	i;
	const uint8_t *cp, *ul;
	uint8_t	data;
	uint8_t	nibblett;
	int	bytes_wide;

	if (TEM_CHAR_ATTR(c) & TEM_ATTR_UNDERLINE)
		ul = font_lookup(f, 0x0332);	/* combining low line */
	else
		ul = NULL;

	cp = font_lookup(f, c);
	bytes_wide = (f->vf_width + 7) / 8;

	for (row = 0; row < f->vf_height; row++) {
		for (byte = 0; byte < bytes_wide; byte++) {
			if (ul == NULL)
				data = *cp++;
			else
				data = *cp++ | *ul++;
			for (i = 0; i < 4; i++) {
				nibblett = (data >> ((3-i) * 2)) & 0x3;
				switch (nibblett) {
				case 0x0:
					*dest++ = bg_color << 4 | bg_color;
					break;
				case 0x1:
					*dest++ = bg_color << 4 | fg_color;
					break;
				case 0x2:
					*dest++ = fg_color << 4 | bg_color;
					break;
				case 0x3:
					*dest++ = fg_color << 4 | fg_color;
					break;
				}
			}
		}
	}
}

/*
 * bit_to_pix8 is for 8-bit frame buffers.  It will write one output byte
 * for each bit of input bitmap.  It inverts the input bits before
 * doing the output translation, for reverse video.
 *
 * Assuming foreground is 00000001 and background is 00000000...
 * An input data byte of 0x53 will output the bit pattern
 * 0000000 000000001 00000000 00000001 00000000 00000000 00000001 00000001.
 */

void
font_bit_to_pix8(
    struct font *f,
    uint8_t *dest,
    uint32_t c,
    uint8_t fg_color,
    uint8_t bg_color)
{
	uint32_t row;
	int	byte;
	int	i;
	const uint8_t *cp, *ul;
	uint8_t	data;
	int	bytes_wide;
	uint8_t	mask;
	int	bitsleft, nbits;

	if (TEM_CHAR_ATTR(c) & TEM_ATTR_UNDERLINE)
		ul = font_lookup(f, 0x0332);	/* combining low line */
	else
		ul = NULL;

	cp = font_lookup(f, c);
	bytes_wide = (f->vf_width + 7) / 8;

	for (row = 0; row < f->vf_height; row++) {
		bitsleft = f->vf_width;
		for (byte = 0; byte < bytes_wide; byte++) {
			if (ul == NULL)
				data = *cp++;
			else
				data = *cp++ | *ul++;
			mask = 0x80;
			nbits = MIN(8, bitsleft);
			bitsleft -= nbits;
			for (i = 0; i < nbits; i++) {
				*dest++ = (data & mask ? fg_color: bg_color);
				mask = mask >> 1;
			}
		}
	}
}

/*
 * bit_to_pix16 is for 16-bit frame buffers.  It will write two output bytes
 * for each bit of input bitmap.  It inverts the input bits before
 * doing the output translation, for reverse video.
 *
 * Assuming foreground is 11111111 11111111
 * and background is 00000000 00000000
 * An input data byte of 0x53 will output the bit pattern
 *
 * 00000000 00000000
 * 11111111 11111111
 * 00000000 00000000
 * 11111111 11111111
 * 00000000 00000000
 * 00000000 00000000
 * 11111111 11111111
 * 11111111 11111111
 *
 */

void
font_bit_to_pix16(
    struct font *f,
    uint16_t *dest,
    uint32_t c,
    uint16_t fg_color16,
    uint16_t bg_color16)
{
	uint32_t row;
	int	byte;
	int	i;
	const uint8_t *cp, *ul;
	uint16_t data, d;
	int	bytes_wide;
	int	bitsleft, nbits;

	if (TEM_CHAR_ATTR(c) & TEM_ATTR_UNDERLINE)
		ul = font_lookup(f, 0x0332);	/* combining low line */
	else
		ul = NULL;

	cp = font_lookup(f, c);
	bytes_wide = (f->vf_width + 7) / 8;

	for (row = 0; row < f->vf_height; row++) {
		bitsleft = f->vf_width;
		for (byte = 0; byte < bytes_wide; byte++) {
			if (ul == NULL)
				data = *cp++;
			else
				data = *cp++ | *ul++;
			nbits = MIN(8, bitsleft);
			bitsleft -= nbits;
			for (i = 0; i < nbits; i++) {
				d = ((data << i) & 0x80 ?
				    fg_color16 : bg_color16);
				*dest++ = d;
			}
		}
	}
}

/*
 * bit_to_pix24 is for 24-bit frame buffers.  It will write three output bytes
 * for each bit of input bitmap.  It inverts the input bits before
 * doing the output translation, for reverse video.
 *
 * Assuming foreground is 11111111 11111111 11111111
 * and background is 00000000 00000000 00000000
 * An input data byte of 0x53 will output the bit pattern
 *
 * 00000000 00000000 00000000
 * 11111111 11111111 11111111
 * 00000000 00000000 00000000
 * 11111111 11111111 11111111
 * 00000000 00000000 00000000
 * 00000000 00000000 00000000
 * 11111111 11111111 11111111
 * 11111111 11111111 11111111
 *
 */

void
font_bit_to_pix24(
    struct font *f,
    uint8_t *dest,
    uint32_t c,
    uint32_t fg_color32,
    uint32_t bg_color32)
{
	uint32_t row;
	int	byte;
	int	i;
	const uint8_t *cp, *ul;
	uint32_t data, d;
	int	bytes_wide;
	int	bitsleft, nbits;

	if (TEM_CHAR_ATTR(c) & TEM_ATTR_UNDERLINE)
		ul = font_lookup(f, 0x0332);	/* combining low line */
	else
		ul = NULL;

	cp = font_lookup(f, c);
	bytes_wide = (f->vf_width + 7) / 8;

	for (row = 0; row < f->vf_height; row++) {
		bitsleft = f->vf_width;
		for (byte = 0; byte < bytes_wide; byte++) {
			if (ul == NULL)
				data = *cp++;
			else
				data = *cp++ | *ul++;

			nbits = MIN(8, bitsleft);
			bitsleft -= nbits;
			for (i = 0; i < nbits; i++) {
				d = ((data << i) & 0x80 ?
				    fg_color32 : bg_color32);
				*dest++ = d & 0xff;
				*dest++ = (d >> 8) & 0xff;
				*dest++ = (d >> 16) & 0xff;
			}
		}
	}
}

/*
 * bit_to_pix32 is for 32-bit frame buffers.  It will write four output bytes
 * for each bit of input bitmap.  It inverts the input bits before
 * doing the output translation, for reverse video.  Note that each
 * 24-bit RGB value is finally stored in a 32-bit unsigned int, with the
 * high-order byte set to zero.
 *
 * Assuming foreground is 00000000 11111111 11111111 11111111
 * and background is 00000000 00000000 00000000 00000000
 * An input data byte of 0x53 will output the bit pattern
 *
 * 00000000 00000000 00000000 00000000
 * 00000000 11111111 11111111 11111111
 * 00000000 00000000 00000000 00000000
 * 00000000 11111111 11111111 11111111
 * 00000000 00000000 00000000 00000000
 * 00000000 00000000 00000000 00000000
 * 00000000 11111111 11111111 11111111
 * 00000000 11111111 11111111 11111111
 *
 */

void
font_bit_to_pix32(
    struct font *f,
    uint32_t *dest,
    uint32_t c,
    uint32_t fg_color32,
    uint32_t bg_color32)
{
	uint32_t row;
	int	byte;
	int	i;
	const uint8_t *cp, *ul;
	uint32_t data;
	int	bytes_wide;
	int	bitsleft, nbits;

	if (TEM_CHAR_ATTR(c) & TEM_ATTR_UNDERLINE)
		ul = font_lookup(f, 0x0332);	/* combining low line */
	else
		ul = NULL;

	cp = font_lookup(f, c);
	bytes_wide = (f->vf_width + 7) / 8;

	for (row = 0; row < f->vf_height; row++) {
		bitsleft = f->vf_width;
		for (byte = 0; byte < bytes_wide; byte++) {
			if (ul == NULL)
				data = *cp++;
			else
				data = *cp++ | *ul++;
			nbits = MIN(8, bitsleft);
			bitsleft -= nbits;
			for (i = 0; i < nbits; i++) {
				*dest++ = ((data << i) & 0x80 ?
				    fg_color32 : bg_color32);
			}
		}
	}
}