blob: 614e38a5cc11b2964ec7031eb19064f4a169a488 [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 Shi95d680e2015-04-27 20:29:17 -070015static int font_scaling = 1;
16static 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
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800180void font_fillchar(uint32_t* dst_pointer, int dst_char_x, int dst_char_y,
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700181 int32_t pitch, uint32_t front_color, uint32_t back_color)
182{
Stéphane Marchesinaf4423b2014-08-13 16:39:24 -0700183 int dst_x = dst_char_x * GLYPH_WIDTH * font_scaling;
184 int dst_y = dst_char_y * GLYPH_HEIGHT * font_scaling;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700185
Stéphane Marchesinaf4423b2014-08-13 16:39:24 -0700186 for (int j = 0; j < GLYPH_HEIGHT * font_scaling; j++)
187 for (int i = 0; i < GLYPH_WIDTH * font_scaling; i++)
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700188 dst_pointer[dst_x + i + (dst_y + j) * pitch / 4] =
189 back_color;
190}
191
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800192void font_render(uint32_t* dst_pointer, int dst_char_x, int dst_char_y,
Dominik Behr00003502014-08-15 16:42:37 -0700193 int32_t pitch, uint32_t ch, uint32_t front_color,
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700194 uint32_t back_color)
195{
Stéphane Marchesinaf4423b2014-08-13 16:39:24 -0700196 int dst_x = dst_char_x * GLYPH_WIDTH * font_scaling;
197 int dst_y = dst_char_y * GLYPH_HEIGHT * font_scaling;
Haixia Shi77955522015-04-22 15:21:03 -0700198 int32_t glyph_index = code_point_to_glyph_index(ch);
Stéphane Marchesinac14d292015-12-14 15:27:18 -0800199
Zach Reiznera752c102015-04-15 15:03:11 -0700200 if (glyph_index < 0) {
Haixia Shi77955522015-04-22 15:21:03 -0700201 glyph_index = code_point_to_glyph_index(
Zach Reiznera752c102015-04-15 15:03:11 -0700202 UNICODE_REPLACEMENT_CHARACTER_CODE_POINT);
203 if (glyph_index < 0) {
204 return;
205 }
206 }
207
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800208 const uint8_t* glyph;
Haixia Shi95d680e2015-04-27 20:29:17 -0700209 if (font_scaling == 1) {
210 glyph = glyphs[glyph_index];
211 } else {
212 glyph = &prescaled_glyphs[glyph_index * glyph_size];
213 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700214
Haixia Shi95d680e2015-04-27 20:29:17 -0700215 for (int j = 0; j < GLYPH_HEIGHT * font_scaling; j++) {
Stéphane Marchesin00ff1872015-12-14 13:40:09 -0800216 const uint8_t* src_row =
Haixia Shi95d680e2015-04-27 20:29:17 -0700217 &glyph[j * GLYPH_BYTES_PER_ROW * font_scaling];
218 for (int i = 0; i < GLYPH_WIDTH * font_scaling; i++) {
219 dst_pointer[dst_x + i + (dst_y + j) * pitch / 4] =
220 get_bit(src_row, i) ? front_color : back_color;
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700221 }
Haixia Shi95d680e2015-04-27 20:29:17 -0700222 }
Stéphane Marchesinae37e6c2014-08-08 18:19:40 -0700223}