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 | 95d680e | 2015-04-27 20:29:17 -0700 | [diff] [blame] | 15 | static int font_scaling = 1; |
| 16 | static int glyph_size = GLYPH_BYTES_PER_ROW * GLYPH_HEIGHT; |
| 17 | static uint8_t *prescaled_glyphs = NULL; |
| 18 | |
| 19 | static uint8_t get_bit(const uint8_t *buffer, int bit_offset) |
| 20 | { |
| 21 | return (buffer[bit_offset / 8] >> (7 - (bit_offset % 8))) & 0x1; |
| 22 | } |
| 23 | |
| 24 | static void set_bit(uint8_t *buffer, int bit_offset) |
| 25 | { |
| 26 | buffer[bit_offset / 8] |= (0x1 << (7 - (bit_offset % 8))); |
| 27 | } |
| 28 | |
| 29 | static uint8_t glyph_pixel(const uint8_t *glyph, int x, int y) |
| 30 | { |
| 31 | if (x < 0 || x >= GLYPH_WIDTH || y < 0 || y >= GLYPH_HEIGHT) |
| 32 | return 0; |
| 33 | return get_bit(&glyph[y * GLYPH_BYTES_PER_ROW], x); |
| 34 | } |
| 35 | |
Haixia Shi | 0079afb | 2015-04-29 13:35:38 -0700 | [diff] [blame^] | 36 | 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] | 37 | { |
| 38 | /* |
Haixia Shi | 0079afb | 2015-04-29 13:35:38 -0700 | [diff] [blame^] | 39 | * Scale a pixel by a factor of |scaling|, based on the colors of the |
| 40 | * center pixel and the eight neighbor pixels on a 3x3 grid: |
Haixia Shi | 95d680e | 2015-04-27 20:29:17 -0700 | [diff] [blame] | 41 | * If the center pixel is 1, always return 1; |
| 42 | * If the center pixel is 0: |
Haixia Shi | 0079afb | 2015-04-29 13:35:38 -0700 | [diff] [blame^] | 43 | * Return 0 if all four side pixels (up, down, left, right) are 1; |
| 44 | * Otherwise, return 1 if two adjacent side pixels are 1, and |
Haixia Shi | 95d680e | 2015-04-27 20:29:17 -0700 | [diff] [blame] | 45 | * (sx, sy) falls inside the isosceles right triangle adjoining |
Haixia Shi | 0079afb | 2015-04-29 13:35:38 -0700 | [diff] [blame^] | 46 | * these two neighbor pixels and with legs of length |scaling - 1|, |
| 47 | * and either the corner pixel next to both side pixels is 0, or |
| 48 | * 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] | 49 | */ |
Haixia Shi | 0079afb | 2015-04-29 13:35:38 -0700 | [diff] [blame^] | 50 | return ((neighbors & 0x10) || |
| 51 | ((neighbors & 0xaa) != 0xaa && |
| 52 | ((sx < sy && |
| 53 | (neighbors & 0x22) == 0x22 && |
| 54 | ((neighbors & 0x4) == 0x0 || |
| 55 | (neighbors & 0x105) == 0x4)) || |
| 56 | (sy < sx && |
| 57 | (neighbors & 0x88) == 0x88 && |
| 58 | ((neighbors & 0x40) == 0x0 || |
| 59 | (neighbors & 0x141) == 0x40)) || |
| 60 | (sx + sy > scaling - 1 && |
| 61 | (neighbors & 0x0a) == 0x0a && |
| 62 | ((neighbors & 0x1) == 0x0 || |
| 63 | (neighbors & 0x45) == 0x1)) || |
| 64 | (sx + sy < scaling - 1 && |
| 65 | (neighbors & 0xa0) == 0xa0 && |
| 66 | ((neighbors & 0x100) == 0x0 || |
| 67 | (neighbors & 0x144) == 0x100))))); |
Haixia Shi | 95d680e | 2015-04-27 20:29:17 -0700 | [diff] [blame] | 68 | } |
| 69 | |
| 70 | static void scale_glyph(uint8_t *dst, const uint8_t *src, int scaling) |
| 71 | { |
| 72 | for (int y = 0; y < GLYPH_HEIGHT; y++) { |
| 73 | for (int x = 0; x < GLYPH_WIDTH; x++) { |
Haixia Shi | 0079afb | 2015-04-29 13:35:38 -0700 | [diff] [blame^] | 74 | uint32_t neighbors = 0; |
| 75 | for (int dy = -1; dy <= 1; dy++) { |
| 76 | for (int dx = -1; dx <= 1; dx++) { |
| 77 | neighbors <<= 1; |
| 78 | neighbors |= glyph_pixel( |
| 79 | src, x + dx, y + dy); |
| 80 | } |
| 81 | } |
Haixia Shi | 95d680e | 2015-04-27 20:29:17 -0700 | [diff] [blame] | 82 | for (int sy = 0; sy < scaling; sy++) { |
| 83 | uint8_t *dst_row = &dst[(y * scaling + sy) * |
| 84 | GLYPH_BYTES_PER_ROW * scaling]; |
| 85 | for (int sx = 0; sx < scaling; sx++) { |
| 86 | if (scale_pixel(neighbors, sx, sy, |
| 87 | scaling)) { |
| 88 | set_bit(dst_row, |
| 89 | x * scaling + sx); |
| 90 | } |
| 91 | } |
| 92 | } |
| 93 | } |
| 94 | } |
| 95 | } |
| 96 | |
| 97 | static void prescale_font(int scaling) |
| 98 | { |
| 99 | int glyph_count = sizeof(glyphs) / (GLYPH_BYTES_PER_ROW * GLYPH_HEIGHT); |
| 100 | glyph_size = GLYPH_BYTES_PER_ROW * GLYPH_HEIGHT * scaling * scaling; |
| 101 | prescaled_glyphs = (uint8_t *)calloc(glyph_count, glyph_size); |
| 102 | for (int i = 0; i < glyph_count; i++) { |
| 103 | const uint8_t *src_glyph = glyphs[i]; |
| 104 | uint8_t *dst_glyph = &prescaled_glyphs[i * glyph_size]; |
| 105 | scale_glyph(dst_glyph, src_glyph, scaling); |
| 106 | } |
| 107 | } |
Stéphane Marchesin | af4423b | 2014-08-13 16:39:24 -0700 | [diff] [blame] | 108 | |
Stéphane Marchesin | af4423b | 2014-08-13 16:39:24 -0700 | [diff] [blame] | 109 | void font_init(int scaling) |
| 110 | { |
| 111 | font_scaling = scaling; |
Haixia Shi | 95d680e | 2015-04-27 20:29:17 -0700 | [diff] [blame] | 112 | if (scaling > 1) { |
| 113 | prescale_font(scaling); |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | void font_free() |
| 118 | { |
| 119 | if (prescaled_glyphs) { |
| 120 | free(prescaled_glyphs); |
| 121 | prescaled_glyphs = NULL; |
| 122 | } |
Stéphane Marchesin | af4423b | 2014-08-13 16:39:24 -0700 | [diff] [blame] | 123 | } |
| 124 | |
Stéphane Marchesin | f8807af | 2014-08-18 10:34:41 -0700 | [diff] [blame] | 125 | 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] | 126 | { |
Stéphane Marchesin | af4423b | 2014-08-13 16:39:24 -0700 | [diff] [blame] | 127 | *char_width = GLYPH_WIDTH * font_scaling; |
| 128 | *char_height = GLYPH_HEIGHT * font_scaling; |
Stéphane Marchesin | ae37e6c | 2014-08-08 18:19:40 -0700 | [diff] [blame] | 129 | } |
| 130 | |
Stéphane Marchesin | f8807af | 2014-08-18 10:34:41 -0700 | [diff] [blame] | 131 | void font_fillchar(uint32_t *dst_pointer, int dst_char_x, int dst_char_y, |
Stéphane Marchesin | ae37e6c | 2014-08-08 18:19:40 -0700 | [diff] [blame] | 132 | int32_t pitch, uint32_t front_color, uint32_t back_color) |
| 133 | { |
Stéphane Marchesin | af4423b | 2014-08-13 16:39:24 -0700 | [diff] [blame] | 134 | int dst_x = dst_char_x * GLYPH_WIDTH * font_scaling; |
| 135 | int dst_y = dst_char_y * GLYPH_HEIGHT * font_scaling; |
Stéphane Marchesin | ae37e6c | 2014-08-08 18:19:40 -0700 | [diff] [blame] | 136 | |
Stéphane Marchesin | af4423b | 2014-08-13 16:39:24 -0700 | [diff] [blame] | 137 | for (int j = 0; j < GLYPH_HEIGHT * font_scaling; j++) |
| 138 | for (int i = 0; i < GLYPH_WIDTH * font_scaling; i++) |
Stéphane Marchesin | ae37e6c | 2014-08-08 18:19:40 -0700 | [diff] [blame] | 139 | dst_pointer[dst_x + i + (dst_y + j) * pitch / 4] = |
| 140 | back_color; |
| 141 | } |
| 142 | |
Stéphane Marchesin | f8807af | 2014-08-18 10:34:41 -0700 | [diff] [blame] | 143 | void font_render(uint32_t *dst_pointer, int dst_char_x, int dst_char_y, |
Dominik Behr | 0000350 | 2014-08-15 16:42:37 -0700 | [diff] [blame] | 144 | int32_t pitch, uint32_t ch, uint32_t front_color, |
Stéphane Marchesin | ae37e6c | 2014-08-08 18:19:40 -0700 | [diff] [blame] | 145 | uint32_t back_color) |
| 146 | { |
Stéphane Marchesin | af4423b | 2014-08-13 16:39:24 -0700 | [diff] [blame] | 147 | int dst_x = dst_char_x * GLYPH_WIDTH * font_scaling; |
| 148 | int dst_y = dst_char_y * GLYPH_HEIGHT * font_scaling; |
Stéphane Marchesin | ae37e6c | 2014-08-08 18:19:40 -0700 | [diff] [blame] | 149 | |
Haixia Shi | 7795552 | 2015-04-22 15:21:03 -0700 | [diff] [blame] | 150 | int32_t glyph_index = code_point_to_glyph_index(ch); |
Zach Reizner | a752c10 | 2015-04-15 15:03:11 -0700 | [diff] [blame] | 151 | if (glyph_index < 0) { |
Haixia Shi | 7795552 | 2015-04-22 15:21:03 -0700 | [diff] [blame] | 152 | glyph_index = code_point_to_glyph_index( |
Zach Reizner | a752c10 | 2015-04-15 15:03:11 -0700 | [diff] [blame] | 153 | UNICODE_REPLACEMENT_CHARACTER_CODE_POINT); |
| 154 | if (glyph_index < 0) { |
| 155 | return; |
| 156 | } |
| 157 | } |
| 158 | |
Haixia Shi | 95d680e | 2015-04-27 20:29:17 -0700 | [diff] [blame] | 159 | const uint8_t *glyph; |
| 160 | if (font_scaling == 1) { |
| 161 | glyph = glyphs[glyph_index]; |
| 162 | } else { |
| 163 | glyph = &prescaled_glyphs[glyph_index * glyph_size]; |
| 164 | } |
Stéphane Marchesin | ae37e6c | 2014-08-08 18:19:40 -0700 | [diff] [blame] | 165 | |
Haixia Shi | 95d680e | 2015-04-27 20:29:17 -0700 | [diff] [blame] | 166 | for (int j = 0; j < GLYPH_HEIGHT * font_scaling; j++) { |
| 167 | const uint8_t *src_row = |
| 168 | &glyph[j * GLYPH_BYTES_PER_ROW * font_scaling]; |
| 169 | for (int i = 0; i < GLYPH_WIDTH * font_scaling; i++) { |
| 170 | dst_pointer[dst_x + i + (dst_y + j) * pitch / 4] = |
| 171 | get_bit(src_row, i) ? front_color : back_color; |
Stéphane Marchesin | ae37e6c | 2014-08-08 18:19:40 -0700 | [diff] [blame] | 172 | } |
Haixia Shi | 95d680e | 2015-04-27 20:29:17 -0700 | [diff] [blame] | 173 | } |
Stéphane Marchesin | ae37e6c | 2014-08-08 18:19:40 -0700 | [diff] [blame] | 174 | } |