| /* |
| * 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(fb_t *fb, int dst_char_x, int dst_char_y, |
| uint32_t front_color, uint32_t back_color) |
| { |
| fb_stepper_t s; |
| |
| fb_stepper_init(&s, |
| fb, |
| dst_char_x * GLYPH_WIDTH * font_scaling, |
| dst_char_y * GLYPH_HEIGHT * font_scaling, |
| GLYPH_WIDTH * font_scaling, |
| GLYPH_HEIGHT * font_scaling); |
| |
| do { |
| do { |
| } while (fb_stepper_step_x(&s, back_color)); |
| } while (fb_stepper_step_y(&s)); |
| } |
| |
| void font_render(fb_t *fb, int dst_char_x, int dst_char_y, |
| uint32_t ch, uint32_t front_color, |
| uint32_t back_color) |
| { |
| int32_t glyph_index = code_point_to_glyph_index(ch); |
| fb_stepper_t s; |
| |
| if (glyph_index < 0) { |
| glyph_index = code_point_to_glyph_index( |
| UNICODE_REPLACEMENT_CHARACTER_CODE_POINT); |
| if (glyph_index < 0) { |
| return; |
| } |
| } |
| |
| fb_stepper_init(&s, |
| fb, |
| dst_char_x * GLYPH_WIDTH * font_scaling, |
| dst_char_y * GLYPH_HEIGHT * font_scaling, |
| GLYPH_WIDTH * font_scaling, |
| GLYPH_HEIGHT * font_scaling); |
| |
| const uint8_t* glyph; |
| if (font_scaling == 1) { |
| glyph = glyphs[glyph_index]; |
| } else { |
| glyph = &prescaled_glyphs[glyph_index * glyph_size]; |
| } |
| |
| do { |
| const uint8_t* src_row = |
| &glyph[s.y * GLYPH_BYTES_PER_ROW * font_scaling]; |
| do { |
| } while (fb_stepper_step_x(&s, get_bit(src_row, s.x) ? front_color : back_color)); |
| } while (fb_stepper_step_y(&s)); |
| } |