~ubuntu-branches/ubuntu/trusty/psychtoolbox-3/trusty-proposed

« back to all changes in this revision

Viewing changes to PsychSourceGL/Cohorts/FTGLTextRenderer/libptbdrawtext_ftgles.cpp

  • Committer: Package Import Robot
  • Author(s): Yaroslav Halchenko
  • Date: 2013-11-19 23:34:50 UTC
  • mfrom: (3.1.4 experimental)
  • Revision ID: package-import@ubuntu.com-20131119233450-f7nf92vb8qavjmk8
Tags: 3.0.11.20131017.dfsg1-3
Upload to unsable since fresh glew has arrived to sid!

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * libptbdrawtext_ftgles.cpp: Source of libptbdrawtext_ftgles dynamic shared library plugin
 
3
 * for Screen('DrawText') text rendering via external engine. This is the version for use
 
4
 * with OpenGL-ES 1.1 instead of desktop OpenGL.
 
5
 *
 
6
 * This works only on GNU/Linux.
 
7
 *
 
8
 * Features:
 
9
 *
 
10
 * - Texture mapped renderer, like on OS/X with ATSU Drawtext.
 
11
 * - Fast due to use of a texture atlas for fast glyph recycling.
 
12
 * - Good text layouting.
 
13
 * - Supports all Freetype-2 supported fonts, e.g., vectorgraphics TrueType fonts.
 
14
 * - Anti-Aliased drawing via Alpha-Blending.
 
15
 * - Full Unicode support.
 
16
 * - Text measuring support, e.g., bounding boxes.
 
17
 *
 
18
 * Requires:
 
19
 *
 
20
 * Modified FTGLES library forked from https://github.com/cdave1/ftgles, modified version
 
21
 * in https://github.com/kleinerm/ftgles in the linuxptb branch. The library is licensed
 
22
 * to us under a MIT style license.
 
23
 *
 
24
 * The library must be ./configured as follows to allow linking the plugin with the static lib:
 
25
 * ./configure --with-pic
 
26
 *
 
27
 * Requires the FTGL subfolder of that lib (ftgles/ftgles/src/FTGL) in our build directory, for
 
28
 * access to header definitions.
 
29
 *
 
30
 * Requires libfontconfig and libFreeType-2.
 
31
 *
 
32
 * Building the plugin for Linux:
 
33
 * Intel-64 Bit:
 
34
 * g++ -g -fPIC -I. -I/usr/include/ -I/usr/include/freetype2/ -I/usr/include/GLES -L/usr/lib libptbdrawtext_ftgles.cpp -lGLESv1_CM -lfontconfig -lfreetype -pie -shared -Wl,-Bsymbolic -Wl,-Bsymbolic-functions -Wl,--version-script=linuxexportlist.txt -o ../../../Psychtoolbox/PsychBasic/PsychPlugins/libptbdrawtext_ftgles64.so.1 libftgles_intel64.a
 
35
 *
 
36
 * ARM-32 Bit:
 
37
 * g++ -g -fPIC -I. -I/usr/include/ -I/usr/include/freetype2/ -I/usr/include/GLES -L/usr/lib libptbdrawtext_ftgles.cpp -lGLESv1_CM -lfontconfig -lfreetype -pie -shared -Wl,-Bsymbolic -Wl,-Bsymbolic-functions -Wl,--version-script=linuxexportlist.txt -o ../../../Psychtoolbox/PsychBasic/PsychPlugins/libptbdrawtext_ftgles_arm.so.1 libftgles_arm32.a
 
38
 *
 
39
 * libptbdrawtext_ftgles is copyright (c) 2013 by Mario Kleiner.
 
40
 * It is licensed to you as follows:
 
41
 *
 
42
 * Permission is hereby granted, free of charge, to any person obtaining
 
43
 * a copy of this software and associated documentation files (the
 
44
 * "Software"), to deal in the Software without restriction, including
 
45
 * without limitation the rights to use, copy, modify, merge, publish,
 
46
 * distribute, sublicense, and/or sell copies of the Software, and to
 
47
 * permit persons to whom the Software is furnished to do so, subject to
 
48
 * the following conditions:
 
49
 *
 
50
 * The above copyright notice and this permission notice shall be
 
51
 * included in all copies or substantial portions of the Software.
 
52
 *
 
53
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 
54
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 
55
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 
56
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 
57
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 
58
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 
59
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
60
 *
 
61
 */
 
62
 
 
63
// Standard libs for debug output and unicode character string handling:
 
64
#include <stdlib.h>
 
65
#include <stdio.h>
 
66
#include <string.h>
 
67
#include <wchar.h>
 
68
 
 
69
// Include GLFTES and OpenGL-ES stuff, as well as GL on GLES emulation glue:
 
70
#include <gl.h>
 
71
#include <FTGL/ftgles.h>
 
72
#include <FTGL/ftglesGlue.h>
 
73
 
 
74
// Include fontconfig stuff:
 
75
#include "fontconfig/fontconfig.h"
 
76
#include "fontconfig/fcfreetype.h"
 
77
 
 
78
 
 
79
// Maximum number of cacheable font settings, combined over all onscreen windows
 
80
// font family names, sizes, styles and anti-aliasing settings. E.g., a setting of 40 means
 
81
// you could quickly switch between up to 40 different combinations of font name, size,
 
82
// style and anti-aliasing setting without speed penalty, due to caching. This is a tradeoff
 
83
// between memory consumption and switchability, so choose wisely.
 
84
// If usercode requests more than those number, caching for the given context (window)
 
85
// will use LRU replacement to recycle slots and switching between fonts in that context
 
86
// (aka window) will slow down by about a factor of 10x to 15x, unless the LRU will do a good
 
87
// job of finding a new steady state. It will still be reasonably fast though.
 
88
#define MAX_CACHE_SLOTS 40
 
89
 
 
90
// Guarantee correct text rendering on at least 10 different contexts (aka onscreen windows).
 
91
// This limit makes sure that as long as there aren't more than 10 contexts, none of them can
 
92
// starve of resources because another one is too greedy. When this limit is reached, the cache
 
93
// turns to agressive LRU replacement instead of allocating new slots for new font configurations.
 
94
// Note: 10 is a good number, because Screen() allows for maximum 10 screens, so therefore at most
 
95
// 10 fullscreen onscreen windows, so guaranteeing for 10 onscreen windows should be good enough.
 
96
#define MIN_GUARANTEED_CONTEXTS 10
 
97
 
 
98
unsigned int nowtime = 0;
 
99
unsigned int hitcount = 0;
 
100
unsigned int _verbosity = 2;
 
101
bool _firstCall = true;
 
102
bool _useOwnFontmapper = false;
 
103
unsigned _mapperFlags;
 
104
int _antiAliasing = 1;
 
105
double _vxs, _vys, _vw, _vh;
 
106
char _fontName[FILENAME_MAX] = { 0 };
 
107
unsigned int _fontStyle = 0;
 
108
double _fontSize = 0.0;
 
109
GLfloat _fgcolor[4];
 
110
GLfloat _bgcolor[4];
 
111
 
 
112
typedef struct fontCacheItem_t {
 
113
    int contextId;
 
114
    unsigned int timestamp;
 
115
    int antiAliasing;
 
116
    char fontName[FILENAME_MAX];
 
117
    char fontRealName[FILENAME_MAX];
 
118
    unsigned int fontStyle;
 
119
    double fontSize;
 
120
    FTFont *faceT;
 
121
    FTFont *faceM;
 
122
    FT_Face ft_face;
 
123
} fontCacheItem;
 
124
fontCacheItem cache[MAX_CACHE_SLOTS];
 
125
 
 
126
// Internal helper:
 
127
// Convert unicode text in 'text', with length 'textLen' into wchar_t text string and
 
128
// store it in internal global uniCodeText[] variable for use. Filter out unprintable
 
129
// characters:
 
130
#define MAX_TEXT_LENGTH 10000
 
131
wchar_t uniCodeText[MAX_TEXT_LENGTH + 1];
 
132
int UnicodeToWchar(int textInLen, double *text)
 
133
{
 
134
    int i, j = 0;
 
135
 
 
136
    // Synthesize Unicode wchar_t string from double vector:
 
137
    memset(&uniCodeText[0], 0, sizeof(uniCodeText));
 
138
    textInLen = (textInLen > MAX_TEXT_LENGTH) ? MAX_TEXT_LENGTH : textInLen;
 
139
    for (i = 0; i < textInLen; i++) {
 
140
        // Filter out unprintable characters like 10 (LF) and 13 (CR):
 
141
        if ((text[i] != 10) && (text[i] != 13)) uniCodeText[j++] = (wchar_t) text[i];
 
142
    }
 
143
 
 
144
    // Return new textLen after potential filtering of unprintable chars
 
145
    return j;
 
146
}
 
147
 
 
148
extern "C" {
 
149
 
 
150
    int PsychInitText(void);
 
151
    int PsychShutdownText(int context);
 
152
    int PsychRebuildFont(fontCacheItem* fi);
 
153
    int PsychSetTextFont(int context, const char* fontName);
 
154
    const char* PsychGetTextFont(int context);
 
155
    int PsychSetTextStyle(int context, unsigned int fontStyle);
 
156
    int PsychSetTextSize(int context, double fontSize);
 
157
    void PsychSetTextFGColor(int context, double* color);
 
158
    void PsychSetTextBGColor(int context, double* color);
 
159
    void PsychSetTextUseFontmapper(unsigned int useMapper, unsigned int mapperFlags);
 
160
    void PsychSetTextViewPort(int context, double xs, double ys, double w, double h);
 
161
    int PsychDrawText(int context, double xStart, double yStart, int textLen, double* text);
 
162
    int PsychMeasureText(int context, int textLen, double* text, float* xmin, float* ymin, float* xmax, float* ymax);
 
163
    void PsychSetTextVerbosity(unsigned int verbosity);
 
164
    void PsychSetTextAntiAliasing(int context, int antiAliasing);
 
165
 
 
166
    fontCacheItem* getForContext(int contextId)
 
167
    {
 
168
        int lruslotid = -1;
 
169
        unsigned int lruage = 0;
 
170
        int freeslot = -1;
 
171
        int freecount = 0;
 
172
        fontCacheItem* fi = NULL;
 
173
 
 
174
        // Update running "time" for LRU replacement:
 
175
        nowtime++;
 
176
 
 
177
        // Search for matching cached font object:
 
178
        for (int i = 0; i < MAX_CACHE_SLOTS; i++) {
 
179
            // Only look at slots for requested contextId:
 
180
            if (contextId == cache[i].contextId) {
 
181
                // This one is for our contextId.
 
182
                fi = &(cache[i]);
 
183
 
 
184
                //  LRU updating, in case we need to replace:
 
185
                if (nowtime - fi->timestamp > lruage) {
 
186
                    lruslotid = i;
 
187
                    lruage = nowtime - fi->timestamp;
 
188
                }
 
189
 
 
190
                // Matching attributes for current requested attributes?
 
191
                // We match requested fontName against both, the originally requested fontName for this cache slot,
 
192
                // and the real effective fontRealName that libfontconfig actually gave us. Otherwise, as fontRealName
 
193
                // is returned to Screen(), we could get into a funny loop which causes false cache misses if the loaded font
 
194
                // doesn't match exactly the required one.
 
195
                if ((fi->antiAliasing == _antiAliasing) && (fi->fontStyle == _fontStyle) && (fi->fontSize == _fontSize) &&
 
196
                    ((strcmp(fi->fontName, _fontName) == 0) || (strcmp(fi->fontRealName, _fontName) == 0))) {
 
197
                    // Match! We have cached OGLFT font objects for this font on this context. Return them:
 
198
                    hitcount++;
 
199
 
 
200
                    if (_verbosity > 3) fprintf(stderr, "libptbdrawtext_ftgles: Cache hit for contextId %i at slot %i. Hit ratio is %f%%\n", contextId, i, (double) hitcount / (double) nowtime * 100);
 
201
 
 
202
                    // Update last access timestamp for LRU:
 
203
                    fi->timestamp = nowtime;
 
204
 
 
205
                    return(fi);
 
206
                }
 
207
            }
 
208
            else if (cache[i].contextId == -1) {
 
209
                if (freeslot == -1) freeslot = i;
 
210
                freecount++;
 
211
            }
 
212
        }
 
213
 
 
214
        // No match. We need to (re-)create a matching object.
 
215
 
 
216
        // Free slots available?
 
217
        if ((freeslot >= 0) && ((lruslotid == -1) || (freecount > MIN_GUARANTEED_CONTEXTS))) {
 
218
            // Yes. Fill it with new font object of matching properties:
 
219
            fi = &(cache[freeslot]);
 
220
            if (_verbosity > 3) fprintf(stderr, "libptbdrawtext_ftgles: Nothing cached for contextId %i. Using new slot %i. %i free slots remaining.\n", contextId, freeslot, freecount);
 
221
        }
 
222
        else if (lruslotid >= 0) {
 
223
            // No. Overwrite least recently used font object for current contextId:
 
224
            fi = &(cache[lruslotid]);
 
225
            if (_verbosity > 3) fprintf(stderr, "libptbdrawtext_ftgles: Nothing cached for contextId %i but cache full. LRU replacing slot %i, age %i. %i free slots remaining.\n", contextId, lruslotid, lruage, freecount);
 
226
        }
 
227
        else {
 
228
            // Cache full, with no possibility to even LRU replace on this new context (aka window). Game over!
 
229
            if (_verbosity > 0) fprintf(stderr, "libptbdrawtext_ftgles: ERROR when trying to setup new drawtext context %i: Font cache full with no way to free up resources! Text drawing will fail!\n", contextId);
 
230
            return(NULL);
 
231
        }
 
232
 
 
233
        // Rebuild or create font objects for slot fi. Return NULL on failure:
 
234
        if (PsychRebuildFont(fi)) return(NULL);
 
235
 
 
236
        // Update tags:
 
237
        fi->contextId = contextId;
 
238
        fi->antiAliasing = _antiAliasing;
 
239
        fi->fontStyle = _fontStyle;
 
240
        fi->fontSize = _fontSize;
 
241
        strcpy(fi->fontName, _fontName);
 
242
 
 
243
        // Update last access timestamp for LRU:
 
244
        fi->timestamp = nowtime;
 
245
 
 
246
        // Return new font objects:
 
247
        return(fi);
 
248
    }
 
249
 
 
250
    void PsychSetTextVerbosity(unsigned int verbosity)
 
251
    {
 
252
        _verbosity = verbosity;
 
253
        return;
 
254
    }
 
255
 
 
256
    void PsychSetTextAntiAliasing(int context, int antiAliasing)
 
257
    {
 
258
        static bool firstAntiAliasCall = true;
 
259
 
 
260
        // FTGLES as of April 2013 can't support non-anti-aliased rendering. Give a one-time warning:
 
261
        if ((antiAliasing == 0) && (_verbosity > 0) && firstAntiAliasCall) {
 
262
            firstAntiAliasCall = false;
 
263
            fprintf(stderr, "libptbdrawtext_ftgles: WARNING: Asked to disable text anti-aliasing for Screen('DrawText'), but this is not yet supported on OpenGL-ES! Ignored.\n");
 
264
        }
 
265
 
 
266
        _antiAliasing = antiAliasing;
 
267
        return;
 
268
    }
 
269
 
 
270
    int PsychRebuildFont(fontCacheItem* fi)
 
271
    {
 
272
        int faceIndex = 0;
 
273
        char fontFileName[FILENAME_MAX] = { 0 };
 
274
 
 
275
        // Destroy old font object, if any:
 
276
        if (fi->faceT || fi->faceM) {
 
277
            // Delete OGLFT face object:
 
278
            if (fi->faceT) delete(fi->faceT);
 
279
            fi->faceT = NULL;
 
280
 
 
281
            if (fi->faceM) delete(fi->faceM);
 
282
            fi->faceM = NULL;
 
283
 
 
284
            if (_verbosity > 3) fprintf(stderr, "libptbdrawtext_ftgles: Destroying old font face...\n");
 
285
 
 
286
            // Delete underlying FreeType representation:
 
287
            FT_Done_Face(fi->ft_face);
 
288
            fi->ft_face = NULL;
 
289
        }
 
290
 
 
291
        if (_useOwnFontmapper) {
 
292
            FcResult result = FcResultMatch;    // Must init this due to weirdness in libfontconfig...
 
293
            FcPattern *target = NULL;
 
294
 
 
295
            if (_fontName[0] == '-') {
 
296
                // _fontName starts with a '-' dash: This is not a simple font family string but a special
 
297
                // fontspec string in FontConfig's special format. It contains many possible required font
 
298
                // properties encoded in the string. Parse it into a font matching pattern:
 
299
                target = FcNameParse((FcChar8 *) & (_fontName[1]));
 
300
 
 
301
                // Need to manually add the current _fontSize, otherwise inconsistent stuff may happen:
 
302
                FcPatternAddDouble(target, FC_PIXEL_SIZE, _fontSize);
 
303
            } else {
 
304
                // _fontName contains only font family name: Build matching pattern based on _fontSize and
 
305
                // the flags provided in _fontStyle, according to the conventions in Psychtoolbox Screen('TextStyle'):
 
306
                target = FcPatternBuild(0, FC_FAMILY, FcTypeString, _fontName, FC_PIXEL_SIZE, FcTypeDouble, _fontSize,
 
307
                                        FC_WEIGHT, FcTypeInteger, ((_fontStyle & 1) ? FC_WEIGHT_BOLD : FC_WEIGHT_NORMAL),
 
308
                                        FC_SLANT, FcTypeInteger, ((_fontStyle & 2) ? FC_SLANT_ITALIC : FC_SLANT_ROMAN),
 
309
                                        FC_OUTLINE, FcTypeBool, ((_fontStyle & 8) ? true : false),
 
310
                                        FC_WIDTH, FcTypeInteger, ((_fontStyle & 32) ? FC_WIDTH_CONDENSED : ((_fontStyle & 64) ? FC_WIDTH_EXPANDED : FC_WIDTH_NORMAL)),
 
311
                                        FC_DPI, FcTypeDouble, (double)72.0, FC_SCALABLE, FcTypeBool, true, FC_ANTIALIAS, FcTypeBool, ((_antiAliasing != 0) ? true : false), NULL);
 
312
            }
 
313
 
 
314
            // Set default settings for missing pattern properties:
 
315
            FcDefaultSubstitute(target);
 
316
            if (!FcConfigSubstitute(NULL, target, FcMatchPattern)) {
 
317
                // Failed!
 
318
                if (_verbosity > 1)
 
319
                    fprintf(stderr, "libptbdrawtext_ftgles: FontConfig failed to substitute default properties for family %s, size %f pts and style flags %i.\n", _fontName, (float)_fontSize, _fontStyle);
 
320
                FcPatternDestroy(target);
 
321
                return (1);
 
322
            }
 
323
 
 
324
            // Have a matching pattern:
 
325
            if (_verbosity > 3) {
 
326
                fprintf(stderr, "libptbdrawtext_ftgles: Trying to find font that closely matches following specification:\n");
 
327
                FcPatternPrint(target);
 
328
            }
 
329
            // Perform font matching for the font in the default configuration (0) that best matches the
 
330
            // specified target pattern:
 
331
            FcPattern *matched = FcFontMatch(NULL, target, &result);
 
332
            if ((matched == NULL) || (result == FcResultNoMatch)) {
 
333
                // Failed!
 
334
                if (_verbosity > 1)
 
335
                    fprintf(stderr, "libptbdrawtext_ftgles: FontConfig failed to find a matching font for family %s, size %f pts and style flags %i.\n", _fontName, (float)_fontSize, _fontStyle);
 
336
                FcPatternDestroy(target);
 
337
                return (1);
 
338
            }
 
339
 
 
340
            // Success: Extract relevant information for Freetype-2, the font filename and faceIndex:
 
341
            if (_verbosity > 3) {
 
342
                fprintf(stderr, "libptbdrawtext_ftgles: Best matching font which will be selected for drawing has following specs:\n");
 
343
                FcPatternPrint(matched);
 
344
            }
 
345
 
 
346
            // Retrieve font filename for matched font:
 
347
            FcChar8 *localfontFileName = NULL;
 
348
            if (FcPatternGetString(matched, FC_FILE, 0, (FcChar8 **) & localfontFileName) != FcResultMatch) {
 
349
                // Failed!
 
350
                if (_verbosity > 1)
 
351
                    fprintf(stderr, "libptbdrawtext_ftgles: FontConfig did not find filename for font with family %s, size %f pts and style flags %i.\n", _fontName, (float)_fontSize, _fontStyle);
 
352
                FcPatternDestroy(target);
 
353
                FcPatternDestroy(matched);
 
354
                return (1);
 
355
            }
 
356
 
 
357
            strcpy(fontFileName, (char *)localfontFileName);
 
358
 
 
359
            // Retrieve faceIndex within fontfile:
 
360
            if (FcPatternGetInteger(matched, FC_INDEX, 0, &faceIndex) != FcResultMatch) {
 
361
                // Failed!
 
362
                if (_verbosity > 1)
 
363
                    fprintf(stderr, "libptbdrawtext_ftgles: FontConfig did not find faceIndex for font file %s, family %s, size %f pts and style flags %i.\n", fontFileName, _fontName, (float)_fontSize, _fontStyle);
 
364
                FcPatternDestroy(target);
 
365
                FcPatternDestroy(matched);
 
366
                return (1);
 
367
            }
 
368
 
 
369
            // Retrieve font family name for matched font:
 
370
            if (FcPatternGetString(matched, FC_FAMILY, 0, (FcChar8**) &localfontFileName) != FcResultMatch) {
 
371
                // Failed!
 
372
                if (_verbosity > 1) fprintf(stderr, "libptbdrawtext_ftgles: FontConfig did not return actual font family name for font with requested family %s, size %f pts and style flags %i.\n", _fontName, (float) _fontSize, _fontStyle);
 
373
                FcPatternDestroy(target);
 
374
                FcPatternDestroy(matched);
 
375
                return(1);
 
376
            }
 
377
 
 
378
            // Store it as actual name in fi:
 
379
            strcpy(fi->fontRealName, (char*) localfontFileName);
 
380
 
 
381
            // Release target pattern and matched pattern objects:
 
382
            FcPatternDestroy(target);
 
383
            FcPatternDestroy(matched);
 
384
        } else {
 
385
            // Use "raw" values as passed by calling client code:
 
386
            strcpy(fontFileName, _fontName);
 
387
            strcpy(fi->fontRealName, fontFileName);
 
388
            faceIndex = (int)_fontStyle;
 
389
        }
 
390
 
 
391
        /*
 
392
        // MK TODO FIXME: THIS DOES NOT WORK WITH FTGLES: It does not support a simple method to pass in the ft_face and build
 
393
        // a suitable OpenGL-ES representation out of it. We need to leave control to the constructors below, solely
 
394
        // based on filename of the font file, and hope for the best for now.
 
395
        //
 
396
        // Load & Create new font and face object, based on current spec settings:
 
397
        // We directly use the Freetype library, so we can spec the faceIndex for selection of textstyle, which wouldn't be
 
398
        // possible with the higher-level OGLFT constructor...
 
399
        FT_Error error = FT_New_Face(FTLibrary::Instance(), fontFileName, faceIndex, &ft_face );
 
400
        if (error) {
 
401
        if (_verbosity > 1) fprintf(stderr, "libptbdrawtext_ftgles: Freetype did not load face with index %i from font file %s.\n", faceIndex, fontFileName);
 
402
        return(1);
 
403
        }
 
404
        else {
 
405
        if (_verbosity > 3) fprintf(stderr, "libptbdrawtext_ftgles: Freetype loaded face %p with index %i from font file %s.\n", ft_face, faceIndex, fontFileName);
 
406
        }
 
407
        */
 
408
 
 
409
        // Create FTGLES face from Freetype face with given size and a 72 DPI resolution, aka _fontSize == pixelsize:
 
410
        if (_antiAliasing != 0) {
 
411
            // Font glyphs stored in textures - or better, rectangular sybregions of a texture atlas
 
412
            // where one or few textures store all glyphs of a font face. Textures are GL_ALPHA textures,
 
413
            // and blitted with alpha blending hard-coded to src_alpha, one_minus_src_alpha, so alpha blending
 
414
            // takes care of anti-aliased rendering.
 
415
            fi->faceT = new FTTextureFont(fontFileName);
 
416
            // Test the created face to make sure it will work correctly:
 
417
            if (fi->faceT->Error()) {
 
418
                delete(fi->faceT);
 
419
                fi->faceT = NULL;
 
420
                if (_verbosity > 1)
 
421
                    fprintf(stderr, "libptbdrawtext_ftgles: FTGLES did not recognize %s as a font file.\n", _fontName);
 
422
                return (1);
 
423
            }
 
424
            // Set size of font in pixels for a DPI of 72.
 
425
            fi->faceT->FaceSize(_fontSize, 72);
 
426
 
 
427
            // Use Unicode character encoding:
 
428
            fi->faceT->CharMap(ft_encoding_unicode);
 
429
        } else {
 
430
            // The current FTGLES implementation does not support BitMap or PixMap fonts without anti-aliasing.
 
431
            // The polygon/geometry based variants are not useful to us, or they are dysfunctional.
 
432
            // Therefore we are left with anti-aliased, alpha-blended texture fonts, iow., there ain't no
 
433
            // easy way to disable anti-aliasing. For now we "solve" this problem by ignoring it and just
 
434
            // do the same anti-aliased rendering, effectively ignoring the anti-aliasing enable/disable switch.
 
435
            fi->faceM = new FTTextureFont(fontFileName);
 
436
            // Test the created face to make sure it will work correctly:
 
437
            if (fi->faceM->Error()) {
 
438
                delete(fi->faceM);
 
439
                fi->faceM = NULL;
 
440
                if (_verbosity > 1)
 
441
                    fprintf(stderr, "libptbdrawtext_ftgles: FTGLES did not recognize %s as a font file.\n", _fontName);
 
442
                return (1);
 
443
            }
 
444
            // Set size of font in pixels for a DPI of 72.
 
445
            fi->faceM->FaceSize(_fontSize, 72);
 
446
 
 
447
            // Use Unicode character encoding:
 
448
            fi->faceM->CharMap(ft_encoding_unicode);
 
449
        }
 
450
 
 
451
        return (0);
 
452
    }
 
453
 
 
454
 
 
455
    void PsychSetTextFGColor(int context, double* color)
 
456
    {
 
457
        _fgcolor[0] = color[0];
 
458
        _fgcolor[1] = color[1];
 
459
        _fgcolor[2] = color[2];
 
460
        _fgcolor[3] = color[3];
 
461
 
 
462
        return;
 
463
    }
 
464
 
 
465
    void PsychSetTextBGColor(int context, double* color)
 
466
    {
 
467
        _bgcolor[0] = color[0];
 
468
        _bgcolor[1] = color[1];
 
469
        _bgcolor[2] = color[2];
 
470
        _bgcolor[3] = color[3];
 
471
 
 
472
        return;
 
473
    }
 
474
 
 
475
    // Assign dimensions and position of target viewport for rendering:
 
476
    // Assumption is that origin is bottom-left, x-axis pointing right, y-axis pointint up.
 
477
    // The plugin needs to remap or setup proper OpenGL transformations if its underlying
 
478
    // renderbackend needs a different geometric setup:
 
479
    void PsychSetTextViewPort(int context, double xs, double ys, double w, double h)
 
480
    {
 
481
        _vxs = xs;
 
482
        _vys = ys;
 
483
        _vw  = w;
 
484
        _vh  = h;
 
485
 
 
486
        return;
 
487
    }
 
488
 
 
489
    // Should the plugin use its own fontMapper implementation, or can it rely at least
 
490
    // partially - as defined by mapperFlags - on the PTB internal mapper?
 
491
    void PsychSetTextUseFontmapper(unsigned int useMapper, unsigned int mapperFlags)
 
492
    {
 
493
        _useOwnFontmapper = (useMapper > 0) ? true : false;
 
494
        _mapperFlags      = mapperFlags;
 
495
 
 
496
        return;
 
497
    }
 
498
 
 
499
    // Select font Name:
 
500
    int PsychSetTextFont(int context, const char* fontName)
 
501
    {
 
502
        strcpy(_fontName, fontName);
 
503
        return(0);
 
504
    }
 
505
 
 
506
    // Select font Style:
 
507
    int PsychSetTextStyle(int context, unsigned int fontStyle)
 
508
    {
 
509
        _fontStyle = fontStyle;
 
510
        return(0);
 
511
    }
 
512
 
 
513
    // Select font Size:
 
514
    int PsychSetTextSize(int context, double fontSize)
 
515
    {
 
516
        _fontSize = fontSize;
 
517
        return(0);
 
518
    }
 
519
 
 
520
    const char* PsychGetTextFont(int context)
 
521
    {
 
522
        // Check if rebuild of font face needed due to parameter
 
523
        // change. Reload/Rebuild font face if so, check for errors:
 
524
        fontCacheItem *fi = getForContext(context);
 
525
        if (!fi) return(NULL);
 
526
 
 
527
        return(fi->fontRealName);
 
528
    }
 
529
 
 
530
    int PsychDrawText(int context, double xStart, double yStart, int textLen, double* text)
 
531
    {
 
532
        // Check if rebuild of font face needed due to parameter
 
533
        // change. Reload/Rebuild font face if so, check for errors:
 
534
        fontCacheItem *fi = getForContext(context);
 
535
        if (!fi) return(1);
 
536
 
 
537
        // Synthesize Unicode wchar_t string from double vector:
 
538
        UnicodeToWchar(textLen, text);
 
539
 
 
540
        //  Done by FTGLES: glEnable( GL_TEXTURE_2D );
 
541
        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
 
542
        int oldBlendEnable = glIsEnabled(GL_BLEND);
 
543
 
 
544
        glMatrixMode(GL_PROJECTION);
 
545
        glPushMatrix();
 
546
        glLoadIdentity();
 
547
        glOrthof(_vxs, _vxs + _vw, _vys, _vys + _vh, -1, +1);
 
548
 
 
549
        glMatrixMode(GL_MODELVIEW);
 
550
 
 
551
        // Rendering of background quad requested? -- True if background alpha > 0.
 
552
        if (_bgcolor[3] > 0) {
 
553
            // Yes. Compute bounding box of "to be drawn" text and render a quad in background color:
 
554
            float xmin, ymin, xmax, ymax;
 
555
            PsychMeasureText(context, textLen, text, &xmin, &ymin, &xmax, &ymax);
 
556
 
 
557
            // Compensate for 1-pixel off problems with some fonts:
 
558
            xmin--;
 
559
            xmax++;
 
560
            ymin--;
 
561
            ymax++;
 
562
 
 
563
            // Use immediate mode emulation functions from glesGlue.h/c.
 
564
            // They implemented the same trick i use in the Screen() core
 
565
            // code to get immediate mode convenience back - Great minds
 
566
            // think alike ;-) -- Their implementation is slightly more
 
567
            // complete and refined though. While this is not needed in
 
568
            // Screen() core code, we might use their glesGlue in other
 
569
            // parts of ptb, e.g., our moglcore for OpenGL-ES support.
 
570
            ftglBegin(GL_QUADS);
 
571
            ftglColor4f(_bgcolor[0], _bgcolor[1], _bgcolor[2], _bgcolor[3]);
 
572
            ftglVertex2f(xmin + xStart, ymin + yStart);
 
573
            ftglVertex2f(xmin + xStart, ymax + yStart);
 
574
            ftglVertex2f(xmax + xStart, ymax + yStart);
 
575
            ftglVertex2f(xmax + xStart, ymin + yStart);
 
576
            ftglEnd();
 
577
        }
 
578
        // Set text color:
 
579
        glColor4f(_fgcolor[0], _fgcolor[1], _fgcolor[2], _fgcolor[3]);
 
580
 
 
581
        // Set drawing start position:
 
582
        glPushMatrix();
 
583
        glTranslatef(xStart, yStart, 0.0f);
 
584
 
 
585
        // Draw the text:
 
586
        if (fi->faceT) {
 
587
            fi->faceT->Render(uniCodeText);
 
588
        } else {
 
589
            fi->faceM->Render(uniCodeText);
 
590
        }
 
591
 
 
592
        // Reset drawing position:
 
593
        glPopMatrix();
 
594
 
 
595
        glMatrixMode(GL_PROJECTION);
 
596
        glPopMatrix();
 
597
 
 
598
        // Back to standard modelview matrix:
 
599
        glMatrixMode(GL_MODELVIEW);
 
600
 
 
601
        // Restore blending enable state:
 
602
        if (oldBlendEnable) {
 
603
            glEnable(GL_BLEND);
 
604
        } else {
 
605
            glDisable(GL_BLEND);
 
606
        }
 
607
 
 
608
        // Ready!
 
609
        return (0);
 
610
    }
 
611
 
 
612
    int PsychMeasureText(int context, int textLen, double* text, float* xmin, float* ymin, float* xmax, float* ymax)
 
613
    {
 
614
        // Check if rebuild of font face needed due to parameter
 
615
        // change. Reload/Rebuild font face if so, check for errors:
 
616
        fontCacheItem *fi = getForContext(context);
 
617
        if (!fi) return(1);
 
618
 
 
619
        // Convert text to unicode wchar_t, filtering for unprintable characters:
 
620
        textLen = UnicodeToWchar(textLen, text);
 
621
 
 
622
        // Compute its bounding box:
 
623
        FTBBox box = (fi->faceT) ? fi->faceT->BBox(uniCodeText, textLen) : fi->faceM->BBox(uniCodeText, textLen);
 
624
 
 
625
        *xmin = box.Lower().X();
 
626
        *ymin = box.Lower().Y();
 
627
        *xmax = box.Upper().X();
 
628
        *ymax = box.Upper().Y();
 
629
 
 
630
        return (0);
 
631
    }
 
632
 
 
633
    int PsychInitText(void)
 
634
    {
 
635
        _firstCall = true;
 
636
 
 
637
        // Try to initialize libfontconfig - our fontMapper library for font matching and selection:
 
638
        if (!FcInit()) {
 
639
            if (_verbosity > 0)
 
640
                fprintf(stderr, "libptbdrawtext_ftgles: FontMapper initialization failed!\n");
 
641
            return (1);
 
642
        }
 
643
 
 
644
        // Clear cache of all fonts instances:
 
645
        memset(&cache, 0, sizeof(cache));
 
646
        for (int i = 0; i < MAX_CACHE_SLOTS; i++) cache[i].contextId = -1;
 
647
 
 
648
        if (_verbosity > 2) {
 
649
            fprintf(stderr, "libptbdrawtext_ftgles: External 'DrawText' text rendering plugin initialized.\n");
 
650
            fprintf(stderr, "libptbdrawtext_ftgles: Maximum number of cacheable fonts is %i, minimum number of supported concurrent windows is %i.\n", MAX_CACHE_SLOTS, MIN_GUARANTEED_CONTEXTS);
 
651
            fprintf(stderr, "libptbdrawtext_ftgles: This plugin uses multiple excellent free software libraries to do its work:\n");
 
652
            fprintf(stderr, "libptbdrawtext_ftgles: FTGLES (https://github.com/cdave1/ftgles) the FreeType OpenGL-ES library.\n");
 
653
            fprintf(stderr, "libptbdrawtext_ftgles: The FreeType-2 (http://freetype.sourceforge.net/) library.\n");
 
654
            fprintf(stderr, "libptbdrawtext_ftgles: The FontConfig (http://www.fontconfig.org) library.\n");
 
655
            fprintf(stderr, "libptbdrawtext_ftgles: Thanks!\n\n");
 
656
        }
 
657
 
 
658
        return (0);
 
659
    }
 
660
 
 
661
    int PsychShutdownText(int context)
 
662
    {
 
663
        // Delete specific context?
 
664
        if (context >= 0) {
 
665
            // Yes. Delete all objects for this context:
 
666
            for (int i = 0; i < MAX_CACHE_SLOTS; i++) {
 
667
                // Is this slot to be destructed?
 
668
                if (cache[i].contextId == context) {
 
669
                    fontCacheItem *fi = &(cache[i]);
 
670
                    fi->contextId = -1;
 
671
 
 
672
                    if (fi->faceT || fi->faceM) {
 
673
                        if (_verbosity > 3) fprintf(stderr, "libptbdrawtext_ftgles: In shutdown for context %i, slot %i:  faceT = %p faceM = %p\n", context, i, fi->faceT, fi->faceM);
 
674
 
 
675
                        // Delete OGLFT face objects:
 
676
                        if (fi->faceT) delete(fi->faceT);
 
677
                        fi->faceT = NULL;
 
678
 
 
679
                        if (fi->faceM) delete(fi->faceM);
 
680
                        fi->faceM = NULL;
 
681
 
 
682
                        // Delete Freetype face object:
 
683
                        if (fi->ft_face) FT_Done_Face(fi->ft_face);
 
684
                        fi->ft_face = NULL;
 
685
                    }
 
686
                }
 
687
            }
 
688
 
 
689
            return(0);
 
690
        }
 
691
 
 
692
        // Complete shutdown for the plugin:
 
693
        if (_verbosity > 3) fprintf(stderr, "libptbdrawtext_ftgles: Shutting down. Overall cache hit ratio was %f%%\n", (double) hitcount / (double) nowtime * 100);
 
694
        _firstCall = false;
 
695
 
 
696
        // Shutdown fontmapper library:
 
697
        // Actually, don't! Some versions of octave also use fontconfig internally, and there is only
 
698
        // one shared library instance in the process. Calling FcFini() here will shutdown that instance
 
699
        // and wreak havoc if octave or other clients later try to access that shutdown instance after
 
700
        // we've closed down. Keeping it alive is ok, let octave/matlab/the os whatever do the cleanup.
 
701
        // Luckily we can always call FcInit() in our init path, as it turns into a no-op if fontconfig
 
702
        // has been already brought online by some other client, e.g., octave or matlab.
 
703
        // Should fix bug from forum msg #12560
 
704
        // FcFini();
 
705
 
 
706
        return (0);
 
707
    }
 
708
} // extern "C"