int PsychMeasureText(int textLen, double* text, float* xmin, float* ymin, float* xmax, float* ymax) { int i; QChar* myUniChars = new QChar[textLen]; // Check if rebuild of font face needed due to parameter // chage. Reload/Rebuild font face if so, check for errors: if (_needsRebuild && PsychRebuildFont()) return(1); // Synthesize Unicode QString from double vector: for(i = 0; i < textLen; i++) { myUniChars[i] = QChar((unsigned int) text[i]); } QString uniCodeText = QString(myUniChars, textLen); delete [] myUniChars; // Compute its bounding box: OGLFT::BBox box = (faceT) ? faceT->measure(uniCodeText) : faceM->measure(uniCodeText); *xmin = box.x_min_; *ymin = box.y_min_; *xmax = box.x_max_; *ymax = box.y_max_; return(0); }
int PsychDrawText(double xStart, double yStart, int textLen, double* text) { int i; GLuint ti; QChar* myUniChars = new QChar[textLen]; // On first invocation after init we need to generate a useless texture object. // This is a weird workaround for some weird bug somewhere in FTGL... if (_firstCall) { _firstCall = false; glGenTextures(1, &ti); } // Check if rebuild of font face needed due to parameter // change. Reload/Rebuild font face if so, check for errors: if (_needsRebuild && PsychRebuildFont()) return(1); // Synthesize Unicode QString from double vector: for(i = 0; i < textLen; i++) { myUniChars[i] = QChar((unsigned int) text[i]); } QString uniCodeText = QString(myUniChars, textLen); delete [] myUniChars; glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS); glPushAttrib(GL_ALL_ATTRIB_BITS); glPixelStorei( GL_UNPACK_ALIGNMENT, 1); glEnable( GL_TEXTURE_2D ); glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); gluOrtho2D(_vxs, _vxs + _vw, _vys, _vys + _vh); glMatrixMode(GL_MODELVIEW); // Set text color: This will be filtered by OGLFT for redundant settings: if (faceT) { faceT->setForegroundColor( _fgcolor[0], _fgcolor[1], _fgcolor[2], _fgcolor[3]); } else { faceM->setForegroundColor( _fgcolor[0], _fgcolor[1], _fgcolor[2], _fgcolor[3]); } // Rendering of background quad requested? -- True if background alpha > 0. if (_bgcolor[3] > 0) { // Yes. Compute bounding box of "to be drawn" text and render a quad in background color: float xmin, ymin, xmax, ymax; PsychMeasureText(textLen, text, &xmin, &ymin, &xmax, &ymax); glColor4fv(&(_bgcolor[0])); glRectf(xmin + xStart, ymin + yStart, xmax + xStart, ymax + yStart); } // Draw the text at selected start location: if (faceT) { faceT->draw(xStart, yStart, uniCodeText); } else { faceM->draw(xStart, yStart, uniCodeText); } glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glDisable( GL_TEXTURE_2D ); glPopAttrib(); glPopClientAttrib(); // Ready! return(0); }
fontCacheItem* getForContext(int contextId) { int lruslotid = -1; unsigned int lruage = 0; int freeslot = -1; int freecount = 0; fontCacheItem* fi = NULL; // Update running "time" for LRU replacement: nowtime++; // Search for matching cached font object: for (int i = 0; i < MAX_CACHE_SLOTS; i++) { // Only look at slots for requested contextId: if (contextId == cache[i].contextId) { // This one is for our contextId. fi = &(cache[i]); // LRU updating, in case we need to replace: if (nowtime - fi->timestamp > lruage) { lruslotid = i; lruage = nowtime - fi->timestamp; } // Matching attributes for current requested attributes? // We match requested fontName against both, the originally requested fontName for this cache slot, // and the real effective fontRealName that libfontconfig actually gave us. Otherwise, as fontRealName // is returned to Screen(), we could get into a funny loop which causes false cache misses if the loaded font // doesn't match exactly the required one. if ((fi->antiAliasing == _antiAliasing) && (fi->fontStyle == _fontStyle) && (fi->fontSize == _fontSize) && ((strcmp(fi->fontName, _fontName) == 0) || (strcmp(fi->fontRealName, _fontName) == 0))) { // Match! We have cached OGLFT font objects for this font on this context. Return them: hitcount++; if (_verbosity > 15) fprintf(stdout, "libptbdrawtext_ftgl: Cache hit for contextId %i at slot %i. Hit ratio is %f%%\n", contextId, i, (double) hitcount / (double) nowtime * 100); // Update last access timestamp for LRU: fi->timestamp = nowtime; return(fi); } } else if (cache[i].contextId == -1) { if (freeslot == -1) freeslot = i; freecount++; } } // No match. We need to (re-)create a matching object. // Free slots available? if ((freeslot >= 0) && ((lruslotid == -1) || (freecount > MIN_GUARANTEED_CONTEXTS))) { // Yes. Fill it with new font object of matching properties: fi = &(cache[freeslot]); if (_verbosity > 12) fprintf(stdout, "libptbdrawtext_ftgl: Nothing cached for contextId %i. Using new slot %i. %i free slots remaining.\n", contextId, freeslot, freecount); } else if (lruslotid >= 0) { // No. Overwrite least recently used font object for current contextId: fi = &(cache[lruslotid]); if (_verbosity > 12) fprintf(stdout, "libptbdrawtext_ftgl: Nothing cached for contextId %i but cache full. LRU replacing slot %i, age %i. %i free slots remaining.\n", contextId, lruslotid, lruage, freecount); } else { // Cache full, with no possibility to even LRU replace on this new context (aka window). Game over! if (_verbosity > 0) fprintf(stdout, "libptbdrawtext_ftgl: 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); return(NULL); } // Rebuild or create font objects for slot fi. Return NULL on failure: if (PsychRebuildFont(fi)) return(NULL); // Update tags: fi->contextId = contextId; fi->antiAliasing = _antiAliasing; fi->fontStyle = _fontStyle; fi->fontSize = _fontSize; strcpy(fi->fontName, _fontName); // Update last access timestamp for LRU: fi->timestamp = nowtime; // Return new font objects: return(fi); }