Stéphane Marchesin | ae37e6c | 2014-08-08 18:19:40 -0700 | [diff] [blame] | 1 | /* |
| 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 Reizner | a752c10 | 2015-04-15 15:03:11 -0700 | [diff] [blame] | 10 | #include "glyphs.h" |
Stéphane Marchesin | ae37e6c | 2014-08-08 18:19:40 -0700 | [diff] [blame] | 11 | #include "util.h" |
| 12 | |
Zach Reizner | a752c10 | 2015-04-15 15:03:11 -0700 | [diff] [blame] | 13 | #define UNICODE_REPLACEMENT_CHARACTER_CODE_POINT 0xFFFD |
Stéphane Marchesin | ae37e6c | 2014-08-08 18:19:40 -0700 | [diff] [blame] | 14 | |
Haixia Shi | 9528587 | 2016-11-08 15:26:35 -0800 | [diff] [blame] | 15 | static int font_scaling = 0; |
Haixia Shi | 95d680e | 2015-04-27 20:29:17 -0700 | [diff] [blame] | 16 | static int glyph_size = GLYPH_BYTES_PER_ROW * GLYPH_HEIGHT; |
Stéphane Marchesin | 00ff187 | 2015-12-14 13:40:09 -0800 | [diff] [blame] | 17 | static uint8_t* prescaled_glyphs = NULL; |
David Sodman | f0a925a | 2015-05-04 11:19:19 -0700 | [diff] [blame] | 18 | static int font_ref = 0; |
Haixia Shi | 95d680e | 2015-04-27 20:29:17 -0700 | [diff] [blame] | 19 | |
Stéphane Marchesin | 00ff187 | 2015-12-14 13:40:09 -0800 | [diff] [blame] | 20 | static uint8_t get_bit(const uint8_t* buffer, int bit_offset) |
Haixia Shi | 95d680e | 2015-04-27 20:29:17 -0700 | [diff] [blame] | 21 | { |
| 22 | return (buffer[bit_offset / 8] >> (7 - (bit_offset % 8))) & 0x1; |
| 23 | } |
| 24 | |
Stéphane Marchesin | 00ff187 | 2015-12-14 13:40:09 -0800 | [diff] [blame] | 25 | static void set_bit(uint8_t* buffer, int bit_offset) |
Haixia Shi | 95d680e | 2015-04-27 20:29:17 -0700 | [diff] [blame] | 26 | { |
| 27 | buffer[bit_offset / 8] |= (0x1 << (7 - (bit_offset % 8))); |
| 28 | } |
| 29 | |
Stéphane Marchesin | 00ff187 | 2015-12-14 13:40:09 -0800 | [diff] [blame] | 30 | static uint8_t glyph_pixel(const uint8_t* glyph, int x, int y) |
Haixia Shi | 95d680e | 2015-04-27 20:29:17 -0700 | [diff] [blame] | 31 | { |
| 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 Shi | 0079afb | 2015-04-29 13:35:38 -0700 | [diff] [blame] | 37 | static uint8_t scale_pixel(uint32_t neighbors, int sx, int sy, int scaling) |
Haixia Shi | 95d680e | 2015-04-27 20:29:17 -0700 | [diff] [blame] | 38 | { |
Haixia Shi | c731c92 | 2015-04-30 14:59:08 -0700 | [diff] [blame] | 39 | /* 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 Shi | 95d680e | 2015-04-27 20:29:17 -0700 | [diff] [blame] | 52 | /* |
Haixia Shi | 0079afb | 2015-04-29 13:35:38 -0700 | [diff] [blame] | 53 | * 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 Shi | c731c92 | 2015-04-30 14:59:08 -0700 | [diff] [blame] | 55 | * |
| 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 Shi | 95d680e | 2015-04-27 20:29:17 -0700 | [diff] [blame] | 69 | * If the center pixel is 0: |
Haixia Shi | c731c92 | 2015-04-30 14:59:08 -0700 | [diff] [blame] | 70 | * Return 0 if all four side pixels are 1; |
Haixia Shi | 0079afb | 2015-04-29 13:35:38 -0700 | [diff] [blame] | 71 | * Otherwise, return 1 if two adjacent side pixels are 1, and |
Haixia Shi | 95d680e | 2015-04-27 20:29:17 -0700 | [diff] [blame] | 72 | * (sx, sy) falls inside the isosceles right triangle adjoining |
Haixia Shi | 0079afb | 2015-04-29 13:35:38 -0700 | [diff] [blame] | 73 | * 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 Shi | 95d680e | 2015-04-27 20:29:17 -0700 | [diff] [blame] | 76 | */ |
Haixia Shi | c731c92 | 2015-04-30 14:59:08 -0700 | [diff] [blame] | 77 | 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 Shi | 95d680e | 2015-04-27 20:29:17 -0700 | [diff] [blame] | 109 | } |
| 110 | |
Stéphane Marchesin | 00ff187 | 2015-12-14 13:40:09 -0800 | [diff] [blame] | 111 | static void scale_glyph(uint8_t* dst, const uint8_t* src, int scaling) |
Haixia Shi | 95d680e | 2015-04-27 20:29:17 -0700 | [diff] [blame] | 112 | { |
| 113 | for (int y = 0; y < GLYPH_HEIGHT; y++) { |
| 114 | for (int x = 0; x < GLYPH_WIDTH; x++) { |
Haixia Shi | 0079afb | 2015-04-29 13:35:38 -0700 | [diff] [blame] | 115 | 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 Shi | 95d680e | 2015-04-27 20:29:17 -0700 | [diff] [blame] | 123 | for (int sy = 0; sy < scaling; sy++) { |
Stéphane Marchesin | 00ff187 | 2015-12-14 13:40:09 -0800 | [diff] [blame] | 124 | uint8_t* dst_row = &dst[(y * scaling + sy) * |
Haixia Shi | 95d680e | 2015-04-27 20:29:17 -0700 | [diff] [blame] | 125 | 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 | |
| 138 | static void prescale_font(int scaling) |
| 139 | { |
| 140 | int glyph_count = sizeof(glyphs) / (GLYPH_BYTES_PER_ROW * GLYPH_HEIGHT); |
Stéphane Marchesin | ac14d29 | 2015-12-14 15:27:18 -0800 | [diff] [blame] | 141 | |
Haixia Shi | 95d680e | 2015-04-27 20:29:17 -0700 | [diff] [blame] | 142 | glyph_size = GLYPH_BYTES_PER_ROW * GLYPH_HEIGHT * scaling * scaling; |
David Sodman | f0a925a | 2015-05-04 11:19:19 -0700 | [diff] [blame] | 143 | if (!prescaled_glyphs) |
Stéphane Marchesin | 00ff187 | 2015-12-14 13:40:09 -0800 | [diff] [blame] | 144 | prescaled_glyphs = (uint8_t*)calloc(glyph_count, glyph_size); |
Haixia Shi | 95d680e | 2015-04-27 20:29:17 -0700 | [diff] [blame] | 145 | for (int i = 0; i < glyph_count; i++) { |
Stéphane Marchesin | 00ff187 | 2015-12-14 13:40:09 -0800 | [diff] [blame] | 146 | const uint8_t* src_glyph = glyphs[i]; |
| 147 | uint8_t* dst_glyph = &prescaled_glyphs[i * glyph_size]; |
Haixia Shi | 95d680e | 2015-04-27 20:29:17 -0700 | [diff] [blame] | 148 | scale_glyph(dst_glyph, src_glyph, scaling); |
| 149 | } |
| 150 | } |
Stéphane Marchesin | af4423b | 2014-08-13 16:39:24 -0700 | [diff] [blame] | 151 | |
Stéphane Marchesin | af4423b | 2014-08-13 16:39:24 -0700 | [diff] [blame] | 152 | void font_init(int scaling) |
| 153 | { |
David Sodman | f0a925a | 2015-05-04 11:19:19 -0700 | [diff] [blame] | 154 | if (font_ref == 0) { |
| 155 | font_scaling = scaling; |
| 156 | if (scaling > 1) { |
| 157 | prescale_font(scaling); |
| 158 | } |
Haixia Shi | 95d680e | 2015-04-27 20:29:17 -0700 | [diff] [blame] | 159 | } |
David Sodman | f0a925a | 2015-05-04 11:19:19 -0700 | [diff] [blame] | 160 | font_ref++; |
Haixia Shi | 95d680e | 2015-04-27 20:29:17 -0700 | [diff] [blame] | 161 | } |
| 162 | |
| 163 | void font_free() |
| 164 | { |
David Sodman | f0a925a | 2015-05-04 11:19:19 -0700 | [diff] [blame] | 165 | font_ref--; |
| 166 | if (font_ref == 0) { |
| 167 | if (prescaled_glyphs) { |
| 168 | free(prescaled_glyphs); |
| 169 | prescaled_glyphs = NULL; |
| 170 | } |
Haixia Shi | 95d680e | 2015-04-27 20:29:17 -0700 | [diff] [blame] | 171 | } |
Stéphane Marchesin | af4423b | 2014-08-13 16:39:24 -0700 | [diff] [blame] | 172 | } |
| 173 | |
Stéphane Marchesin | 00ff187 | 2015-12-14 13:40:09 -0800 | [diff] [blame] | 174 | void font_get_size(uint32_t* char_width, uint32_t* char_height) |
Stéphane Marchesin | ae37e6c | 2014-08-08 18:19:40 -0700 | [diff] [blame] | 175 | { |
Stéphane Marchesin | af4423b | 2014-08-13 16:39:24 -0700 | [diff] [blame] | 176 | *char_width = GLYPH_WIDTH * font_scaling; |
| 177 | *char_height = GLYPH_HEIGHT * font_scaling; |
Stéphane Marchesin | ae37e6c | 2014-08-08 18:19:40 -0700 | [diff] [blame] | 178 | } |
| 179 | |
Haixia Shi | 9528587 | 2016-11-08 15:26:35 -0800 | [diff] [blame] | 180 | |
| 181 | int font_get_scaling() |
| 182 | { |
| 183 | return font_scaling; |
| 184 | } |
| 185 | |
Dominik Behr | bb728f3 | 2019-09-03 17:52:13 -0700 | [diff] [blame] | 186 | void font_fillchar(fb_t *fb, int dst_char_x, int dst_char_y, |
| 187 | uint32_t front_color, uint32_t back_color) |
Stéphane Marchesin | ae37e6c | 2014-08-08 18:19:40 -0700 | [diff] [blame] | 188 | { |
Dominik Behr | bb728f3 | 2019-09-03 17:52:13 -0700 | [diff] [blame] | 189 | fb_stepper_t s; |
Stéphane Marchesin | ae37e6c | 2014-08-08 18:19:40 -0700 | [diff] [blame] | 190 | |
Dominik Behr | bb728f3 | 2019-09-03 17:52:13 -0700 | [diff] [blame] | 191 | 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 Marchesin | ae37e6c | 2014-08-08 18:19:40 -0700 | [diff] [blame] | 202 | } |
| 203 | |
Dominik Behr | bb728f3 | 2019-09-03 17:52:13 -0700 | [diff] [blame] | 204 | void font_render(fb_t *fb, int dst_char_x, int dst_char_y, |
| 205 | uint32_t ch, uint32_t front_color, |
Stéphane Marchesin | ae37e6c | 2014-08-08 18:19:40 -0700 | [diff] [blame] | 206 | uint32_t back_color) |
| 207 | { |
Haixia Shi | 7795552 | 2015-04-22 15:21:03 -0700 | [diff] [blame] | 208 | int32_t glyph_index = code_point_to_glyph_index(ch); |
Dominik Behr | bb728f3 | 2019-09-03 17:52:13 -0700 | [diff] [blame] | 209 | fb_stepper_t s; |
Stéphane Marchesin | ac14d29 | 2015-12-14 15:27:18 -0800 | [diff] [blame] | 210 | |
Zach Reizner | a752c10 | 2015-04-15 15:03:11 -0700 | [diff] [blame] | 211 | if (glyph_index < 0) { |
Haixia Shi | 7795552 | 2015-04-22 15:21:03 -0700 | [diff] [blame] | 212 | glyph_index = code_point_to_glyph_index( |
Zach Reizner | a752c10 | 2015-04-15 15:03:11 -0700 | [diff] [blame] | 213 | UNICODE_REPLACEMENT_CHARACTER_CODE_POINT); |
| 214 | if (glyph_index < 0) { |
| 215 | return; |
| 216 | } |
| 217 | } |
| 218 | |
Dominik Behr | bb728f3 | 2019-09-03 17:52:13 -0700 | [diff] [blame] | 219 | 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 Marchesin | 00ff187 | 2015-12-14 13:40:09 -0800 | [diff] [blame] | 226 | const uint8_t* glyph; |
Haixia Shi | 95d680e | 2015-04-27 20:29:17 -0700 | [diff] [blame] | 227 | if (font_scaling == 1) { |
| 228 | glyph = glyphs[glyph_index]; |
| 229 | } else { |
| 230 | glyph = &prescaled_glyphs[glyph_index * glyph_size]; |
| 231 | } |
Stéphane Marchesin | ae37e6c | 2014-08-08 18:19:40 -0700 | [diff] [blame] | 232 | |
Dominik Behr | bb728f3 | 2019-09-03 17:52:13 -0700 | [diff] [blame] | 233 | do { |
Stéphane Marchesin | 00ff187 | 2015-12-14 13:40:09 -0800 | [diff] [blame] | 234 | const uint8_t* src_row = |
Dominik Behr | bb728f3 | 2019-09-03 17:52:13 -0700 | [diff] [blame] | 235 | &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 Marchesin | ae37e6c | 2014-08-08 18:19:40 -0700 | [diff] [blame] | 239 | } |