Bitmap与OnPaintBackground中的TextRenderer.DrawText

如果我使用OnPaintBackground中提供的Graphics对象使用TextRenderer.DrawText(),我的文本看起来很完美。 如果我创建自己的Bitmap并使用从我的Bitmap获得的Graphics对象,我的文本看起来很糟糕。 看起来它使用黑色来消除锯齿,而不是位图的背景颜色。 如果我使用Graphics.DrawString(),我可以避免这个问题,但这种方法有可怕的字距调整问题。 我该怎么办? 如何使用Bitmap的内容正确地将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); } 

有什么不同?

答案是不要使用TextRenderer 。 TextRenderer是文本呈现的GDI(而不是GDI +)实现的包装器,它具有许多function,但是与您发现的内存DC不能很好地互操作。

使用Graphics.DrawStringGraphics.MeasureString ,但请记住将其传递给StringFormat.GenericTypographic以获得准确的大小和位置。

最初引入TextRenderer的原因是GDI +不支持GDI的Uniscribe引擎所做的所有复杂脚本。 然而,随着时间的推移,GDI +对复杂脚本的支持已经扩展,现在没有任何好的理由可以使用TextRenderer(它甚至不再是两者中更快的,事实上它恰好相反)。

但实际上,除非遇到严重的,可衡量的性能问题,否则只需使用Graphics.DrawString

我认为问题是如果背景是透明的,清晰类型的文本渲染不起作用。 一些可能的解决方案。

选项1.使用颜色填充位图的背景。

如果你这样做(正如Tim Robinson在他的代码示例中通过使用g.Clear(Color.Red)所做的那样)clear type将做正确的事情。 但是你的位图不会完全透明,这可能是不可接受的。 如果您使用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 (cA == 255 && cR == 255 && cG == 255 && cB == 255) { bmp.SetPixel(x, y, Color.Transparent); } } } } } 

我知道,这是一个可怕的黑客,但它似乎工作。

答案是使用BuffersGraphicsContext 当您在ControlStyles.OptimizedDoubleBuffer上设置ControlStyles.OptimizedDoubleBuffer样式时,这与.NET在内部使用的系统相同。

有关.NET中双缓冲的更多信息,请参见http://msdn.microsoft.com/en-us/library/b367a457.aspx 。

另一种可能的解决方案:将整个事物绘制到屏幕上,在顶部显示文本的位图,然后将一些代码写入“屏幕捕获”屏幕的那一部分。 在所有情况下都不实用,但你是对的,DrawString创建奇怪的文本,DrawText到位图看起来很糟糕。

如果您的位图与显示区域的大小不同,那么它可能只是一个resize的问题,.NET会将位图缩放到显示大小,并且您会看到有趣的文本。

您可以使用与显示区域相同大小创建的位图进行测试吗?

你能发布遭受这个问题的最小程序吗? 我无法像这样重现它 – 抗锯齿看起来很好:

  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); } }