COCKOS
CONFEDERATED FORUMS
Cockos : REAPER : NINJAM : Forums
Forum Home : Register : FAQ : Members List : Search :
Old 01-19-2009, 01:11 AM   #1
bvesco
Human being with feelings
 
bvesco's Avatar
 
Join Date: Jun 2007
Posts: 125
Default Improved font rendering for IPlug (Win)

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.
__________________
Audio tutorials and articles: http://www.benvesco.com/tonemonster/
My VST plugins: http://www.vescofx.com/
bvesco is offline   Reply With Quote
Old 01-22-2009, 10:54 AM   #2
stixsmith
Human being with feelings
 
Join Date: Dec 2008
Location: UK
Posts: 7
Default Re: Improved font rendering for IPlug (Win)

The approach seems sound, and perfectly logical - it is how I would attack the problem myself. I hope that schwa also approves?

Quote:
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?
stixsmith is offline   Reply With Quote
Old 01-22-2009, 04:49 PM   #3
schwa
Administrator
 
schwa's Avatar
 
Join Date: Mar 2007
Location: NY
Posts: 11,867
Default

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.
schwa is offline   Reply With Quote
Old 01-22-2009, 07:38 PM   #4
bvesco
Human being with feelings
 
bvesco's Avatar
 
Join Date: Jun 2007
Posts: 125
Default

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.
__________________
Audio tutorials and articles: http://www.benvesco.com/tonemonster/
My VST plugins: http://www.vescofx.com/
bvesco is offline   Reply With Quote
Old 03-12-2009, 10:08 PM   #5
ahmedwali
Banned
 
Join Date: Mar 2009
Posts: 2
Default Hi

Hello,
Where to put these code? it will work on a website?
ahmedwali is offline   Reply With Quote
Old 12-16-2009, 01:23 PM   #6
GregHolmes
Human being with feelings
 
GregHolmes's Avatar
 
Join Date: Sep 2009
Location: Ontario, Canada
Posts: 399
Default

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:rawIText 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.
__________________
Greg Holmes | play:GregHolmes.com | work:GHServices.com
GregHolmes is offline   Reply With Quote
Old 07-11-2011, 10:57 PM   #7
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,223
Default Better font support through LICE_IFont

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).
Tale is offline   Reply With Quote
Old 07-12-2011, 01:53 AM   #8
olilarkin
Human being with feelings
 
Join Date: Apr 2009
Location: Berlin, Germany
Posts: 1,243
Default

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?
__________________
VirtualCZ | Endless Series | iPlug2 | Linkedin | Facebook
olilarkin is offline   Reply With Quote
Old 07-12-2011, 06:32 AM   #9
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,223
Default

For most fonts it is close, but not exact. Here is a licecap of Arial, Times New Roman, and Verdana:



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 is offline   Reply With Quote
Old 07-14-2011, 02:42 PM   #10
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,223
Default

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:

Tale is offline   Reply With Quote
Old 07-16-2011, 02:15 AM   #11
olilarkin
Human being with feelings
 
Join Date: Apr 2009
Location: Berlin, Germany
Posts: 1,243
Default

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
__________________
VirtualCZ | Endless Series | iPlug2 | Linkedin | Facebook
olilarkin is offline   Reply With Quote
Old 07-16-2011, 06:58 AM   #12
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,223
Default

Quote:
Originally Posted by olilarkin View Post
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.
Tale is offline   Reply With Quote
Old 08-08-2011, 07:51 AM   #13
cerberus
Human being with feelings
 
Join Date: Nov 2009
Location: memory
Posts: 634
Default

Quote:
Originally Posted by Tale View Post
...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

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.

Last edited by cerberus; 08-09-2011 at 12:57 AM.
cerberus is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -7. The time now is 04:59 AM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2021, vBulletin Solutions Inc.