5 ******************************************************************************
6 * @file encode_fonts.py
7 * @author dRonin, http://dRonin.org/, Copyright (C) 2016
8 * @addtogroup dRonin Modules
10 * @addtogroup OnScreenDisplay Module
12 * @brief Font converter script for OSD
13 *****************************************************************************/
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 3 of the License, or
18 * (at your option) any later version.
20 * This program is distributed in the hope that it will be useful, but
21 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
22 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
25 * You should have received a copy of the GNU General Public License along
26 * with this program; if not, see <http://www.gnu.org/licenses/>
28 * Additional note on redistribution: The copyright and license notices above
29 * must be maintained in each individual source file that is a derivative work
30 * of this source file; otherwise redistribution is prohibited.
36 from string
import Template
37 from itertools
import chain, repeat
40 import matplotlib.pyplot
as plt
41 import matplotlib.image
as mpimg
43 file_header = Template(
"""
45 ******************************************************************************
47 * @author dRonin, http://dRonin.org/, Copyright (C) 2016
48 * @author The OpenPilot Team, http://www.openpilot.org, Copyright (C) 2012
49 * @author Thomas Oldbury Copyright (C) 2010
50 * @addtogroup dRonin Modules
52 * @addtogroup OnScreenDisplay Module
54 * @brief Fonts for OSD
55 *****************************************************************************/
57 * This program is free software; you can redistribute it and/or modify
58 * it under the terms of the GNU General Public License as published by
59 * the Free Software Foundation; either version 3 of the License, or
60 * (at your option) any later version.
62 * This program is distributed in the hope that it will be useful, but
63 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
64 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
67 * You should have received a copy of the GNU General Public License along
68 * with this program; if not, see <http://www.gnu.org/licenses/>
70 * Additional note on redistribution: The copyright and license notices above
71 * must be maintained in each individual source file that is a derivative work
72 * of this source file; otherwise redistribution is prohibited.
74 * NOTE: This file is generated by encode_fonts.py DO NOT EDIT!
75 * NOTE: Some fonts are CC 3.0 BY-SA and note GPL licensed. See FONT_LICENSE.txt
82 font_header_template = Template(
"""
86 #include <openpilot.h>
92 \tconst uint8_t* lookup;
93 \tconst uint16_t* data;
96 #define NUM_FONTS $NUM_FONTS
103 font_c_template = Template(
"""
104 static const struct FontEntry font_$name = {
107 \t.lookup = lookup_$name,
108 \t.data = (uint16_t*)data_$name
114 font_c_table = Template(
"""
115 const struct FontEntry* fonts[NUM_FONTS] = {$CONTENT};
120 "grouper(3, 'abcdefg', 'x') --> ('a','b','c'), ('d','e','f'), ('g','x','x')"
121 return zip(*[chain(iterable, repeat(padvalue, n-1))]*n)
125 img = img=mpimg.imread(fname)
126 height, width, depth = img.shape
127 font_height = int(height / 16)
128 font_width = int(width / 16)
129 if font_width * 16 != width
or font_height * 16 != height:
130 raise RuntimeError(
"width or height of image is not divisable by 16!")
131 gray = np.max(img[:, :, :3], axis=-1)
135 alpha = img[:, :, -1]
138 mask = np.logical_or(gray < 0.1, gray > 0.9)
142 lookup = np.zeros(256, dtype=np.uint8)
144 for ii
in range(start, last + 1):
145 row = int(ii / 16) * font_height
146 col = (ii % 16) * font_width
147 if np.sum(mask[row:row + font_height, col:col + font_width]) > 0:
151 print(
'Encoding %s: %d x %d pixels / character. Active characters: %d' % (fname, font_width, font_height, n_active))
153 out_height = n_active * font_height
155 byte_width_split = int(np.ceil(font_width / float(8)))
157 mask_buffer = np.zeros((out_height, byte_width_split), dtype=np.uint8)
158 level_buffer = np.zeros((out_height, byte_width_split), dtype=np.uint8)
160 byte_width = int(np.ceil(font_width / float(4)))
161 data_buffer = np.zeros((out_height, byte_width), dtype=np.uint8)
164 for ii, entry
in enumerate(lookup):
167 for row
in range(0, font_height):
168 row_in = int(ii / 16) * font_height + row
169 for col
in range(font_width):
170 col_in = (ii % 16) * font_width + col
171 if mask[row_in, col_in]:
173 mask_buffer[row_out, pos] |= 0x01 << (7 - (col % 8))
174 level_buffer[row_out, pos] |= ~bw[row_in, col_in] << (7 - (col % 8))
176 data_buffer[row_out, pos] |= (bw[row_in, col_in] << (7 - 2 * (col % 4))) | (0x01 << (6 - 2 * (col % 4)))
179 if data_buffer.shape[1] == 3:
180 data_buffer = np.c_[data_buffer, np.zeros((out_height, 1), dtype=np.uint8)]
182 data_split_buffer = np.c_[level_buffer, mask_buffer]
184 font = dict(width=font_width, height=font_height,
185 data=data_buffer, data_split=data_split_buffer, lookup=lookup)
190 """Format image as a C header"""
191 for buf_name, buf
in zip(buffer_names, buffers):
192 if len(font[buf].shape) == 1
or font[buf].shape[1] == 1:
194 elif font[buf].shape[1] == 2:
198 fid.write(
'const uint%d_t %s_%s[] = {\n' % (word_size, buf_name, name))
200 n_rows = np.ceil(len(font[buf].ravel()) / float(row_width))
201 for ii, row
in enumerate(
grouper(row_width, font[buf].ravel())):
202 row = [val
for val
in row
if val
is not None]
207 fid.write(
'0x%0.2X, ' % byte)
208 elif word_size == 16:
209 for pos
in range(int(len(row) / 2)):
210 fid.write(
'0x%0.2X%0.2X, ' % (row[2 * pos], row[2 * pos + 1]))
211 elif word_size == 32:
212 for pos
in range(int(len(row) / 4)):
213 value = (row[4 * pos], row[4 * pos + 1], row[4 * pos + 2], row[4 * pos + 3])
214 fid.write(
'0x%0.2X%0.2X%0.2X%0.2X, ' % value)
221 if __name__ ==
'__main__':
222 from optparse
import OptionParser
223 parser = OptionParser()
224 parser.add_option(
'-o',
'--out', dest=
'filename_out',
225 help=
'Output file', metavar=
'FILE')
226 parser.add_option(
'-d',
'--header', dest=
'filename_header_out',
227 help=
'Output header file', metavar=
'FILE')
228 parser.add_option(
'-s',
'--start', dest=
'start',
229 help=
'First ASCII character to encode',
230 type=
"int", default=0)
231 parser.add_option(
'-l',
'--last', dest=
'last',
232 help=
'Last ASCII character to encode',
233 type=
"int", default=255)
235 options, args = parser.parse_args()
238 names = [fname[:-4]
for fname
in in_files]
239 print(
'Input fonts: %s' % str(in_files))
241 for name,fname
in zip(names, in_files):
242 fonts[name] =
encode_font(fname, options.start, options.last)
245 with open(options.filename_header_out,
'w')
as fid:
247 for ii, name
in enumerate(names):
248 font_name_str +=
'#define %s %d\n' % (name.upper(), ii)
249 fid.write(file_header.substitute(FILENAME=op.basename(options.filename_header_out)))
250 fid.write(font_header_template.substitute(NUM_FONTS=n_fonts, names=font_name_str))
253 with open(options.filename_out,
'w')
as fid:
254 fid.write(file_header.substitute(FILENAME=op.basename(options.filename_header_out)))
255 fid.write(
'#include "fonts.h"\n')
256 fid.write(
'#if defined(PIOS_VIDEO_SPLITBUFFER)\n')
259 c_format_font(fid, name, font, buffer_names=(
'data',), buffers=(
'data_split',))
260 fid.write(
'#else /* defined(PIOS_VIDEO_SPLITBUFFER) */\n')
263 c_format_font(fid, name, font, buffer_names=(
'data',), buffers=(
'data',))
264 fid.write(
'#endif /* defined(PIOS_VIDEO_SPLITBUFFER) */\n')
267 c_format_font(fid, name, font, buffer_names=(
'lookup',), buffers=(
'lookup',))
271 fid.write(font_c_template.substitute(width=font[
'width'],
272 height=font[
'height'], name=name))
276 content +=
'&font_' + name +
', '
277 content = content[:-2]
278 fid.write(font_c_table.substitute(CONTENT=content))
static int print(char **out, const char *format, va_list args)