Can't load font when ofTruTypeFont object is a class member inside a data structure

I’ve written a class that includes an ofTrueTypeFont object and it worked fine, until I moved the object of this class into a data structure. Below is a pseudo-code trying to express what is going on:
ofApp.h

typedef struct _MyStruct{
  vector<myClass> objs;
} MyStruct;

class MyClass {
  public:
    MyClass(); // constructore stuff here, I'm not loading a font here
    ofTrueTypeFont font;
    void setup(int fontsize){
      font.load("sonata.ttf", fontsize);
    }
    void drawText(){
      font.drawString("test string", 100, 100);
    }
};

class ofApp : public ofBaseApp {
  public:
    // all OF functions here
    MyStruct myStruct;
};

ofApp.cpp

void setup(){
  MyClass myClass;
  myStruct.objs.push_back(myClass);
  myStruct.objs.back().setup(30);
}

void draw(){
  for (unsigned i = 0; i < myStruct.objs.size(); i++) {
    myStruct.objs[i].drawText();
  }
}

Is there a caveat when placing an ofTrueTypeFont object inside a data structure? Because before I did that, everything was being drawn fine, but now I get this error:
[ error ] ofTrueTypeFont: drawString(): font not allocated
BTW, I need to place my class inside a data structure because I need to share some data between threads, and passing a pointer of the data structure to the threaded class is the easiest solution for me.

Two possible solutions:

  • 1: Get rid of the MyClass constructor or make a copy constructor. Generally if you make a constructor and you want objects to be in a vector you need to make a copy constructor too. This should then make sure that the textures inside ofTrueTypeFont get copied correctly.
  • 2: Simpler. Change:
ofTrueTypeFont font;

to

shared_ptr<ofTrueTypeFont> font;

and change setup to:

    void setup(int fontsize){
          font = make_shared<ofTrueTypeFont>(); 
          font->load("sonata.ttf", fontsize);
    }

That way when the object gets copied by pushing into a vector you are just copying the pointer and not the whole object.

Changed this part (to the actual source code which is different than the code I’m posting here, I just can’t post the entire code, it’s becoming long). I keep on getting the same error. I also get this:

[ error ] ofTruetypeFont: Trying to allocate texture of 512x512 which is bigger than supported in current platform: 0

Does this give any hint? I saw some posts in this forum about ofTrueTypeFontSettings where you can set the range, but it looks pretty complicated to me. I’m using the Sonata font to display music notes etc. and this font uses quite some extensive symbols, so I’m really not sure how to use .range() and if this is what I need.

Can you load and use the font in a bare bones ofApp?

If you can it might be one of the issues I mentioned ( and I would recommend switching the font to a shared_ptr ).

If the font doesn’t work in ofApp not inside any vector of objects that could def be an issue with the font itself.

What OS / Platform are you on?

Using OpenGL in threads is not possible so that could be your issue right there.
Def make sure your font loading is not happening in a thread.

Hey @alexandrosdrymonitis fun to see you in the forum! So I had a go at pasting your code above into the fontsExample. And you know it worked just fine and I didn’t get any errors after I removed the MyClass() constructor; So, I’m wondering if you’ve tried something similar, with one of the fonts from the OF examples. Here’s what I added to the fontsExample:

in ofApp.h, add MyClass():

class MyClass {
  public:
    // removed the constructor
    ofTrueTypeFont font;
    void setup(int fontsize){
      font.load("verdana.ttf", fontsize);
    }
    void drawText(){
      font.drawString("test string", 100, 100);
    }
};

// then in class ofApp, added:
    typedef struct _MyStruct{
      std::vector<MyClass> objs;
    } MyStruct;
    
    MyStruct mystruct;

in ofApp::setup(), added:

    MyClass myClass;
    mystruct.objs.push_back(myClass);
    mystruct.objs.at(0).setup(14);
    mystruct.objs.at(0).font.setLineHeight(18.f);
    mystruct.objs.at(0).font.setLetterSpacing(1.037);

then in ofApp::draw(), added:

    mystruct.objs.at(0).font.drawString("test string for MyClass", -bounds.width/2, bounds.height/2 );

Also @Theo has some important thoughts on using threads. Also remember that ofTrueTypeFont has some vectors in it, which may not like being used with threads either.

Also I tried both of the following and got errors:

// this didn't work:
    MyClass myClass;
    mystruct.objs.at(0).setup(14);
    mystruct.objs.push_back(myClass); // copy a setup instance into the vector

// and neither did this:
    MyClass myClass;
    myClass.setup(14); // setup the instance
    mystruct.objs.push_back(std::move(myClass)); // then move it into the vector

I guess it’s my bad that I wrote the pseudo-code trying to narrow down what I thought was wrong with my code, without actually testing it. @TimChi (BTW, nice to see you here again too!) you’re right, the code worked, even with the constructor. I did change it a bit to the following:
ofApp.h:

#pragma once

#include "ofMain.h"

class MyClass {
  public:
    // removed the constructor
    ofTrueTypeFont font;
		string myStr;
    void setup(int fontsize){
      font.load("verdana.ttf", fontsize);
    }
		void setStr(string str){
			myStr = str;
		}
    void drawText(){
      font.drawString(myStr, 100, 100);
    }
};

typedef struct _MyStruct{
	std::vector<MyClass> objs;
} MyStruct;

class ClassTwo {
	public:
		MyStruct *myStruct;
		ClassTwo(MyStruct *mStr) {
			myStruct = mStr;
		}
		void setStr(string str){
			myStruct->objs.at(0).setStr(str);
		}
};

class ofApp : public ofBaseApp{
	public:
		void setup();
		void update();
		void draw();

		void keyPressed(int key);
		void keyReleased(int key);
		void mouseMoved(int x, int y);
		void mouseDragged(int x, int y, int button);
		void mousePressed(int x, int y, int button);
		void mouseReleased(int x, int y, int button);
		void mouseEntered(int x, int y);
		void mouseExited(int x, int y);
		void windowResized(int w, int h);
		void dragEvent(ofDragInfo dragInfo);
		void gotMessage(ofMessage msg);

    MyStruct mystruct;
};

ofApp.cpp:

void ofApp::setup(){
  MyClass myClass;
  mystruct.objs.push_back(myClass);
  mystruct.objs.at(0).setup(14);
  mystruct.objs.at(0).font.setLineHeight(18.f);
  mystruct.objs.at(0).font.setLetterSpacing(1.037);
  ClassTwo classTwo(&mystruct);
  classTwo.setStr("this is a test");
}

void ofApp::draw(){
  mystruct.objs.at(0).drawText();
}

What I needed to do is to create a class that will be pushed back to a vector that is part of a data structure, and then have another class passed the reference to the data structure which will control some parameter of the first class, in this case the string to be drawn.
The code above is totally functional, but my original code still doesn’t work, so the culprit is somewhere else. I have to keep on digging.
Thank you both for the help and I’m really sorry for the noise.

1 Like

It seems that the problem was indeed related to calling OpenGL functions inside a threaded function. I was calling font.stringWidth() in some places of the threade function. I replaced that by storing the various widths of strings in arrays prior to calling the threaded function, and used these arrays instead of stringWidth() and now I’m not gettting any more errors.
I didn’t realize this could cause an issue as I didn’t think stringWidth() is drawing a string. It turns out I was wrong. Once again, thank you for the help.

1 Like

Hey that’s awesome! And thanks for posting about it too! When I looked at ofTrueTypeFont quickly it seemed nontrivial, with lots going on in it. I will definitely remember this idea of checking for openGL calls in threaded projects when errors arise. And I like how you found a way to still use the class while working around the thread-sensitive parts of it.

1 Like