如何绘制独立于DPI的GDI +文本

时间:2014-08-26 22:10:56

标签: text gdi+ draw dpi

我正在使用GDI +绘制文本。我最近注意到,当DPI更改时,此文本会自动缩放。有没有办法使GDI +文本绘图独立于DPI?例如。无论DPI如何,我想绘制最多20像素的文本。可能吗?怎么做?

以下是示例代码。我想绘制具有常量大小的第一个文本,无论DPI如何,第二个文本通常是:

    case WM_PAINT:
    {
        inherited::WndProc(message);

        Canvas->Brush->Style = bsSolid;
        Canvas->Brush->Color = clWhite;
        Canvas->FillRect(ClientRect);

        // get GDI+ graphics from canvas
        Gdiplus::Graphics graphics(Canvas->Handle);

        // set text rendering hint
        graphics.SetTextRenderingHint(Gdiplus::TextRenderingHintSystemDefault);

        std::auto_ptr<Gdiplus::Font>         pFont(new Gdiplus::Font(Canvas->Handle, Font->Handle));
        std::auto_ptr<Gdiplus::SolidBrush>   pBrush(new Gdiplus::SolidBrush(Gdiplus::Color(255, 0, 0, 0)));
        std::auto_ptr<Gdiplus::StringFormat> pFormat(new Gdiplus::StringFormat());

        Gdiplus::FontFamily fontFamily;
        pFont->GetFamily(&fontFamily);

        std::auto_ptr<Gdiplus::Font> pFont2(new Gdiplus::Font(&fontFamily, pFont->GetSize(),
                pFont->GetStyle(), Gdiplus::UnitPixel));
        Gdiplus::Unit test = pFont->GetUnit();
        Gdiplus::Unit test2 = pFont2->GetUnit();

        pFormat->SetAlignment(Gdiplus::StringAlignmentNear);
        pFormat->SetLineAlignment(Gdiplus::StringAlignmentNear);

        Gdiplus::StringFormatFlags flags = Gdiplus::StringFormatFlagsBypassGDI;
        //flags = (Gdiplus::StringFormatFlags)(flags | Gdiplus::StringFormatFlagsDirectionRightToLeft);
        //flags = (Gdiplus::StringFormatFlags)(flags | Gdiplus::StringFormatFlagsDirectionVertical);
        //flags = (Gdiplus::StringFormatFlags)(flags | Gdiplus::StringFormatFlagsNoWrap);
        //flags = (Gdiplus::StringFormatFlags)(flags | Gdiplus::StringFormatFlagsNoClip);
        pFormat->SetFormatFlags(flags);

        pFormat->SetTrimming(Gdiplus::StringTrimmingEllipsisCharacter);
        pFormat->SetHotkeyPrefix(Gdiplus::HotkeyPrefixNone);

        std::wstring text = L"This is a sample code";

        Gdiplus::Unit prevPageUnit = graphics.GetPageUnit();

        try
        {
            graphics.SetPageUnit(Gdiplus::UnitPixel);

            // draw text
            graphics.DrawString(text.c_str(), text.length(), pFont2.get(), Gdiplus::RectF(ClientRect.Left,
                    ClientRect.Top, ClientWidth, ClientHeight), pFormat.get(), pBrush.get());
        }
        __finally
        {
            graphics.SetPageUnit(prevPageUnit);
        }

        // draw text 2
        graphics.DrawString(text.c_str(), text.length(), pFont.get(), Gdiplus::RectF(ClientRect.Left,
                ClientRect.Top + 25, ClientWidth, ClientHeight), pFormat.get(), pBrush.get());

        return;
    }

此致

2 个答案:

答案 0 :(得分:5)

我想提一些与您的问题略有不同的内容。你不应该再在GDI +中使用Graphics.DrawString了。它在.NET 2中被弃用。相反,Microsoft创建了TextRenderer.DrawString

在.NET中有两种绘制文本的方法:

  • GDI +(graphics.MeasureStringgraphics.DrawString
  • GDI(TextRenderer.MeasureTextTextRenderer.DrawText

在.NET 1.1中,所有内容都使用 GDI + 进行文本呈现。 But there were some problems

  
      
  • 由于GDI +的某种无状态特性会导致一些性能问题,其中设置上下文然后在每次调用后恢复原始内容。
  •   
  • 国际文本的整形引擎已针对Windows / Uniscribe和Avalon(Windows Presentation Foundation)多次更新,但尚未针对GDI +进行更新,这导致对新语言的国际渲染支持不具有相同级别质量。
  •   

所以他们知道他们想要改变.NET框架以停止使用 GDI + 的文本渲染系统,并使用 GDI 。起初他们希望他们可以简单地改变:

graphics.DrawString

调用旧的DrawText API而不是GDI +。但它们无法使文本包装和间距与GDI +完全匹配。所以他们被迫保留graphics.DrawString来调用GDI +(兼容性原因;正在调用graphics.DrawString的人会突然发现他们的文本没有像过去那样包装)。

创建了一个新的静态TextRenderer类来包装GDI文本呈现。它有两种方法:

TextRenderer.MeasureText
TextRenderer.DrawText
  

注意:
   - TextRenderer是GDI的包装纸    - graphics.DrawString仍然是GDI +

的包装器

然后出现了如何处理所有现有.NET控件的问题,例如:

  • Label
  • Button
  • TextBox

他们希望将它们切换为使用TextRenderer(即GDI),但他们必须小心。可能有人依赖于他们的控件绘制,就像在.NET 1.1中那样。因此诞生了“兼容的文本呈现”。

默认情况下,应用程序中的控件的行为与它们在.NET 1.1中的行为相同(它们是“兼容”)。

关闭兼容模式,请致电:

Application.SetCompatibleTextRenderingDefault(false);

这使您的应用程序更好,更快,具有更好的国际支持。总结一下:

SetCompatibleTextRenderingDefault(true)  SetCompatibleTextRenderingDefault(false)
=======================================  ========================================
 default                                  opt-in
 bad                                      good
 the one we don't want to use             the one we want to use
 uses GDI+ for text rendering             uses GDI for text rendering
 graphics.MeasureString                   TextRenderer.MeasureText
 graphics.DrawString                      TextRenderer.DrawText
 Behaves same as 1.1                      Behaves *similar* to 1.1
                                          Looks better
                                          Localizes better
                                          Faster

注意GDI + TextRenderingHint与用于GDI字体绘制的相应LOGFONT Quality之间的映射也很有用:

TextRenderingHint           mapped by TextRenderer to LOGFONT quality
========================    =========================================================
ClearTypeGridFit            CLEARTYPE_QUALITY (5) (Windows XP: CLEARTYPE_NATURAL (6))
AntiAliasGridFit            ANTIALIASED_QUALITY (4)
AntiAlias                   ANTIALIASED_QUALITY (4)
SingleBitPerPixelGridFit    PROOF_QUALITY (2)
SingleBitPerPixel           DRAFT_QUALITY (1)
else (e.g.SystemDefault)    DEFAULT_QUALITY (0)

样品

以下是GDI +(graphics.DrawString)与GDI(TextRenderer.DrawText)文本呈现的比较:

GDI + TextRenderingHintClearTypeGridFit GDI CLEARTYPE_QUALITY

enter image description here

GDI + TextRenderingHintAntiAlias GDI ANTIALIASED_QUALITY

enter image description here

GDI + TextRenderingHintAntiAliasGridFit GDI 不受支持,使用ANTIALIASED_QUALITY

enter image description here

GDI + TextRenderingHintSingleBitPerPixelGridFit GDI PROOF_QUALITY

enter image description here

GDI + TextRenderingHintSingleBitPerPixel GDI DRAFT_QUALITY

enter image description here

我觉得奇怪DRAFT_QUALITYPROOF_QUALITY相同,与CLEARTYPE_QUALITY相同。

另见

答案 1 :(得分:1)

这对我有用。

using namespace Gdiplus; 

HDC hDC = ::GetDC( NULL );
int nDPI = ::GetDeviceCaps( hDC, LOGPIXELSY );
::ReleaseDC( NULL, hDC );

REAL fFontHeight = 96 / (REAL)nDPI * 8;

FontFamily fontFamily( L"Arial" );
Gdiplus::Font font( &fontFamily, fFontHeight, UnitPixel );

REAL fMeasuredFontHeight = font.GetHeight( &gr );

事实证明,Gdiplus :: Font虽然以像素为单位指定,但却使用用户的DPI设置来调整生成的字体(即使字体用于在位图中绘制!)。 96的标准DPI是一个很好的值,可以确定正确的比例来调整字体大小。

在上面的代码片段中,所寻求的字体高度为8像素高。

通过所有DPI设置,fMeasuredFontHeight几乎保持不变(大约12)。