在位图中使用TextRenderer.DrawText与在OnPaintBackground中使用的区别

8
如果我使用OnPaintBackground提供的Graphics对象,使用TextRenderer.DrawText()方法,我的文本看起来完美无缺。但如果我创建自己的Bitmap并使用从自己的Bitmap获得的Graphics对象,则文本看起来非常糟糕。它看起来像是使用黑色对文本进行抗锯齿处理,而不是位图的背景颜色。我可以避免这个问题,如果我使用Graphics.DrawString()方法,但这种方法具有可怕的字距问题。我该怎么做?如何让TextRenderer.DrawText()方法在使用位图内容时正确地进行抗锯齿处理?

看起来非常糟糕:

Bitmap bmp = new Bitmap(100, 100, PixelFormat.Format32bppArgb);
using (Graphics g = Graphics.FromImage(bmp))
{
    g.Clear(Color.Red);
    TextFormatFlags tf = TextFormatFlags.Left;
    TextRenderer.DrawText(g, @"C:\Development\Testing\blag", font, clip, Color.White,
                          Color.Transparent, tf);
}

看起来不错,但我想把它渲染到位图上,而不是控件表面:

protected override void OnPaintBackground(PaintEventArgs e)
{
    e.Graphics.Clear(Color.Red);
    TextFormatFlags tf = TextFormatFlags.Left;
    TextRenderer.DrawText(e.Graphics, @"C:\Development\Testing\blag", font, clip,
                          Color.White, Color.Transparent, tf);
}

有什么区别?

6个回答

9
答案是不要使用TextRenderer。 TextRenderer是文本呈现的GDI(而不是GDI+)实现的包装器,它具有许多功能,但与您发现的内存DCs的互操作性不佳。
使用Graphics.DrawStringGraphics.MeasureString,但记得传递StringFormat.GenericTypographic以获得准确的大小和定位。
TextRenderer最初被引入的原因是,GDI+不支持GDI的Uniscribe引擎支持的所有复杂脚本。然而,随着时间的推移,GDI+对复杂脚本的支持已经扩展,现在没有任何好的理由使用TextRenderer了(事实上,相反,它似乎甚至不是两者中更快的一个)。
但是,如果您没有遇到严重的、可衡量的性能问题,只需使用Graphics.DrawString即可。

“TextRenderer更快”?你确定吗?这篇文章说它比GDI+更慢:http://theartofdev.com/2014/04/21/text-rendering-methods-comparison-or-gdi-vs-gdi-revised/ - qakmak
@qakmak 这是9年前的事情了,但我猜它可能已经改变了。你链接文章中的代码也显示DrawString在我的机器上速度更快。 - Roman Starkov

3
我认为问题在于在背景透明的情况下,清晰度文本渲染无法正常工作。以下是几种可能的解决方案:
选项1. 用颜色填充位图的背景。
如果您这样做(就像Tim Robinson在他的代码示例中使用g.Clear(Color.Red)一样),清晰度将会得到正确呈现。但您的位图不会完全透明,这可能是不可接受的。如果您使用Graphics.MeasureText,您可以只填充文本周围的矩形区域,如果您愿意的话。
选项2. 设置TextRenderingHint = TextRenderingHintAntiAliasGridFit
这似乎会关闭清晰度。文本将以比在背景上清晰度渲染低的质量进行呈现,但比清晰度在无背景时创建的混乱好得多。
选项3. 使用白色填充文本矩形,绘制文本,然后找到所有非文本像素并将它们变成透明。
using (Bitmap bmp = new Bitmap(someWidth, someHeight))
{
    using (Graphics g = Graphics.FromImage(bmp))
    {
        // figure out where our text will go
        Point textPoint = new Point(someX, someY);
        Size textSize = g.MeasureString(someText, someFont).ToSize();
        Rectangle textRect = new Rectangle(textPoint, textSize);

        // fill that rect with white
        g.FillRectangle(Brushes.White, textRect);

        // draw the text
        g.DrawString(someText, someFont, Brushes.Black, textPoint);

        // set any pure white pixels back to transparent
        for (int x = textRect.Left; x <= textRect.Left + textRect.Width; x++)
        {
            for (int y = textRect.Top; y <= textRect.Top + textRect.Height; y++)
            {
                Color c = bmp.GetPixel(x, y);
                if (c.A == 255 && c.R == 255 && c.G == 255 && c.B == 255)
                {
                    bmp.SetPixel(x, y, Color.Transparent);
                }
            }
        }
    }
}

我知道,这是一个可怕的hack,但它似乎起作用了。

2

0
另一个可能的解决方案是:将整个内容绘制到屏幕上,包括文本的位图,然后编写一些代码来“屏幕截图”该部分内容。虽然不是所有情况下都实用,但您说得对,DrawString会产生奇怪的文本,而在位图上绘制文本看起来很糟糕。

-1
你能发一下最小的程序,让我看看这个问题吗?我无法像这样重现它 -- 抗锯齿效果看起来很好:
    using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;

public class Program
{
    public static void Main()
    {
        Bitmap bmp = new Bitmap(100, 100, PixelFormat.Format32bppArgb);
        using (Font font = new Font("Arial", 10, GraphicsUnit.Point))
        using (Graphics g = Graphics.FromImage(bmp))
        {
            Rectangle clip = Rectangle.FromLTRB(0, 0, 100, 100);
            g.Clear(Color.Red);
            TextFormatFlags tf = TextFormatFlags.Left;
            TextRenderer.DrawText(g, @"C:\Development\Testing\blag", font, clip, Color.White, Color.Transparent, tf);
        }

        Form form = new Form();
        form.BackgroundImage = bmp;
        Application.Run(form);
    }
}

2
尝试添加这行代码:g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; 另外,如果您使用“Color.LightBlue”作为背景色,则问题会更加明显。这是由于文本抗锯齿功能引起的混乱。 - Jon Tackabury
此外,“Application.SetCompatibleTextRenderingDefault(false);”设置为true或false都没有关系。 - Jon Tackabury

-1
如果您的位图大小与显示区域不同,可能只是一个调整大小的问题,.NET会将位图缩放到显示大小,导致文本看起来很奇怪。
您能否使用与显示区域相同大小创建的位图进行测试?

它们的尺寸和DPI都是相同的。 - Jon Tackabury
我猜想,由于位图没有内置透明度,您可以尝试使用不同的图像类作为内存画布。在这种情况下,Color.Transparent 可能被解释为 Color.Black。值得一试吗? - Mike
我已经尝试使用Image或Bitmap,并尝试了各种不同的PixelFormat。真正的问题在于TextRenderer并没有通过Bitmap来进行文本抗锯齿处理,而是使用颜色黑色(原因不明)。 - Jon Tackabury

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接