PDA

View Full Version : Improved font rendering for IPlug (Win)


bvesco
01-19-2009, 01:11 AM
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.


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::DrawIText 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::DrawIText 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::DrawIText 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.

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.

stixsmith
01-22-2009, 10:54 AM
The approach seems sound, and perfectly logical - it is how I would attack the problem myself. I hope that schwa also approves?


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.


Obviously there would be some impact to performance - but probably too small to worry?

schwa
01-22-2009, 04:49 PM
Great mod. I don't think there's any meaningful cost to SelectObject for an existing font (it's probably just a quick lookup). Nice work!

Even better :) would be using the new LICE_IFont objects, which wrap Windows fonts but should be portable to OSX as well. I'll do that if, er, I ever do it.

bvesco
01-22-2009, 07:38 PM
Ah, thanks for the tip. I didn't know about the IFont objects. I would eventually like to port my plugs to Mac so there would be a gain there for me as well. I may take a look at those some time soon.

ahmedwali
03-12-2009, 10:08 PM
Hello,
Where to put these code? it will work on a website?

GregHolmes
12-16-2009, 01:23 PM
I'm not sure what the state of this is at this time, but my eye caught something:

I noticed that the code in bvesco's IGraphicsWin::DrawIText modification does not save and restore the return value from SelectObject(pDC, font).

The MS docs say: "An application should always replace a new object with the original, default object after it has finished drawing with the new object."

"SAVE-[doSomething]-RESTORE" was always a mantra of mine.

Tale
07-11-2011, 10:57 PM
Resurrecting an old topic, but I have implemented LICE_IFont for drawing text, replacing the platform-dependent DrawIText() methods.

Multiple fonts/sizes/styles can be used on both Windows and Mac OS X.
Alpha channel is respected on both Windows and Mac OS X.
Font sizes and coordinates on Mac OS X pretty much match those on Windows.
Fonts are cached the same way bitmaps are cached.
A pointer to a font in the cache is stored with the IText object, and it is reused in subsequent calls to DrawIText(). This only works if the IText object is persistant, e.g. if it is stored in a (static) member variable.
By default a font is added to the cache the first time DrawIText() is called. Alternatively you can preload a font using PrepDrawIText().

The changes are in my Git repository:

d40091d IPlug: Better font support through LICE_IFont.
463cbb7 IPlug: Changed project files for new font support.
ba5cb1 IPlugExample: Added advapi32.lib to Makefile.msc.

You only need the 1st commit if you edit your project file(s) yourself (add lice_textnew.cpp and advapi32.lib).

olilarkin
07-12-2011, 01:53 AM
interesting...

how similar did you get it on windows and osx? I did a similar mod but the font rendering was very different so I decided to use freetype. Can you post a screenshot?

Tale
07-12-2011, 06:32 AM
For most fonts it is close, but not exact. Here is a licecap of Arial, Times New Roman, and Verdana:

http://www.taletn.com/temp/20110712-iplug_fonts.gif

But it very much depends on the font used. I have also tried Courier New and Comic Sans MS, and Courier New was slightly too small while Comic Sans MS was slightly too big.

Tale
07-14-2011, 02:42 PM
I have slightly updated my new DrawIText() using LICE_IFont. It now uses native rendering when possible (which I assume/hope is more efficient), and fonts are automatically resized on Mac OS X.

This is what I get now:

http://www.taletn.com/temp/20110714-iplug_fonts.gif

olilarkin
07-16-2011, 02:15 AM
that's pretty similar i'd say. I guess it's not possible to turn antialiasing on or off - on win it depends on if you enable cleartype right? I'm going to try and use your new IGraphics code with freetype

Tale
07-16-2011, 06:58 AM
I guess it's not possible to turn antialiasing on or off - on win it depends on if you enable cleartype right?
Actually on Windows it is possible to control anti-aliasing for fonts. I have added a member variable (mQuality) to the IText struct, which is passed on as the quality setting to the CreateFont() WinAPI function. That was easy! :)

However, this didn't work on Mac OS X, because SWELL's CreateFont() ignored the quality setting. So I have added/enabled anti-aliasing control in SWELL as well, both for ATSUI and NSString font drawing.

Anyway, the changes are in my WDL repository:

eb1e030 IPlug: Added anti-aliasing control for fonts.
9bf0337 SWELL: Enabled anti-aliasing control for fonts.

cerberus
08-08-2011, 07:51 AM
...the changes are in my WDL repository:

eb1e030 IPlug: Added anti-aliasing control for fonts.
9bf0337 SWELL: Enabled anti-aliasing control for fonts.
i've had some pretty ugly bouts with aliasing and IText lately
http://cerberusaudio.com/a/mouseo7.gif
not to mention my own windows xp did not have antialiased
fonts enabled until i saw my own plug-in in the raw there...
i will give this a whirl... thanks tale!

edit: it seems like i would need to merge some
other major changes. perhaps more than i can
handle at once... hopefully i will reach that
level of confidence at some point.