Feb 032009
 

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 you are restricted to boring text. Here is a solution for better font rendering on Windows.

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 memory 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
{
public:
    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();
    }
};

There is also a mod necessary for 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.

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

From this valuable VST developer discussion at the WDL Forum.

VST plugin development is fun!

Sorry, the comment form is closed at this time.