Update stats in expected walls.srv output
[survex.git] / src / glbitmapfont.cc
blob0eda82a7bb74f65c726a4906025d5db30528c6db
1 //
2 // glbitmapfont.cc
3 //
4 // Draw text using glBitmap.
5 //
6 // Copyright (C) 2011-2022 Olly Betts
7 //
8 // This program is free software; you can redistribute it and/or modify
9 // it under the terms of the GNU General Public License as published by
10 // the Free Software Foundation; either version 2 of the License, or
11 // (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU General Public License for more details.
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 #include <config.h>
25 #include "glbitmapfont.h"
27 #include "aventypes.h"
28 #include "gllogerror.h"
29 #include "useful.h"
30 #include "wx.h"
32 #include <cerrno>
33 #include <cstdint>
34 #ifdef HAVE_MMAP
35 #include <sys/mman.h>
36 #else
37 #include <unistd.h>
38 #endif
40 using namespace std;
42 #include "../lib/preload_font.h"
44 BitmapFont::~BitmapFont() {
45 if (!gllist_base) return;
46 glDeleteLists(gllist_base, BITMAPFONT_MAX_CHAR);
47 CHECK_GL_ERROR("BitmapFont::~BitmapFont", "glDeleteLists");
50 static uint16_t* tab_2x = NULL;
51 static uint16_t* fontdata_2x = NULL;
52 static uint16_t* fontdata_2x_extra = NULL;
54 bool
55 BitmapFont::load(const wxString & font_file_, bool double_size)
57 font_file = font_file_;
59 font_size = double_size ? 32 : 16;
61 if (!gllist_base) {
62 gllist_base = glGenLists(BITMAPFONT_MAX_CHAR);
63 CHECK_GL_ERROR("BitmapFont::load", "glGenLists");
66 if (double_size && !tab_2x) {
67 tab_2x = new uint16_t[256];
68 for (unsigned i = 0; i < 256; ++i) {
69 unsigned v = (i & 1) << 8 |
70 (i & 2) << 9 |
71 (i & 4) << 10 |
72 (i & 8) << 11 |
73 (i & 16) >> 4 |
74 (i & 32) >> 3 |
75 (i & 64) >> 2 |
76 (i & 128) >> 1;
77 tab_2x[i] = 3 * v;
79 fontdata_2x = new uint16_t[sizeof(fontdata_preloaded) * 2];
81 const unsigned char * p = fontdata_preloaded;
82 const unsigned char * end = p + sizeof(fontdata_preloaded);
83 uint16_t* out = fontdata_2x;
84 for (int ch = 0; ch < BITMAPFONT_MAX_CHAR; ++ch) {
85 glNewList(gllist_base + ch, GL_COMPILE);
86 CHECK_GL_ERROR("BitmapFont::load", "glNewList");
87 if (p == end) {
88 return false;
90 unsigned int byte_width = *p++;
92 char_width[ch] = (byte_width & 0x0f) + 2;
93 byte_width >>= 6;
95 int start = 0;
96 unsigned int n = 0;
97 if (byte_width) {
98 if (p == end) {
99 return false;
101 unsigned int start_and_n = *p++;
102 start = start_and_n >> 4;
103 n = (start_and_n & 15) + 1;
105 if (unsigned(end - p) < n * byte_width) {
106 return false;
110 // Note that even if there's nothing to display, we still call
111 // glBitmap() to advance the raster position.
112 if (double_size) {
113 const GLubyte* q = reinterpret_cast<const GLubyte*>(out);
114 for (unsigned i = 0; i != n; ++i) {
115 for (unsigned j = 0; j != byte_width; ++j) {
116 *out++ = tab_2x[*p++];
118 memcpy(out, out - byte_width, byte_width * sizeof(uint16_t));
119 out += byte_width;
122 char_width[ch] *= 2;
123 glBitmap(2 * 8 * byte_width, 2 * n, 0, -2 * start, char_width[ch], 0, q);
124 } else {
125 glBitmap(8 * byte_width, n, 0, -start, char_width[ch], 0, p);
126 p += n * byte_width;
128 CHECK_GL_ERROR("BitmapFont::load", "glBitmap");
129 glEndList();
130 CHECK_GL_ERROR("BitmapFont::load", "glEndList");
133 return true;
136 inline void call_lists(GLsizei n, const GLvoid * lists)
138 #if SIZEOF_WXCHAR == 1
139 glCallLists(n, GL_UNSIGNED_BYTE, lists);
140 #elif SIZEOF_WXCHAR == 2
141 glCallLists(n, GL_UNSIGNED_SHORT, lists);
142 #elif SIZEOF_WXCHAR == 4
143 glCallLists(n, GL_UNSIGNED_INT, lists);
144 #else
145 # error "sizeof(wxChar) not 1, 2 or 4"
146 #endif
149 void
150 BitmapFont::init_extra_chars() const
152 bool double_size = (tab_2x != NULL);
153 int fd = wxOpen(font_file,
154 #ifdef __WXMSW__
155 _O_RDONLY|_O_SEQUENTIAL|_O_BINARY,
156 #else
157 O_RDONLY,
158 #endif
161 int data_len = 0;
162 unsigned char * data = NULL;
163 if (fd >= 0) {
164 struct stat sb;
165 if (fstat(fd, &sb) >= 0) {
166 data_len = sb.st_size;
170 #if HAVE_MMAP
171 if (data_len) {
172 void * p = mmap(NULL, data_len, PROT_READ, MAP_SHARED, fd, 0);
173 if (p == MAP_FAILED) {
174 data_len = 0;
175 } else {
176 extra_data = data = static_cast<unsigned char*>(p);
179 #else
180 data = new unsigned char[data_len];
181 extra_data = data;
182 if (data_len) {
183 size_t c = data_len;
184 unsigned char * p = data;
185 while (c) {
186 ssize_t n = read(fd, p, c);
187 if (n <= 0) {
188 if (errno == EINTR) continue;
189 data_len = 0;
190 // FIXME: do something better. wxGetApp().ReportError(m);
191 // We have this message available: Error in format of font file ā€œ%sā€
192 // fprintf(stderr, "Couldn't load extended font.\n");
193 break;
195 p += n;
196 c -= n;
199 #endif
201 if (fd >= 0)
202 close(fd);
204 extra_chars = new int[0x10000 - BITMAPFONT_MAX_CHAR];
206 int ch = 0;
207 if (double_size) {
208 // The bitmap data will increase 4*, while the metadata will increase
209 // 2*, so we can use 4* the total size as an overestimate.
210 fontdata_2x_extra = new uint16_t[data_len * 2];
211 uint16_t* out = fontdata_2x_extra;
212 int data_ch = 0;
213 while (data_ch < data_len) {
214 extra_chars[ch++] = out - fontdata_2x_extra;
215 unsigned int b = data[data_ch++];
216 *out++ = b;
217 unsigned int byte_width = b >> 6;
218 if (byte_width) {
219 unsigned int start_and_n = data[data_ch++];
220 *out++ = start_and_n;
221 unsigned int n = (start_and_n & 15) + 1;
222 for (unsigned i = 0; i != n; ++i) {
223 for (unsigned j = 0; j != byte_width; ++j) {
224 *out++ = tab_2x[data[data_ch++]];
225 if (out - fontdata_2x_extra >= data_len * 2) abort();
227 memcpy(out, out - byte_width, byte_width * sizeof(uint16_t));
228 out += byte_width;
229 if (out - fontdata_2x_extra >= data_len * 2) abort();
233 } else {
234 int data_ch = 0;
235 while (data_ch < data_len) {
236 extra_chars[ch++] = data_ch;
237 unsigned int byte_width = data[data_ch++];
238 byte_width >>= 6;
240 if (byte_width) {
241 unsigned int start_and_n = data[data_ch];
242 int n = (start_and_n & 15) + 1;
243 data_ch += n * byte_width + 1;
248 while (ch < 0x10000 - BITMAPFONT_MAX_CHAR) {
249 extra_chars[ch++] = -1;
254 BitmapFont::glyph_width(wxChar ch) const
256 #if SIZEOF_WXCHAR > 2
257 if (ch >= 0x10000) return 0;
258 #endif
259 if (!extra_chars)
260 init_extra_chars();
262 int char_idx = extra_chars[ch - BITMAPFONT_MAX_CHAR];
263 if (char_idx < 0) {
264 return tab_2x ? 16 : 8;
266 if (tab_2x) {
267 unsigned int byte_width = fontdata_2x_extra[char_idx];
268 return ((byte_width & 0x0f) + 2) << 1;
269 } else {
270 unsigned int byte_width = extra_data[char_idx];
271 return (byte_width & 0x0f) + 2;
275 void
276 BitmapFont::write_glyph(wxChar ch) const
278 #if SIZEOF_WXCHAR > 2
279 if (ch >= 0x10000) return;
280 #endif
281 if (!extra_chars)
282 init_extra_chars();
284 unsigned int byte_width = 0;
285 int start = 0;
286 int n = 0;
287 int width = 8;
289 int char_idx = extra_chars[ch - BITMAPFONT_MAX_CHAR];
290 const unsigned char* data = NULL;
291 if (char_idx >= 0) {
292 if (tab_2x) {
293 const uint16_t* p = fontdata_2x_extra + char_idx;
294 byte_width = *p++;
295 width = (byte_width & 0x0f) + 2;
296 byte_width >>= 6;
298 if (byte_width) {
299 byte_width <<= 1;
300 unsigned int start_and_n = *p++;
301 start = (start_and_n >> 4) << 1;
302 n = ((start_and_n & 15) + 1) << 1;
304 width <<= 1;
306 data = reinterpret_cast<const unsigned char*>(p);
307 } else {
308 const unsigned char * p = extra_data + char_idx;
309 byte_width = *p++;
310 width = (byte_width & 0x0f) + 2;
311 byte_width >>= 6;
313 if (byte_width) {
314 unsigned int start_and_n = *p++;
315 start = start_and_n >> 4;
316 n = (start_and_n & 15) + 1;
318 data = p;
322 // Even if there's nothing to display, we want to advance the
323 // raster position.
324 glBitmap(8 * byte_width, n, 0, -start, width, 0, data);
325 CHECK_GL_ERROR("BitmapFont::write_glyph", "glBitmap");
328 void
329 BitmapFont::write_string(const wxChar *s, size_t len) const
331 if (!gllist_base) return;
332 glListBase(gllist_base);
333 #if SIZEOF_WXCHAR == 1
334 call_lists(len, s);
335 #elif SIZEOF_WXCHAR == 2 || SIZEOF_WXCHAR == 4
336 while (len) {
337 size_t n;
338 for (n = 0; n < len; ++n)
339 if (int(s[n]) >= BITMAPFONT_MAX_CHAR)
340 break;
341 call_lists(n, s);
342 s += n;
343 len -= n;
344 while (len && int(*s) >= BITMAPFONT_MAX_CHAR) {
345 write_glyph(*s);
346 ++s;
347 --len;
350 #else
351 # error "sizeof(wxChar) not 1, 2 or 4"
352 #endif