source: git/src/glbitmapfont.cc @ 04078a7

faster-cavernlogwalls-datawalls-data-hanging-as-warning
Last change on this file since 04078a7 was 7eac11e, checked in by Olly Betts <olly@…>, 2 years ago

Scale indicators and fonts on HiDPI displays

  • Property mode set to 100644
File size: 8.5 KB
Line 
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.
12//
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.
17//
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
21//
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#include "glbitmapfont.h"
28
29#include "aventypes.h"
30#include "gllogerror.h"
31#include "useful.h"
32#include "wx.h"
33
34#include <cerrno>
35#include <cstdint>
36#ifdef HAVE_MMAP
37#include <sys/mman.h>
38#else
39#include <unistd.h>
40#endif
41
42using namespace std;
43
44#include "../lib/preload_font.h"
45
46BitmapFont::~BitmapFont() {
47    if (!gllist_base) return;
48    glDeleteLists(gllist_base, BITMAPFONT_MAX_CHAR);
49    CHECK_GL_ERROR("BitmapFont::~BitmapFont", "glDeleteLists");
50}
51
52static uint16_t* tab_2x = NULL;
53static uint16_t* fontdata_2x = NULL;
54static uint16_t* fontdata_2x_extra = NULL;
55
56bool
57BitmapFont::load(const wxString & font_file_, bool double_size)
58{
59    font_file = font_file_;
60
61    font_size = double_size ? 32 : 16;
62
63    if (!gllist_base) {
64        gllist_base = glGenLists(BITMAPFONT_MAX_CHAR);
65        CHECK_GL_ERROR("BitmapFont::load", "glGenLists");
66    }
67
68    if (double_size && !tab_2x) {
69        tab_2x = new uint16_t[256];
70        for (unsigned i = 0; i < 256; ++i) {
71            unsigned v = (i & 1) << 8 |
72                         (i & 2) << 9 |
73                         (i & 4) << 10 |
74                         (i & 8) << 11 |
75                         (i & 16) >> 4 |
76                         (i & 32) >> 3 |
77                         (i & 64) >> 2 |
78                         (i & 128) >> 1;
79            tab_2x[i] = 3 * v;
80        }
81        fontdata_2x = new uint16_t[sizeof(fontdata_preloaded) * 2];
82    }
83    const unsigned char * p = fontdata_preloaded;
84    const unsigned char * end = p + sizeof(fontdata_preloaded);
85    uint16_t* out = fontdata_2x;
86    for (int ch = 0; ch < BITMAPFONT_MAX_CHAR; ++ch) {
87        glNewList(gllist_base + ch, GL_COMPILE);
88        CHECK_GL_ERROR("BitmapFont::load", "glNewList");
89        if (p == end) {
90            return false;
91        }
92        unsigned int byte_width = *p++;
93
94        char_width[ch] = (byte_width & 0x0f) + 2;
95        byte_width >>= 6;
96
97        int start = 0;
98        unsigned int n = 0;
99        if (byte_width) {
100            if (p == end) {
101                return false;
102            }
103            unsigned int start_and_n = *p++;
104            start = start_and_n >> 4;
105            n = (start_and_n & 15) + 1;
106
107            if (unsigned(end - p) < n * byte_width) {
108                return false;
109            }
110        }
111
112        // Note that even if there's nothing to display, we still call
113        // glBitmap() to advance the raster position.
114        if (double_size) {
115            const GLubyte* q = reinterpret_cast<const GLubyte*>(out);
116            for (unsigned i = 0; i != n; ++i) {
117                for (unsigned j = 0; j != byte_width; ++j) {
118                    *out++ = tab_2x[*p++];
119                }
120                memcpy(out, out - byte_width, byte_width * sizeof(uint16_t));
121                out += byte_width;
122            }
123
124            char_width[ch] *= 2;
125            glBitmap(2 * 8 * byte_width, 2 * n, 0, -2 * start, char_width[ch], 0, q);
126        } else {
127            glBitmap(8 * byte_width, n, 0, -start, char_width[ch], 0, p);
128            p += n * byte_width;
129        }
130        CHECK_GL_ERROR("BitmapFont::load", "glBitmap");
131        glEndList();
132        CHECK_GL_ERROR("BitmapFont::load", "glEndList");
133    }
134
135    return true;
136}
137
138inline void call_lists(GLsizei n, const GLvoid * lists)
139{
140#if SIZEOF_WXCHAR == 1
141    glCallLists(n, GL_UNSIGNED_BYTE, lists);
142#elif SIZEOF_WXCHAR == 2
143    glCallLists(n, GL_UNSIGNED_SHORT, lists);
144#elif SIZEOF_WXCHAR == 4
145    glCallLists(n, GL_UNSIGNED_INT, lists);
146#else
147# error "sizeof(wxChar) not 1, 2 or 4"
148#endif
149}
150
151void
152BitmapFont::init_extra_chars() const
153{
154    bool double_size = (tab_2x != NULL);
155    int fd = wxOpen(font_file,
156#ifdef __WXMSW__
157            _O_RDONLY|_O_SEQUENTIAL|_O_BINARY,
158#else
159            O_RDONLY,
160#endif
161            0);
162
163    int data_len = 0;
164    unsigned char * data = NULL;
165    if (fd >= 0) {
166        struct stat sb;
167        if (fstat(fd, &sb) >= 0) {
168            data_len = sb.st_size;
169        }
170    }
171
172#if HAVE_MMAP
173    if (data_len) {
174        void * p = mmap(NULL, data_len, PROT_READ, MAP_SHARED, fd, 0);
175        if (p == MAP_FAILED) {
176            data_len = 0;
177        } else {
178            extra_data = data = static_cast<unsigned char*>(p);
179        }
180    }
181#else
182    data = new unsigned char[data_len];
183    extra_data = data;
184    if (data_len) {
185        size_t c = data_len;
186        unsigned char * p = data;
187        while (c) {
188            ssize_t n = read(fd, p, c);
189            if (n <= 0) {
190                if (errno == EINTR) continue;
191                data_len = 0;
192                // FIXME: do something better.  wxGetApp().ReportError(m);
193                // We have this message available: Error in format of font file “%s”
194                // fprintf(stderr, "Couldn't load extended font.\n");
195                break;
196            }
197            p += n;
198            c -= n;
199        }
200    }
201#endif
202
203    if (fd >= 0)
204        close(fd);
205
206    extra_chars = new int[0x10000 - BITMAPFONT_MAX_CHAR];
207
208    int ch = 0;
209    if (double_size) {
210        // The bitmap data will increase 4*, while the metadata will increase
211        // 2*, so we can use 4* the total size as an overestimate.
212        fontdata_2x_extra = new uint16_t[data_len * 2];
213        uint16_t* out = fontdata_2x_extra;
214        int data_ch = 0;
215        while (data_ch < data_len) {
216            extra_chars[ch++] = out - fontdata_2x_extra;
217            unsigned int b = data[data_ch++];
218            *out++ = b;
219            unsigned int byte_width = b >> 6;
220            if (byte_width) {
221                unsigned int start_and_n = data[data_ch++];
222                *out++ = start_and_n;
223                unsigned int n = (start_and_n & 15) + 1;
224                for (unsigned i = 0; i != n; ++i) {
225                    for (unsigned j = 0; j != byte_width; ++j) {
226                        *out++ = tab_2x[data[data_ch++]];
227                        if (out - fontdata_2x_extra >= data_len * 2) abort();
228                    }
229                    memcpy(out, out - byte_width, byte_width * sizeof(uint16_t));
230                    out += byte_width;
231                    if (out - fontdata_2x_extra >= data_len * 2) abort();
232                }
233            }
234        }
235    } else {
236        int data_ch = 0;
237        while (data_ch < data_len) {
238            extra_chars[ch++] = data_ch;
239            unsigned int byte_width = data[data_ch++];
240            byte_width >>= 6;
241
242            if (byte_width) {
243                unsigned int start_and_n = data[data_ch];
244                int n = (start_and_n & 15) + 1;
245                data_ch += n * byte_width + 1;
246            }
247        }
248    }
249
250    while (ch < 0x10000 - BITMAPFONT_MAX_CHAR) {
251        extra_chars[ch++] = -1;
252    }
253}
254
255int
256BitmapFont::glyph_width(wxChar ch) const
257{
258#if SIZEOF_WXCHAR > 2
259    if (ch >= 0x10000) return 0;
260#endif
261    if (!extra_chars)
262        init_extra_chars();
263
264    int char_idx = extra_chars[ch - BITMAPFONT_MAX_CHAR];
265    if (char_idx < 0) {
266        return tab_2x ? 16 : 8;
267    }
268    if (tab_2x) {
269        unsigned int byte_width = fontdata_2x_extra[char_idx];
270        return ((byte_width & 0x0f) + 2) << 1;
271    } else {
272        unsigned int byte_width = extra_data[char_idx];
273        return (byte_width & 0x0f) + 2;
274    }
275}
276
277void
278BitmapFont::write_glyph(wxChar ch) const
279{
280#if SIZEOF_WXCHAR > 2
281    if (ch >= 0x10000) return;
282#endif
283    if (!extra_chars)
284        init_extra_chars();
285
286    unsigned int byte_width = 0;
287    int start = 0;
288    int n = 0;
289    int width = 8;
290
291    int char_idx = extra_chars[ch - BITMAPFONT_MAX_CHAR];
292    const unsigned char* data = NULL;
293    if (char_idx >= 0) {
294        if (tab_2x) {
295            const uint16_t* p = fontdata_2x_extra + char_idx;
296            byte_width = *p++;
297            width = (byte_width & 0x0f) + 2;
298            byte_width >>= 6;
299
300            if (byte_width) {
301                byte_width <<= 1;
302                unsigned int start_and_n = *p++;
303                start = (start_and_n >> 4) << 1;
304                n = ((start_and_n & 15) + 1) << 1;
305            }
306            width <<= 1;
307
308            data = reinterpret_cast<const unsigned char*>(p);
309        } else {
310            const unsigned char * p = extra_data + char_idx;
311            byte_width = *p++;
312            width = (byte_width & 0x0f) + 2;
313            byte_width >>= 6;
314
315            if (byte_width) {
316                unsigned int start_and_n = *p++;
317                start = start_and_n >> 4;
318                n = (start_and_n & 15) + 1;
319            }
320            data = p;
321        }
322    }
323
324    // Even if there's nothing to display, we want to advance the
325    // raster position.
326    glBitmap(8 * byte_width, n, 0, -start, width, 0, data);
327    CHECK_GL_ERROR("BitmapFont::write_glyph", "glBitmap");
328}
329
330void
331BitmapFont::write_string(const wxChar *s, size_t len) const
332{
333    if (!gllist_base) return;
334    glListBase(gllist_base);
335#if SIZEOF_WXCHAR == 1
336    call_lists(len, s);
337#elif SIZEOF_WXCHAR == 2 || SIZEOF_WXCHAR == 4
338    while (len) {
339        size_t n;
340        for (n = 0; n < len; ++n)
341            if (int(s[n]) >= BITMAPFONT_MAX_CHAR)
342                break;
343        call_lists(n, s);
344        s += n;
345        len -= n;
346        while (len && int(*s) >= BITMAPFONT_MAX_CHAR) {
347            write_glyph(*s);
348            ++s;
349            --len;
350        }
351    }
352#else
353# error "sizeof(wxChar) not 1, 2 or 4"
354#endif
355}
Note: See TracBrowser for help on using the repository browser.