1#!/usr/bin/env vpython3
2#
3# [VPYTHON:BEGIN]
4# wheel: <
5#   name: "infra/python/wheels/freetype-py/${vpython_platform}"
6#   version: "version:2.1.0.post1"
7# >
8# [VPYTHON:END]
9
10# Copyright 2019 The ANGLE Project Authors. All rights reserved.
11# Use of this source code is governed by a BSD-style license that can be
12# found in the LICENSE file.
13#
14# gen_vk_overlay_fonts.py:
15#  Code generation for overlay fonts.  Should be run if the font file under overlay/ is changed,
16#  or the font sizes declared in this file are modified.  The font is assumed to be monospace.
17#  The output will contain ASCII characters in order from ' ' to '~'.  The output will be images
18#  with 3 rows of 32 characters each.
19#  NOTE: don't run this script directly. Run scripts/run_code_generation.py.
20
21import sys
22
23if len(sys.argv) < 2:
24    from freetype import *
25
26out_file_cpp = 'Overlay_font_autogen.cpp'
27out_file_h = 'Overlay_font_autogen.h'
28font_file = 'overlay/DejaVuSansMono-Bold.ttf'
29
30template_out_file_h = u"""// GENERATED FILE - DO NOT EDIT.
31// Generated by {script_name} using {font_file}.
32//
33// Copyright 2019 The ANGLE Project Authors. All rights reserved.
34// Use of this source code is governed by a BSD-style license that can be
35// found in the LICENSE file.
36//
37// {out_file_name}:
38//   Autogenerated overlay font data.
39
40#include "libANGLE/Overlay.h"
41
42namespace gl
43{{
44namespace overlay
45{{
46constexpr int kFontCount = {font_count};
47constexpr int kFontGlyphWidths[kFontCount] = {{ {font_glyph_widths} }};
48constexpr int kFontGlyphHeights[kFontCount] = {{ {font_glyph_heights} }};
49constexpr int kFontCharactersPerRow = 32;
50constexpr int kFontCharactersPerCol = 3;
51constexpr int kFontCharacters = kFontCharactersPerRow * kFontCharactersPerCol;
52constexpr int kFontImageWidth = {max_font_width} * kFontCharactersPerRow;
53constexpr int kFontImageHeight = {max_font_height} * kFontCharactersPerCol;
54{font_layers}
55}}  // namespace overlay
56}}  // namespace gl
57
58"""
59
60template_out_file_cpp = u"""// GENERATED FILE - DO NOT EDIT.
61// Generated by {script_name} using images from {font_file}.
62//
63// Copyright 2019 The ANGLE Project Authors. All rights reserved.
64// Use of this source code is governed by a BSD-style license that can be
65// found in the LICENSE file.
66//
67// {out_file_name}:
68//   Autogenerated overlay font data.
69
70#include "libANGLE/Overlay.h"
71#include "libANGLE/Overlay_font_autogen.h"
72
73#include <numeric>
74
75namespace gl
76{{
77using namespace overlay;
78
79// Save binary size if the font images are never to be used.
80#if ANGLE_ENABLE_OVERLAY
81namespace
82{{
83constexpr int kFontWidths[kFontCount] = {{ {font_layer_widths} }};
84constexpr int kFontHeights[kFontCount] = {{ {font_layer_heights} }};
85
86{font_data}
87
88// Returns a bit with the value of the pixel.
89template<int kFontWidth, int kFontHeight>
90uint32_t GetFontLayerPixel(const uint32_t fontImage[kFontHeight][kFontWidth / 32], int x, int y)
91{{
92    ASSERT(x >= 0 && x < kFontWidth && y >= 0 && y < kFontHeight);
93    return fontImage[y][x / 32] >> (x % 32) & 1;
94}}
95
96inline uint32_t GetFontPixel(int layer, int x, int y)
97{{
98    switch (layer)
99    {{
100    {get_font_layer_pixel}
101    default:
102        UNREACHABLE();
103        return 0;
104    }}
105}}
106
107}}  // anonymous namespace
108
109void OverlayState::initFontData(uint8_t *fontData) const
110{{
111    constexpr int kFontDataLayerSize = kFontImageWidth * kFontImageHeight;
112
113    // Unpack the font bitmap into R8_UNORM format.  Border pixels are given a 0.5 value for better
114    // font visibility.
115    for (int layer = 0; layer < kFontCount; ++layer)
116    {{
117        memset(fontData, 0, kFontDataLayerSize);
118        for (int y = 0; y < kFontHeights[layer]; ++y)
119        {{
120            for (int x = 0; x < kFontWidths[layer]; ++x)
121            {{
122                uint32_t src     = GetFontPixel(layer, x, y);
123                uint8_t dstValue = src ? 255 : 0;
124                fontData[y * kFontImageWidth + x] = dstValue;
125            }}
126        }}
127        fontData += kFontDataLayerSize;
128    }}
129}}
130#else
131void OverlayState::initFontData(uint8_t *fontData) const
132{{
133    memset(fontData, 0, kFontCount * kFontImageWidth * kFontImageHeight * sizeof(*fontData));
134}}
135#endif
136}}  // namespace gl
137"""
138
139template_get_font_layer_pixel = u"""case {layer}:
140    return GetFontLayerPixel<kFontWidths[{layer}], kFontHeights[{layer}]>({font_image}, x, y);
141"""
142
143
144def main():
145    if len(sys.argv) == 2 and sys.argv[1] == 'inputs':
146        # disabled because of issues on Windows. http://anglebug.com/3892
147        # print(font_file)
148        return
149    if len(sys.argv) == 2 and sys.argv[1] == 'outputs':
150        print(','.join([out_file_cpp, out_file_h]))
151        return
152
153    font_defs = [('large', 36), ('medium', 23), ('small', 14)]
154    chars = ' !"#$%&\'()*+,-./0123456789:;<=>?' + \
155            '@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_' + \
156            '`abcdefghijklmnopqrstuvwxyz{|}~ '
157    output_rows = 3
158    output_cols = 32
159    assert (len(chars) == output_rows * output_cols)
160
161    font_glyph_widths = []
162    font_glyph_heights = []
163    font_layers = []
164    font_data = []
165    get_font_layer_pixel = []
166    current_font_layer = 0
167
168    # Load the font file.
169    face = Face(font_file)
170    assert (face.is_fixed_width)
171
172    for font_name, font_size in font_defs:
173
174        # Since the font is fixed width, we can retrieve its size right away.
175        face.set_char_size(font_size << 6)
176        glyph_width = face.size.max_advance >> 6
177        glyph_ascender = face.size.ascender >> 6
178        glyph_descender = face.size.descender >> 6
179        glyph_height = glyph_ascender - glyph_descender
180
181        font_tag = font_name.capitalize()
182        font_layer = str(current_font_layer)
183        font_layer_symbol = 'kFontLayer' + font_tag
184        font_array_name = 'kFontImage' + font_tag
185        font_width = 'kFontWidths[' + font_layer_symbol + ']'
186        font_height = 'kFontHeights[' + font_layer_symbol + ']'
187
188        # Font pixels are packed in 32-bit values.
189        font_array_width = output_cols * glyph_width // 32
190        font_array_height = output_rows * glyph_height
191        font_array = [[0] * font_array_width for i in range(font_array_height)]
192
193        for charIndex in range(len(chars)):
194            char = chars[charIndex]
195            base_x = (charIndex % output_cols) * glyph_width
196            base_y = (charIndex // output_cols) * glyph_height
197
198            # Render the character.
199            face.load_char(char)
200            bitmap = face.glyph.bitmap
201            left = face.glyph.bitmap_left
202            top = face.glyph.bitmap_top
203            width = bitmap.width
204            rows = bitmap.rows
205            pitch = bitmap.pitch
206
207            offset_x = left
208            offset_y = glyph_height - (top - glyph_descender)
209
210            # '#' in the smallest font generates a larger glyph than the "fixed" font width.
211            if offset_x + width > glyph_width:
212                offset_x = glyph_width - width
213                if offset_x < 0:
214                    width += offset_x
215                    offset_x = 0
216
217            base_x += offset_x
218            base_y += offset_y
219
220            assert (offset_x + width <= glyph_width)
221            assert (offset_y + rows <= glyph_height)
222
223            # Write the character bitmap in the font image.
224            for y in range(rows):
225                for x in range(width):
226                    pixel_value = bitmap.buffer[y * pitch + x]
227                    output_bit = 1 if pixel_value >= 122 else 0
228
229                    font_array_row = base_y + y
230                    font_array_col = (base_x + x) // 32
231                    font_array_bit = (base_x + x) % 32
232                    font_array[font_array_row][font_array_col] |= output_bit << font_array_bit
233
234        # Output the image to a C array.
235        data = 'constexpr uint32_t ' + font_array_name + '[' + font_height + '][' + font_width + '/32] = {\n'
236        for y in range(font_array_height):
237            data += '{'
238            for x in range(font_array_width):
239                data += '0x{:08X}, '.format(font_array[y][x])
240            data += '},\n'
241        data += '};\n'
242
243        font_glyph_widths.append(glyph_width)
244        font_glyph_heights.append(glyph_height)
245        font_layers.append('constexpr int ' + font_layer_symbol + ' = ' + font_layer + ';')
246        font_data.append(data)
247        get_font_layer_pixel.append(
248            template_get_font_layer_pixel.format(
249                layer=font_layer_symbol, font_image=font_array_name))
250        current_font_layer += 1
251
252    with open(out_file_h, 'w') as outfile:
253        outfile.write(
254            template_out_file_h.format(
255                script_name=__file__,
256                font_file=font_file,
257                out_file_name=out_file_h,
258                font_count=len(font_data),
259                font_glyph_widths=','.join(map(str, font_glyph_widths)),
260                font_glyph_heights=','.join(map(str, font_glyph_heights)),
261                max_font_width=max(font_glyph_widths),
262                max_font_height=max(font_glyph_heights),
263                font_layers='\n'.join(font_layers)))
264        outfile.close()
265
266    font_layer_widths = [
267        'kFontGlyphWidths[' + str(layer) + '] * kFontCharactersPerRow'
268        for layer in range(len(font_data))
269    ]
270    font_layer_heights = [
271        'kFontGlyphHeights[' + str(layer) + '] * kFontCharactersPerCol'
272        for layer in range(len(font_data))
273    ]
274
275    with open(out_file_cpp, 'w') as outfile:
276        outfile.write(
277            template_out_file_cpp.format(
278                script_name=__file__,
279                font_file=font_file,
280                out_file_name=out_file_cpp,
281                font_layer_widths=','.join(font_layer_widths),
282                font_layer_heights=','.join(font_layer_heights),
283                font_data='\n'.join(font_data),
284                get_font_layer_pixel=''.join(get_font_layer_pixel)))
285        outfile.close()
286
287
288if __name__ == '__main__':
289    sys.exit(main())
290