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.
6
* This works only on GNU/Linux.
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.
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.
24
* The library must be ./configured as follows to allow linking the plugin with the static lib:
25
* ./configure --with-pic
27
* Requires the FTGL subfolder of that lib (ftgles/ftgles/src/FTGL) in our build directory, for
28
* access to header definitions.
30
* Requires libfontconfig and libFreeType-2.
32
* Building the plugin for Linux:
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
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
39
* libptbdrawtext_ftgles is copyright (c) 2013 by Mario Kleiner.
40
* It is licensed to you as follows:
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:
50
* The above copyright notice and this permission notice shall be
51
* included in all copies or substantial portions of the Software.
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.
63
// Standard libs for debug output and unicode character string handling:
69
// Include GLFTES and OpenGL-ES stuff, as well as GL on GLES emulation glue:
71
#include <FTGL/ftgles.h>
72
#include <FTGL/ftglesGlue.h>
74
// Include fontconfig stuff:
75
#include "fontconfig/fontconfig.h"
76
#include "fontconfig/fcfreetype.h"
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
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
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;
112
typedef struct fontCacheItem_t {
114
unsigned int timestamp;
116
char fontName[FILENAME_MAX];
117
char fontRealName[FILENAME_MAX];
118
unsigned int fontStyle;
124
fontCacheItem cache[MAX_CACHE_SLOTS];
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
130
#define MAX_TEXT_LENGTH 10000
131
wchar_t uniCodeText[MAX_TEXT_LENGTH + 1];
132
int UnicodeToWchar(int textInLen, double *text)
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];
144
// Return new textLen after potential filtering of unprintable chars
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);
166
fontCacheItem* getForContext(int contextId)
169
unsigned int lruage = 0;
172
fontCacheItem* fi = NULL;
174
// Update running "time" for LRU replacement:
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.
184
// LRU updating, in case we need to replace:
185
if (nowtime - fi->timestamp > lruage) {
187
lruage = nowtime - fi->timestamp;
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:
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);
202
// Update last access timestamp for LRU:
203
fi->timestamp = nowtime;
208
else if (cache[i].contextId == -1) {
209
if (freeslot == -1) freeslot = i;
214
// No match. We need to (re-)create a matching object.
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);
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);
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);
233
// Rebuild or create font objects for slot fi. Return NULL on failure:
234
if (PsychRebuildFont(fi)) return(NULL);
237
fi->contextId = contextId;
238
fi->antiAliasing = _antiAliasing;
239
fi->fontStyle = _fontStyle;
240
fi->fontSize = _fontSize;
241
strcpy(fi->fontName, _fontName);
243
// Update last access timestamp for LRU:
244
fi->timestamp = nowtime;
246
// Return new font objects:
250
void PsychSetTextVerbosity(unsigned int verbosity)
252
_verbosity = verbosity;
256
void PsychSetTextAntiAliasing(int context, int antiAliasing)
258
static bool firstAntiAliasCall = true;
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");
266
_antiAliasing = antiAliasing;
270
int PsychRebuildFont(fontCacheItem* fi)
273
char fontFileName[FILENAME_MAX] = { 0 };
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);
281
if (fi->faceM) delete(fi->faceM);
284
if (_verbosity > 3) fprintf(stderr, "libptbdrawtext_ftgles: Destroying old font face...\n");
286
// Delete underlying FreeType representation:
287
FT_Done_Face(fi->ft_face);
291
if (_useOwnFontmapper) {
292
FcResult result = FcResultMatch; // Must init this due to weirdness in libfontconfig...
293
FcPattern *target = NULL;
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]));
301
// Need to manually add the current _fontSize, otherwise inconsistent stuff may happen:
302
FcPatternAddDouble(target, FC_PIXEL_SIZE, _fontSize);
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);
314
// Set default settings for missing pattern properties:
315
FcDefaultSubstitute(target);
316
if (!FcConfigSubstitute(NULL, target, FcMatchPattern)) {
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);
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);
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)) {
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);
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);
346
// Retrieve font filename for matched font:
347
FcChar8 *localfontFileName = NULL;
348
if (FcPatternGetString(matched, FC_FILE, 0, (FcChar8 **) & localfontFileName) != FcResultMatch) {
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);
357
strcpy(fontFileName, (char *)localfontFileName);
359
// Retrieve faceIndex within fontfile:
360
if (FcPatternGetInteger(matched, FC_INDEX, 0, &faceIndex) != FcResultMatch) {
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);
369
// Retrieve font family name for matched font:
370
if (FcPatternGetString(matched, FC_FAMILY, 0, (FcChar8**) &localfontFileName) != FcResultMatch) {
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);
378
// Store it as actual name in fi:
379
strcpy(fi->fontRealName, (char*) localfontFileName);
381
// Release target pattern and matched pattern objects:
382
FcPatternDestroy(target);
383
FcPatternDestroy(matched);
385
// Use "raw" values as passed by calling client code:
386
strcpy(fontFileName, _fontName);
387
strcpy(fi->fontRealName, fontFileName);
388
faceIndex = (int)_fontStyle;
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.
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 );
401
if (_verbosity > 1) fprintf(stderr, "libptbdrawtext_ftgles: Freetype did not load face with index %i from font file %s.\n", faceIndex, fontFileName);
405
if (_verbosity > 3) fprintf(stderr, "libptbdrawtext_ftgles: Freetype loaded face %p with index %i from font file %s.\n", ft_face, faceIndex, fontFileName);
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()) {
421
fprintf(stderr, "libptbdrawtext_ftgles: FTGLES did not recognize %s as a font file.\n", _fontName);
424
// Set size of font in pixels for a DPI of 72.
425
fi->faceT->FaceSize(_fontSize, 72);
427
// Use Unicode character encoding:
428
fi->faceT->CharMap(ft_encoding_unicode);
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()) {
441
fprintf(stderr, "libptbdrawtext_ftgles: FTGLES did not recognize %s as a font file.\n", _fontName);
444
// Set size of font in pixels for a DPI of 72.
445
fi->faceM->FaceSize(_fontSize, 72);
447
// Use Unicode character encoding:
448
fi->faceM->CharMap(ft_encoding_unicode);
455
void PsychSetTextFGColor(int context, double* color)
457
_fgcolor[0] = color[0];
458
_fgcolor[1] = color[1];
459
_fgcolor[2] = color[2];
460
_fgcolor[3] = color[3];
465
void PsychSetTextBGColor(int context, double* color)
467
_bgcolor[0] = color[0];
468
_bgcolor[1] = color[1];
469
_bgcolor[2] = color[2];
470
_bgcolor[3] = color[3];
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)
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)
493
_useOwnFontmapper = (useMapper > 0) ? true : false;
494
_mapperFlags = mapperFlags;
500
int PsychSetTextFont(int context, const char* fontName)
502
strcpy(_fontName, fontName);
506
// Select font Style:
507
int PsychSetTextStyle(int context, unsigned int fontStyle)
509
_fontStyle = fontStyle;
514
int PsychSetTextSize(int context, double fontSize)
516
_fontSize = fontSize;
520
const char* PsychGetTextFont(int context)
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);
527
return(fi->fontRealName);
530
int PsychDrawText(int context, double xStart, double yStart, int textLen, double* text)
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);
537
// Synthesize Unicode wchar_t string from double vector:
538
UnicodeToWchar(textLen, text);
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);
544
glMatrixMode(GL_PROJECTION);
547
glOrthof(_vxs, _vxs + _vw, _vys, _vys + _vh, -1, +1);
549
glMatrixMode(GL_MODELVIEW);
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);
557
// Compensate for 1-pixel off problems with some fonts:
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.
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);
579
glColor4f(_fgcolor[0], _fgcolor[1], _fgcolor[2], _fgcolor[3]);
581
// Set drawing start position:
583
glTranslatef(xStart, yStart, 0.0f);
587
fi->faceT->Render(uniCodeText);
589
fi->faceM->Render(uniCodeText);
592
// Reset drawing position:
595
glMatrixMode(GL_PROJECTION);
598
// Back to standard modelview matrix:
599
glMatrixMode(GL_MODELVIEW);
601
// Restore blending enable state:
602
if (oldBlendEnable) {
612
int PsychMeasureText(int context, int textLen, double* text, float* xmin, float* ymin, float* xmax, float* ymax)
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);
619
// Convert text to unicode wchar_t, filtering for unprintable characters:
620
textLen = UnicodeToWchar(textLen, text);
622
// Compute its bounding box:
623
FTBBox box = (fi->faceT) ? fi->faceT->BBox(uniCodeText, textLen) : fi->faceM->BBox(uniCodeText, textLen);
625
*xmin = box.Lower().X();
626
*ymin = box.Lower().Y();
627
*xmax = box.Upper().X();
628
*ymax = box.Upper().Y();
633
int PsychInitText(void)
637
// Try to initialize libfontconfig - our fontMapper library for font matching and selection:
640
fprintf(stderr, "libptbdrawtext_ftgles: FontMapper initialization failed!\n");
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;
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");
661
int PsychShutdownText(int context)
663
// Delete specific context?
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]);
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);
675
// Delete OGLFT face objects:
676
if (fi->faceT) delete(fi->faceT);
679
if (fi->faceM) delete(fi->faceM);
682
// Delete Freetype face object:
683
if (fi->ft_face) FT_Done_Face(fi->ft_face);
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);
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