source: git/src/glbitmapfont.cc

walls-data-hanging-as-warning
Last change on this file was 4c83f84, checked in by Olly Betts <olly@…>, 10 days ago

Don't check HAVE_CONFIG_H in most cases

This check is only useful for img.c, which is intended to be usable
outside of Survex (and had fallbacks for functions which may not be
available which will get used if built in a non-autotools project).
For all the other source files it's just useless boilerplate.

  • Property mode set to 100644
File size: 8.4 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#include <config.h>
24
25#include "glbitmapfont.h"
26
27#include "aventypes.h"
28#include "gllogerror.h"
29#include "useful.h"
30#include "wx.h"
31
32#include <cerrno>
33#include <cstdint>
34#ifdef HAVE_MMAP
35#include <sys/mman.h>
36#else
37#include <unistd.h>
38#endif
39
40using namespace std;
41
42#include "../lib/preload_font.h"
43
44BitmapFont::~BitmapFont() {
45    if (!gllist_base) return;
46    glDeleteLists(gllist_base, BITMAPFONT_MAX_CHAR);
47    CHECK_GL_ERROR("BitmapFont::~BitmapFont", "glDeleteLists");
48}
49
50static uint16_t* tab_2x = NULL;
51static uint16_t* fontdata_2x = NULL;
52static uint16_t* fontdata_2x_extra = NULL;
53
54bool
55BitmapFont::load(const wxString & font_file_, bool double_size)
56{
57    font_file = font_file_;
58
59    font_size = double_size ? 32 : 16;
60
61    if (!gllist_base) {
62        gllist_base = glGenLists(BITMAPFONT_MAX_CHAR);
63        CHECK_GL_ERROR("BitmapFont::load", "glGenLists");
64    }
65
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;
78        }
79        fontdata_2x = new uint16_t[sizeof(fontdata_preloaded) * 2];
80    }
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;
89        }
90        unsigned int byte_width = *p++;
91
92        char_width[ch] = (byte_width & 0x0f) + 2;
93        byte_width >>= 6;
94
95        int start = 0;
96        unsigned int n = 0;
97        if (byte_width) {
98            if (p == end) {
99                return false;
100            }
101            unsigned int start_and_n = *p++;
102            start = start_and_n >> 4;
103            n = (start_and_n & 15) + 1;
104
105            if (unsigned(end - p) < n * byte_width) {
106                return false;
107            }
108        }
109
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++];
117                }
118                memcpy(out, out - byte_width, byte_width * sizeof(uint16_t));
119                out += byte_width;
120            }
121
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;
127        }
128        CHECK_GL_ERROR("BitmapFont::load", "glBitmap");
129        glEndList();
130        CHECK_GL_ERROR("BitmapFont::load", "glEndList");
131    }
132
133    return true;
134}
135
136inline void call_lists(GLsizei n, const GLvoid * lists)
137{
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
147}
148
149void
150BitmapFont::init_extra_chars() const
151{
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
159            0);
160
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;
167        }
168    }
169
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);
177        }
178    }
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;
194            }
195            p += n;
196            c -= n;
197        }
198    }
199#endif
200
201    if (fd >= 0)
202        close(fd);
203
204    extra_chars = new int[0x10000 - BITMAPFONT_MAX_CHAR];
205
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();
226                    }
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();
230                }
231            }
232        }
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;
239
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;
244            }
245        }
246    }
247
248    while (ch < 0x10000 - BITMAPFONT_MAX_CHAR) {
249        extra_chars[ch++] = -1;
250    }
251}
252
253int
254BitmapFont::glyph_width(wxChar ch) const
255{
256#if SIZEOF_WXCHAR > 2
257    if (ch >= 0x10000) return 0;
258#endif
259    if (!extra_chars)
260        init_extra_chars();
261
262    int char_idx = extra_chars[ch - BITMAPFONT_MAX_CHAR];
263    if (char_idx < 0) {
264        return tab_2x ? 16 : 8;
265    }
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;
272    }
273}
274
275void
276BitmapFont::write_glyph(wxChar ch) const
277{
278#if SIZEOF_WXCHAR > 2
279    if (ch >= 0x10000) return;
280#endif
281    if (!extra_chars)
282        init_extra_chars();
283
284    unsigned int byte_width = 0;
285    int start = 0;
286    int n = 0;
287    int width = 8;
288
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;
297
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;
303            }
304            width <<= 1;
305
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;
312
313            if (byte_width) {
314                unsigned int start_and_n = *p++;
315                start = start_and_n >> 4;
316                n = (start_and_n & 15) + 1;
317            }
318            data = p;
319        }
320    }
321
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");
326}
327
328void
329BitmapFont::write_string(const wxChar *s, size_t len) const
330{
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;
348        }
349    }
350#else
351# error "sizeof(wxChar) not 1, 2 or 4"
352#endif
353}
Note: See TracBrowser for help on using the repository browser.