blob: 1b0dfca528aac6849d64f9199fc1c54c2d660050 [file] [log] [blame]
/*
* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include <stdint.h>
#include "font.h"
#include "glyphs.h"
#include "util.h"
#define UNICODE_REPLACEMENT_CHARACTER_CODE_POINT 0xFFFD
static int font_scaling = 0;
static int glyph_size = GLYPH_BYTES_PER_ROW * GLYPH_HEIGHT;
static uint8_t* prescaled_glyphs = NULL;
static int font_ref = 0;
static uint8_t get_bit(const uint8_t* buffer, int bit_offset)
{
return (buffer[bit_offset / 8] >> (7 - (bit_offset % 8))) & 0x1;
}
static void set_bit(uint8_t* buffer, int bit_offset)
{
buffer[bit_offset / 8] |= (0x1 << (7 - (bit_offset % 8)));
}
static uint8_t glyph_pixel(const uint8_t* glyph, int x, int y)
{
if (x < 0 || x >= GLYPH_WIDTH || y < 0 || y >= GLYPH_HEIGHT)
return 0;
return get_bit(&glyph[y * GLYPH_BYTES_PER_ROW], x);
}
static uint8_t scale_pixel(uint32_t neighbors, int sx, int sy, int scaling)
{
/* Bitmasks of neighbor pixels */
enum {
NW = (1 << 8),
N = (1 << 7),
NE = (1 << 6),
W = (1 << 5),
C = (1 << 4),
E = (1 << 3),
SW = (1 << 2),
S = (1 << 1),
SE = (1 << 0),
};
/*
* Scale a pixel by a factor of |scaling|, based on the colors of the
* center pixel and the eight neighbor pixels on a 3x3 grid:
*
* NW | N | NE
* ---+---+---
* W | C | E
* ---+---+---
* SW | S | SE
*
* If the center pixel (C) is 1:
* Return 0 if a side pixel (N,W,E,S) and a corner pixel (NW,NE,SW,SE)
* disconnected from each other are both 1, and (sx, sy) falls on
* the corner of the center pixel furthest away from them, and all
* other pixels on the side of that corner are 0;
* Otherwise, return 1.
*
* If the center pixel is 0:
* Return 0 if all four side pixels are 1;
* Otherwise, return 1 if two adjacent side pixels are 1, and
* (sx, sy) falls inside the isosceles right triangle adjoining
* these two neighbor pixels and with legs of length |scaling - 1|,
* and either the corner pixel next to both side pixels is 0, or
* the other two corner pixels next to these side pixels are both 0.
*/
if (neighbors & C) {
return !((sx == 0 && sy == 0 &&
((neighbors & (S|SW|W|NW|N|NE)) == (S|NE) ||
(neighbors & (E|NE|N|NW|W|SW)) == (E|SW))) ||
(sx == scaling - 1 && sy == 0 &&
((neighbors & (W|NW|N|NE|E|SE)) == (W|SE) ||
(neighbors & (S|SE|E|NE|N|NW)) == (S|NW))) ||
(sx == 0 && sy == scaling - 1 &&
((neighbors & (N|NW|W|SW|S|SE)) == (N|SE) ||
(neighbors & (E|SE|S|SW|W|NW)) == (E|NW))) ||
(sx == scaling - 1 && sy == scaling - 1 &&
((neighbors & (N|NE|E|SE|S|SW)) == (N|SW) ||
(neighbors & (W|SW|S|SE|E|NE)) == (W|NE))));
} else {
return ((neighbors & (N|W|E|S)) != (N|W|E|S) &&
((sx < sy &&
(neighbors & (W|S)) == (W|S) &&
((neighbors & SW) == 0 ||
(neighbors & (NW|SE)) == 0)) ||
(sy < sx &&
(neighbors & (N|E)) == (N|E) &&
((neighbors & NE) == 0 ||
(neighbors & (NW|SE)) == 0)) ||
(sx + sy > scaling - 1 &&
(neighbors & (E|S)) == (E|S) &&
((neighbors & SE) == 0 ||
(neighbors & (NE|SW)) == 0)) ||
(sx + sy < scaling - 1 &&
(neighbors & (N|W)) == (N|W) &&
((neighbors & NW) == 0 ||
(neighbors & (NE|SW)) == 0))));
}
}
static void scale_glyph(uint8_t* dst, const uint8_t* src, int scaling)
{
for (int y = 0; y < GLYPH_HEIGHT; y++) {
for (int x = 0; x < GLYPH_WIDTH; x++) {
uint32_t neighbors = 0;
for (int dy = -1; dy <= 1; dy++) {
for (int dx = -1; dx <= 1; dx++) {
neighbors <<= 1;
neighbors |= glyph_pixel(
src, x + dx, y + dy);
}
}
for (int sy = 0; sy < scaling; sy++) {
uint8_t* dst_row = &dst[(y * scaling + sy) *
GLYPH_BYTES_PER_ROW * scaling];
for (int sx = 0; sx < scaling; sx++) {
if (scale_pixel(neighbors, sx, sy,
scaling)) {
set_bit(dst_row,
x * scaling + sx);
}
}
}
}
}
}
static void prescale_font(int scaling)
{
int glyph_count = sizeof(glyphs) / (GLYPH_BYTES_PER_ROW * GLYPH_HEIGHT);
glyph_size = GLYPH_BYTES_PER_ROW * GLYPH_HEIGHT * scaling * scaling;
if (!prescaled_glyphs)
prescaled_glyphs = (uint8_t*)calloc(glyph_count, glyph_size);
for (int i = 0; i < glyph_count; i++) {
const uint8_t* src_glyph = glyphs[i];
uint8_t* dst_glyph = &prescaled_glyphs[i * glyph_size];
scale_glyph(dst_glyph, src_glyph, scaling);
}
}
void font_init(int scaling)
{
if (font_ref == 0) {
font_scaling = scaling;
if (scaling > 1) {
prescale_font(scaling);
}
}
font_ref++;
}
void font_free()
{
font_ref--;
if (font_ref == 0) {
if (prescaled_glyphs) {
free(prescaled_glyphs);
prescaled_glyphs = NULL;
}
}
}
void font_get_size(uint32_t* char_width, uint32_t* char_height)
{
*char_width = GLYPH_WIDTH * font_scaling;
*char_height = GLYPH_HEIGHT * font_scaling;
}
int font_get_scaling()
{
return font_scaling;
}
void font_fillchar(uint32_t* dst_pointer, int dst_char_x, int dst_char_y,
int32_t pitch, uint32_t front_color, uint32_t back_color)
{
int dst_x = dst_char_x * GLYPH_WIDTH * font_scaling;
int dst_y = dst_char_y * GLYPH_HEIGHT * font_scaling;
for (int j = 0; j < GLYPH_HEIGHT * font_scaling; j++)
for (int i = 0; i < GLYPH_WIDTH * font_scaling; i++)
dst_pointer[dst_x + i + (dst_y + j) * pitch / 4] =
back_color;
}
void font_render(uint32_t* dst_pointer, int dst_char_x, int dst_char_y,
int32_t pitch, uint32_t ch, uint32_t front_color,
uint32_t back_color)
{
int dst_x = dst_char_x * GLYPH_WIDTH * font_scaling;
int dst_y = dst_char_y * GLYPH_HEIGHT * font_scaling;
int32_t glyph_index = code_point_to_glyph_index(ch);
if (glyph_index < 0) {
glyph_index = code_point_to_glyph_index(
UNICODE_REPLACEMENT_CHARACTER_CODE_POINT);
if (glyph_index < 0) {
return;
}
}
const uint8_t* glyph;
if (font_scaling == 1) {
glyph = glyphs[glyph_index];
} else {
glyph = &prescaled_glyphs[glyph_index * glyph_size];
}
for (int j = 0; j < GLYPH_HEIGHT * font_scaling; j++) {
const uint8_t* src_row =
&glyph[j * GLYPH_BYTES_PER_ROW * font_scaling];
for (int i = 0; i < GLYPH_WIDTH * font_scaling; i++) {
dst_pointer[dst_x + i + (dst_y + j) * pitch / 4] =
get_bit(src_row, i) ? front_color : back_color;
}
}
}