The font rendering in IPlug is a bit lacking. Out of the box, all text on a plugin must be the same style, size and weight. This practically forces you to use rendered text on the skin of your plugin, or have boring text. Here is where I'm at on my current quest for improved, native font rendering.
The first part of the strategy is to have all IText objects hold their own HFONT handle. The original limitation of single font is caused by the tendency to leak font handles if changing were allowed. So we track each font handle on its assocaited IText. The IText is responsible for destroying its own font handle in the destructor. Yes, this results in some extra font handles floating around, but it is better than a leak and having a few extra to clean up is also better than being restricted to a single font in your entire plugin.
IText mods:
- added HFONT mHfont member
- added destructor
- added copy constructor
- added assignment operator
- added createFont method
- added copyMembers helper method
IText notes:
- all paths to constructing, copying, assigning an object lead to creation of a valid HFONT
- modifying a member directly (size, style, etc.) will put the mHfont member out of sync with object settings. You can call createFont to clean up the old font and resync. Fortunately, most common uses of IText in the framework result in copies being made of the font anyway, so the lazy programmer would likely never have to call createFont.
Code:
struct IText
{
char mFont[FONT_LEN];
HFONT mHfont; // vfxmod: added HFONT
int mSize;
IColor mColor;
enum EStyle { kStyleNormal, kStyleBold, kStyleItalic } mStyle;
enum EAlign { kAlignNear, kAlignCenter, kAlignFar } mAlign;
int mOrientation; // Degrees ccwise from normal.
IText(int size = DEFAULT_TEXT_SIZE, const IColor* pColor = 0, char* font = 0,
EStyle style = kStyleNormal, EAlign align = kAlignCenter, int orientation = 0)
: mSize(size), mColor(pColor ? *pColor : DEFAULT_TEXT_COLOR),
mStyle(style), mAlign(align), mOrientation(orientation), mHfont(0)
{
strcpy(mFont, (font ? font : DEFAULT_FONT));
createFont();
}
IText(const IColor* pColor)
: mSize(DEFAULT_TEXT_SIZE), mColor(*pColor), //mFont(DEFAULT_FONT),
mStyle(kStyleNormal), mAlign(kAlignCenter), mOrientation(0), mHfont(0)
{
strcpy(mFont, DEFAULT_FONT);
createFont();
}
// vfxmod: for windows font stuff, not needed on mac?
~IText()
{
DeleteObject(mHfont);
}
IText(const IText & source)
{
copyMembers(source);
}
IText& IText::operator=(const IText & source)
{
if (this != & source)
{
copyMembers(source);
}
return * this;
}
void createFont()
{
if (mHfont)
{
DeleteObject(mHfont);
mHfont = 0;
}
int h = mSize;
int esc = 10 * mOrientation;
int wt = (mStyle == IText::kStyleBold ? FW_BOLD : 0);
int it = (mStyle == IText::kStyleItalic ? 1 : 0);
mHfont = CreateFont(h, 0, esc, esc, wt, it, 0, 0, 0, 0, 0, 0, 0, mFont);
}
private:
void copyMembers(const IText & source)
{
strncpy(mFont, source.mFont, FONT_LEN);
mSize = source.mSize;
mColor = source.mColor;
mStyle = source.mStyle;
mAlign = source.mAlign;
mOrientation = source.mOrientation;
createFont();
}
};
I also modified IGraphicsWin:
rawIText to be compatible with the IText mods. The method had to be modified so it was not caching the font for the first IText rendered and using that font for all the rest of the IText renders. Now DrawIText retrieves the font handle from whatever IText property is currently being rendered, every time. If there is a performance penalty to calling SelectObject every time an IText is rendered then I have not been able to detect it. I believe this is a decent way to get around the problem.
IGraphics:
rawIText mods:
- mFontActive is no longer needed or used (it was only there to determine if we needed to create the SINGLE rendering font or not)
- now retrieves font handle from the IText it is about to render.
IGraphics:
rawIText possible improvement:
- If repeated calls to SelectObject turn out to be expensive we might be able to save a few by only calling it if the font handle is the same font handle that was selected last time.
Code:
bool IGraphicsWin::DrawIText(IText* pText, char* str, IRECT* pR)
{
if (!str || str == '\0') {
return true;
}
HDC pDC = mDrawBitmap->getDC();
bool setColor = (pText->mColor != mActiveFontColor);
HFONT font = pText->mHfont;
SelectObject(pDC, font);
SetBkMode(pDC, TRANSPARENT);
if (setColor)
{
SetTextColor(pDC, RGB(pText->mColor.R, pText->mColor.G, pText->mColor.B));
mActiveFontColor = pText->mColor;
}
UINT fmt = DT_NOCLIP;
switch(pText->mAlign)
{
case IText::kAlignCenter: fmt |= DT_CENTER; break;
case IText::kAlignFar: fmt |= DT_RIGHT; break;
case IText::kAlignNear:
default: fmt |= DT_LEFT; break;
}
RECT R = { pR->L, pR->T, pR->R, pR->B };
return !!DrawText(pDC, str, strlen(str), &R, fmt);
}
There is no thought given to the MacOS equivalent fix. I don't even know if the Mac version of the library suffers this limitation.
Please do critique the code and approach.