Bug (?) with Font


#1

I’m getting some weird behavior with global const Font variables for JUCE 2.0.21 using Windows Vista.

I have, in my code, a header file of globals with a bunch of const Font’s with names like FONT_BOLD_STANDARD, which I then sometimes pass into various Font-related functions involving Components - the idea being that if I want to then change one of the fonts in my program, I just change what the const Font definition is set to and then the rest of it changes as well.

If I do this, however, I end up getting weird leak detections on exit, with LeakedObjectDetector firing three asserts and telling me that there’s one leaked instance of WindowsDirectWriteTypeface, one leaked instance of Typeface, and one leaked instance of AffineTransform. After this, the problem gets worse - it ends up trying to read some bogus memory in what appears to be ComSmartPtr::release(), which seems to be called as part of the ~WindowsDirectWriteTypeface() destructor, and then crashes completely.

The below code snippet, edited from the Hello World program, reproduces the problem on the latest 2.0.21 JUCE revision on Windows Vista when you hit the “X” button to close the window. The carnage happens in the top class “FoobarButton” - the rest are just generic classes stolen from the demo to actually produce a working program. However, they’re relevant to the bug, since the problem doesn’t seem to occur unless the button is actually displayed.

[code]#include “…/JuceLibraryCode/JuceHeader.h”

const Font f(“Arial”, 15, Font::bold);

class FoobarButton : public TextButton {
public:
FoobarButton():TextButton(“Foobar”) {}
Font getFont() { return f; } //<-- the addition of this line causes the program to crash on close. returning Font(f) doesn’t work either
};

class MainComponent : public Component {
public:
FoobarButton s;

MainComponent () { addAndMakeVisible (&s); setSize (600, 300); }
void resized() { s.setBounds (0,0,getWidth(),getHeight()); }

};

class HelloWorldWindow : public DocumentWindow {
public:
HelloWorldWindow() : DocumentWindow (“JUCE Hello World!”, Colours::lightgrey, DocumentWindow::allButtons, true) {
setContentOwned (new MainComponent(), true);
centreWithSize (getWidth(), getHeight());
setVisible (true);
}

void closeButtonPressed() { JUCEApplication::quit(); }

};

class JUCEHelloWorldApplication : public JUCEApplication {
public:
JUCEHelloWorldApplication() {}
~JUCEHelloWorldApplication() {}

void initialise (const String& commandLine) { helloWorldWindow = new HelloWorldWindow(); }
void shutdown() { delete helloWorldWindow; }

const String getApplicationName() { return "Hello World for JUCE"; }
const String getApplicationVersion() { return ProjectInfo::versionString; }

private:
HelloWorldWindow* helloWorldWindow;
};

START_JUCE_APPLICATION (JUCEHelloWorldApplication)[/code]

Hope that’s enough to describe the problem and that I’ve followed the bug reporting standards properly.


#2

Hi, Boozer

Try this:

[code]// const Font f(“Arial”, 15, Font::bold);

class FoobarButton : public TextButton {
public:
FoobarButton():TextButton(“Foobar”), f(“Arial”, 15, Font::bold) {}
Font getFont() { return f; }

private:
const Font f;
};[/code]

Actually, needless derived the subclass from TextButton class, just revise:

const Font getFontForTextButton (TextButton & button)

in your LookAndFeel class.


#3

Yeah, there’s definitely no reason to use a global there.

And whenever you declare a global which is a complex object rather than just a primitive, you have to be very careful to think about what happens when it gets destructed. In this case, the font object is probably getting deleted AFTER the rest of the juce system, font caches, etc have already been deleted, and the font object will expect to use those things internally… Bang.

Basically, unless you deeply understand all the possible dangers involved with C++ statics, then don’t ever use them! And if you DO fully understand them… you should still only use them if there’s absolutely no alternative!


#4

Hi SwingCoder, Jules - thanks for the advice, I see what’s going on now. I’ve replaced my const Font definitions with #define statements instead and now it all works splendidly.

In the example I gave, the use of a global is pretty silly, but that’s because I was simplifying the actual situation just to show where it goes wrong. For the reference of future users who stumble on this thread and may be in the same situation, what I was actually doing is this

const Font FONT_BOLD_STANDARD("Arial", 15, Font::bold);
const Font FONT_PLAIN_STANDARD("Arial", 15, Font::plain);
const Font FONT_BOLD_LARGER("Arial", 25, Font::bold);
const Font FONT_PLAIN_LARGER("Arial", 25, Font::plain);
//etc

class MyTextButton : public TextButton {
private:
    Font storedFont;

public:
	MyTextButton(String buttonName, String toolTip, Font f) :TextButton(buttonName, toolTip) { storedFont = f; }
	void setFont(Font f) { storedFont = f; }
	Font getFont() { return storedFont; }
};

And then, at various points in my code, I can do this

MyTextButton normal("Foo","Bar",FONT_PLAIN_STANDARD);
MyTextButton bold("Foo","Bar",FONT_BOLD_STANDARD);

etc. Then, changing FONT_BOLD_STANDARD in the header changes it everywhere. It works if I just change to

#define FONT_BOLD_STANDARD Font("Arial", 15, Font::bold)
#define FONT_PLAIN_STANDARD Font("Arial", 15, Font::plain)
#define FONT_BOLD_LARGER Font("Arial", 25, Font::bold)
#define FONT_PLAIN_LARGER Font("Arial", 25, Font::plain)

because of the aforementioned issues with the objects being destructed after JUCE shuts down and etc.


#5

It’s very bad style to use macros for stuff like that. Always use C++ instead, e.g.

struct StandardFonts { static Font getBoldFont() { return Font ("Arial", 15); } ... etc


#6

Ah, very good idea! I couldn’t figure out how to do it using actual C++ without running into the problem above. I’ll do that then.