blob: 450753fca0039583224e56d3a8c18f302d801a07 [file] [log] [blame]
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -07001/*
2 * Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 */
6
7#include <stdint.h>
8
9#include "font.h"
Zach Reiznera752c102015-04-15 15:03:11 -070010#include "glyphs.h"
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070011#include "util.h"
12
Zach Reiznera752c102015-04-15 15:03:11 -070013#define UNICODE_REPLACEMENT_CHARACTER_CODE_POINT 0xFFFD
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -070014
Haixia Shi95285872016-11-08 15:26:35 -080015static int font_scaling = 0;
Haixia Shi95d680e2015-04-27 20:29:17 -070016static int glyph_size = GLYPH_BYTES_PER_ROW * GLYPH_HEIGHT;
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080017static uint8_t* prescaled_glyphs = NULL;
David Sodmanf0a925a2015-05-04 11:19:19 -070018static int font_ref = 0;
Haixia Shi95d680e2015-04-27 20:29:17 -070019
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080020static uint8_t get_bit(const uint8_t* buffer, int bit_offset)
Haixia Shi95d680e2015-04-27 20:29:17 -070021{
22 return (buffer[bit_offset / 8] >> (7 - (bit_offset % 8))) & 0x1;
23}
24
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080025static void set_bit(uint8_t* buffer, int bit_offset)
Haixia Shi95d680e2015-04-27 20:29:17 -070026{
27 buffer[bit_offset / 8] |= (0x1 << (7 - (bit_offset % 8)));
28}
29
Stéphane Marchesin00ff1872015-12-14 13:40:09 -080030static uint8_t glyph_pixel(const uint8_t* glyph, int x, int y)
Haixia Shi95d680e2015-04-27 20:29:17 -070031{
32 if (x < 0 || x >= GLYPH_WIDTH || y < 0 || y >= GLYPH_HEIGHT)
33 return 0;
34 return get_bit(&glyph[y * GLYPH_BYTES_PER_ROW], x);
35}
36
Haixia Shi0079afb2015-04-29 13:35:38 -070037static uint8_t scale_pixel(uint32_t neighbors, int sx, int sy, int scaling)
Haixia Shi95d680e2015-04-27 20:29:17 -070038{
Haixia Shic731c922015-04-30 14:59:08 -070039 /* Bitmasks of neighbor pixels */
40 enum {
41 NW = (1 << 8),
42 N = (1 << 7),
43 NE = (1 << 6),
44 W = (1 << 5),
45 C = (1 << 4),
46 E = (1 << 3),
47 SW = (1 << 2),
48 S = (1 << 1),
49 SE = (1 << 0),
50 };
51
Haixia Shi95d680e2015-04-27 20:29:17 -070052 /*
Haixia Shi0079afb2015-04-29 13:35:38 -070053 * Scale a pixel by a factor of |scaling|, based on the colors of the
54 * center pixel and the eight neighbor pixels on a 3x3 grid:
Haixia Shic731c922015-04-30 14:59:08 -070055 *
56 * NW | N | NE
57 * ---+---+---
58 * W | C | E
59 * ---+---+---
60 * SW | S | SE
61 *
62 * If the center pixel (C) is 1:
63 * Return 0 if a side pixel (N,W,E,S) and a corner pixel (NW,NE,SW,SE)
64 * disconnected from each other are both 1, and (sx, sy) falls on
65 * the corner of the center pixel furthest away from them, and all
66 * other pixels on the side of that corner are 0;
67 * Otherwise, return 1.
68 *
Haixia Shi95d680e2015-04-27 20:29:17 -070069 * If the center pixel is 0:
Haixia Shic731c922015-04-30 14:59:08 -070070 * Return 0 if all four side pixels are 1;
Haixia Shi0079afb2015-04-29 13:35:38 -070071 * Otherwise, return 1 if two adjacent side pixels are 1, and
Haixia Shi95d680e2015-04-27 20:29:17 -070072 * (sx, sy) falls inside the isosceles right triangle adjoining
Haixia Shi0079afb2015-04-29 13:35:38 -070073 * these two neighbor pixels and with legs of length |scaling - 1|,
74 * and either the corner pixel next to both side pixels is 0, or
75 * the other two corner pixels next to these side pixels are both 0.
Haixia Shi95d680e2015-04-27 20:29:17 -070076 */
Haixia Shic731c922015-04-30 14:59:08 -070077 if (neighbors & C) {
78 return !((sx == 0 && sy == 0 &&
79 ((neighbors & (S|SW|W|NW|N|NE)) == (S|NE) ||
80 (neighbors & (E|NE|N|NW|W|SW)) == (E|SW))) ||
81 (sx == scaling - 1 && sy == 0 &&
82 ((neighbors & (W|NW|N|NE|E|SE)) == (W|SE) ||
83 (neighbors & (S|SE|E|NE|N|NW)) == (S|NW))) ||
84 (sx == 0 && sy == scaling - 1 &&
85 ((neighbors & (N|NW|W|SW|S|SE)) == (N|SE) ||
86 (neighbors & (E|SE|S|SW|W|NW)) == (E|NW))) ||
87 (sx == scaling - 1 && sy == scaling - 1 &&
88 ((neighbors & (N|NE|E|SE|S|SW)) == (N|SW) ||
89 (neighbors & (W|SW|S|SE|E|NE)) == (W|NE))));
90 } else {
91 return ((neighbors & (N|W|E|S)) != (N|W|E|S) &&
92 ((sx < sy &&
93 (neighbors & (W|S)) == (W|S) &&
94 ((neighbors & SW) == 0 ||
95 (neighbors & (NW|SE)) == 0)) ||
96 (sy < sx &&
97 (neighbors & (N|E)) == (N|E) &&
98 ((neighbors & NE) == 0 ||
99 (neighbors & (NW|SE)) == 0)) ||
100 (sx + sy > scaling - 1 &&
101 (neighbors & (E|S)) == (E|S) &&
102 ((neighbors & SE) == 0 ||
103 (neighbors & (NE|SW)) == 0)) ||
104 (sx + sy < scaling - 1 &&
105 (neighbors & (N|W)) == (N|W) &&
106 ((neighbors & NW) == 0 ||
107 (neighbors & (NE|SW)) == 0))));
108 }
Haixia Shi95d680e2015-04-27 20:29:17 -0700109}
110
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800111static void scale_glyph(uint8_t* dst, const uint8_t* src, int scaling)
Haixia Shi95d680e2015-04-27 20:29:17 -0700112{
113 for (int y = 0; y < GLYPH_HEIGHT; y++) {
114 for (int x = 0; x < GLYPH_WIDTH; x++) {
Haixia Shi0079afb2015-04-29 13:35:38 -0700115 uint32_t neighbors = 0;
116 for (int dy = -1; dy <= 1; dy++) {
117 for (int dx = -1; dx <= 1; dx++) {
118 neighbors <<= 1;
119 neighbors |= glyph_pixel(
120 src, x + dx, y + dy);
121 }
122 }
Haixia Shi95d680e2015-04-27 20:29:17 -0700123 for (int sy = 0; sy < scaling; sy++) {
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800124 uint8_t* dst_row = &dst[(y * scaling + sy) *
Haixia Shi95d680e2015-04-27 20:29:17 -0700125 GLYPH_BYTES_PER_ROW * scaling];
126 for (int sx = 0; sx < scaling; sx++) {
127 if (scale_pixel(neighbors, sx, sy,
128 scaling)) {
129 set_bit(dst_row,
130 x * scaling + sx);
131 }
132 }
133 }
134 }
135 }
136}
137
138static void prescale_font(int scaling)
139{
140 int glyph_count = sizeof(glyphs) / (GLYPH_BYTES_PER_ROW * GLYPH_HEIGHT);
Stéphane Marchesinac14d292015-12-14 15:27:18 -0800141
Haixia Shi95d680e2015-04-27 20:29:17 -0700142 glyph_size = GLYPH_BYTES_PER_ROW * GLYPH_HEIGHT * scaling * scaling;
David Sodmanf0a925a2015-05-04 11:19:19 -0700143 if (!prescaled_glyphs)
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800144 prescaled_glyphs = (uint8_t*)calloc(glyph_count, glyph_size);
Haixia Shi95d680e2015-04-27 20:29:17 -0700145 for (int i = 0; i < glyph_count; i++) {
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800146 const uint8_t* src_glyph = glyphs[i];
147 uint8_t* dst_glyph = &prescaled_glyphs[i * glyph_size];
Haixia Shi95d680e2015-04-27 20:29:17 -0700148 scale_glyph(dst_glyph, src_glyph, scaling);
149 }
150}
Stéphane Marchesinaf4423b2014-08-13 16:39:24 -0700151
Stéphane Marchesinaf4423b2014-08-13 16:39:24 -0700152void font_init(int scaling)
153{
David Sodmanf0a925a2015-05-04 11:19:19 -0700154 if (font_ref == 0) {
155 font_scaling = scaling;
156 if (scaling > 1) {
157 prescale_font(scaling);
158 }
Haixia Shi95d680e2015-04-27 20:29:17 -0700159 }
David Sodmanf0a925a2015-05-04 11:19:19 -0700160 font_ref++;
Haixia Shi95d680e2015-04-27 20:29:17 -0700161}
162
163void font_free()
164{
David Sodmanf0a925a2015-05-04 11:19:19 -0700165 font_ref--;
166 if (font_ref == 0) {
167 if (prescaled_glyphs) {
168 free(prescaled_glyphs);
169 prescaled_glyphs = NULL;
170 }
Haixia Shi95d680e2015-04-27 20:29:17 -0700171 }
Stéphane Marchesinaf4423b2014-08-13 16:39:24 -0700172}
173
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800174void font_get_size(uint32_t* char_width, uint32_t* char_height)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700175{
Stéphane Marchesinaf4423b2014-08-13 16:39:24 -0700176 *char_width = GLYPH_WIDTH * font_scaling;
177 *char_height = GLYPH_HEIGHT * font_scaling;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700178}
179
Haixia Shi95285872016-11-08 15:26:35 -0800180
181int font_get_scaling()
182{
183 return font_scaling;
184}
185
Dominik Behrbb728f32019-09-03 17:52:13 -0700186void font_fillchar(fb_t *fb, int dst_char_x, int dst_char_y,
187 uint32_t front_color, uint32_t back_color)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700188{
Dominik Behrbb728f32019-09-03 17:52:13 -0700189 fb_stepper_t s;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700190
Dominik Behrbb728f32019-09-03 17:52:13 -0700191 fb_stepper_init(&s,
192 fb,
193 dst_char_x * GLYPH_WIDTH * font_scaling,
194 dst_char_y * GLYPH_HEIGHT * font_scaling,
195 GLYPH_WIDTH * font_scaling,
196 GLYPH_HEIGHT * font_scaling);
197
198 do {
199 do {
200 } while (fb_stepper_step_x(&s, back_color));
201 } while (fb_stepper_step_y(&s));
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700202}
203
Dominik Behrbb728f32019-09-03 17:52:13 -0700204void font_render(fb_t *fb, int dst_char_x, int dst_char_y,
205 uint32_t ch, uint32_t front_color,
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700206 uint32_t back_color)
207{
Haixia Shi77955522015-04-22 15:21:03 -0700208 int32_t glyph_index = code_point_to_glyph_index(ch);
Dominik Behrbb728f32019-09-03 17:52:13 -0700209 fb_stepper_t s;
Stéphane Marchesinac14d292015-12-14 15:27:18 -0800210
Zach Reiznera752c102015-04-15 15:03:11 -0700211 if (glyph_index < 0) {
Haixia Shi77955522015-04-22 15:21:03 -0700212 glyph_index = code_point_to_glyph_index(
Zach Reiznera752c102015-04-15 15:03:11 -0700213 UNICODE_REPLACEMENT_CHARACTER_CODE_POINT);
214 if (glyph_index < 0) {
215 return;
216 }
217 }
218
Dominik Behrbb728f32019-09-03 17:52:13 -0700219 fb_stepper_init(&s,
220 fb,
221 dst_char_x * GLYPH_WIDTH * font_scaling,
222 dst_char_y * GLYPH_HEIGHT * font_scaling,
223 GLYPH_WIDTH * font_scaling,
224 GLYPH_HEIGHT * font_scaling);
225
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800226 const uint8_t* glyph;
Haixia Shi95d680e2015-04-27 20:29:17 -0700227 if (font_scaling == 1) {
228 glyph = glyphs[glyph_index];
229 } else {
230 glyph = &prescaled_glyphs[glyph_index * glyph_size];
231 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700232
Dominik Behrbb728f32019-09-03 17:52:13 -0700233 do {
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800234 const uint8_t* src_row =
Dominik Behrbb728f32019-09-03 17:52:13 -0700235 &glyph[s.y * GLYPH_BYTES_PER_ROW * font_scaling];
236 do {
237 } while (fb_stepper_step_x(&s, get_bit(src_row, s.x) ? front_color : back_color));
238 } while (fb_stepper_step_y(&s));
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700239}