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);
}