root/usr/src/common/font/font.c
/*
 * 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 - grey            */
        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          */
};

const uint8_t pc_color_to_solaris_color[16] = {
        sun_black,              /*  0 - black           */
        sun_blue,               /*  1 - blue            */
        sun_green,              /*  2 - green           */
        sun_cyan,               /*  3 - cyan            */
        sun_red,                /*  4 - red             */
        sun_magenta,            /*  5 - magenta         */
        sun_brown,              /*  6 - brown           */
        sun_white,              /*  7 - white           */
        sun_grey,               /*  8 - grey            */
        sun_brt_blue,           /*  9 - brt_blue        */
        sun_brt_green,          /* 10 - brt_green       */
        sun_brt_cyan,           /* 11 - brt_cyan        */
        sun_brt_red,            /* 12 - brt_red         */
        sun_brt_magenta,        /* 13 - brt_magenta     */
        sun_yellow,             /* 14 - yellow          */
        sun_brt_white           /* 15 - brt_white       */
};

/* 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 */

/* RGB configuration from boot loader */
rgb_t rgb_info = {
        .red = { .size = 8, .pos = 16 },
        .green = { .size = 8, .pos = 8 },
        .blue = { .size = 8, .pos = 0 }
};

/*
 * Map r, g, b to RGB value.
 */
uint32_t
rgb_to_color(const rgb_t *rgb, uint32_t a, uint32_t r, uint32_t g, uint32_t b)
{
        uint32_t color;
        int pos, size;

        color = 0;
        if (a != 0) {
                if (rgb->red.pos != 0 &&
                    rgb->green.pos != 0 &&
                    rgb->blue.pos != 0) {
                        pos = 0;
                        size = MIN(rgb->red.pos,
                            MIN(rgb->green.pos, rgb->blue.pos));
                } else {
                        pos = 24;
                        size = (rgb->red.size + rgb->green.size +
                            rgb->blue.size) / 3;
                }
                color = ((a * ((1 << size) - 1)) / 0xff) << pos;
        }

        pos = rgb->red.pos;
        size = rgb->red.size;
        color |= ((r * ((1 << size) - 1)) / 0xff) << pos;

        pos = rgb->green.pos;
        size = rgb->green.size;
        color |= (((g * ((1 << size) - 1)) / 0xff) << pos);

        pos = rgb->blue.pos;
        size = rgb->blue.size;
        color |= (((b * ((1 << size) - 1)) / 0xff) << pos);

        return (color);
}

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

        if (index < 16) {
                color = rgb_to_color(rgb, alpha, 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, alpha,
                                            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, alpha, 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;
        }
}

__weak_symbol bitmap_data_t *
gfx_get_font(short rows __unused, short cols __unused, short height __unused,
    short width __unused)
{
        return (NULL);
}

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)
                font = gfx_get_font(*rows, *cols, h, w);

        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 ||
                            fl->font_flags == FONT_RELOAD) {
                                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,
    uint32_t fg_color,
    uint32_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,
    uint32_t fg_color,
    uint32_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,
    uint32_t fg_color16,
    uint32_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);
                        }
                }
        }
}