33

将文本渲染为位图时,我发现在具有非不透明 alpha 的区域顶部渲染时,文本看起来很糟糕。随着底层像素变得更加透明,问题越来越严重。如果我不得不猜测,我会说当底层像素是透明的时,文本渲染器会将任何抗锯齿的“灰色”像素绘制为纯黑色。

以下是一些截图:

在透明像素上绘制的文本:

替代文字

在半透明像素上绘制的文本:

替代文字

在不透明像素上绘制的文本:

替代文字

这是用于呈现文本的代码:

g.SmoothingMode = SmoothingMode.HighQuality;
g.DrawString("Press the spacebar", Font, Brushes.Black, textLeft, textTop);
4

4 回答 4

37

我用来解决此问题的选项是:

Graphics graphics = new Graphics();
graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit;

TextRenderingHint 中还有一些其他有用的选项

希望能帮助到你

于 2011-08-16T13:27:52.867 回答
20

有一个非常简单的答案...

g.TextRenderingHint = Drawing.Text.TextRenderingHint.AntiAliasGridFit

如果在渲染文本之前设置它,它会很清晰。此外,此方法支持更多的字体大小(默认仅达到 56 号)。

感谢您阅读这篇文章。

于 2014-08-02T17:07:02.683 回答
14

第一个输出是在黑色背景上绘制黑色文本时得到的结果,可能是 Color.Transparent。第二幅画在几乎是黑色的背景上。第三个是在与它显示的相同背景上绘制的。

在透明背景上时,抗锯齿无法工作。当文本以不同的背景显示时,用于抗锯齿像素的颜色不会将字母形状混合到背景中。这些像素现在将变得非常明显,并使文本看起来很糟糕。

请注意,SmoothingMode 不会影响文本输出。如果您使用质量较低的 TextRenderingHint 和灰色且 alpha 为零的背景颜色,它看起来会稍微不那么糟糕。只有 TextRenderingHint.SingleBitPerPixelGridFit 可以避免所有抗锯齿问题。

对此进行完美修复非常困难。Vista 在窗口标题栏上的玻璃效果使用非常微妙的阴影来为文本提供明确定义的背景颜色。您需要 SysInternals 的 ZoomIt 工具才能真正看到它。具有非零 iGlowSize 的 DrawThemeTextEx() 函数。

于 2010-06-07T17:16:53.077 回答
2

如果您正在寻找比 GDI+ 默认情况下更好地保留抗锯齿的东西,您可以Graphics.Clear使用色度键调用,然后手动删除产生的色度伪影。(请参阅为什么 DrawString 看起来如此糟糕?丑陋的文字问题。)

以下是我最终解决类似问题的方法:

static Bitmap TextToBitmap(string text, Font font, Color foregroundColor)
{
  SizeF textSize;

  using ( var g = Graphics.FromHwndInternal(IntPtr.Zero) )
    textSize = g.MeasureString(text, font);

  var image = new Bitmap((int)Math.Ceiling(textSize.Width), (int)Math.Ceiling(textSize.Height));
  var brush = new SolidBrush(foregroundColor);

  using ( var g = Graphics.FromImage(image) )
  {
    g.Clear(Color.Magenta);
    g.SmoothingMode = SmoothingMode.AntiAlias;
    g.InterpolationMode = InterpolationMode.HighQualityBicubic;
    g.PixelOffsetMode = PixelOffsetMode.HighQuality;
    g.DrawString(text, font, brush, 0, 0);
    g.Flush();
  }

  image.MakeTransparent(Color.Magenta);

  // The image now has a transparent background, but around each letter are antialiasing artifacts still keyed to magenta.  We need to remove those.
  RemoveChroma(image, foregroundColor, Color.Magenta);
  return image;
}

static unsafe void RemoveChroma(Bitmap image, Color foregroundColor, Color chroma)
{
  if (image == null) throw new ArgumentNullException("image");
  BitmapData data = null;

  try
  {
    data = image.LockBits(new Rectangle(Point.Empty, image.Size), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);

    for ( int y = data.Height - 1; y >= 0; --y )
    {
      int* row = (int*)(data.Scan0 + (y * data.Stride));
      for ( int x = data.Width - 1; x >= 0; --x )
      {
        if ( row[x] == 0 ) continue;
        Color pixel = Color.FromArgb(row[x]);

        if ( (pixel != foregroundColor) &&
             ((pixel.B >= foregroundColor.B) && (pixel.B <= chroma.B)) &&
             ((pixel.G >= foregroundColor.G) && (pixel.G <= chroma.G)) &&
             ((pixel.R >= foregroundColor.R) && (pixel.R <= chroma.R)) )
        {
          row[x] = Color.FromArgb(
            255 - ((int)
              ((Math.Abs(pixel.B - foregroundColor.B) +
                Math.Abs(pixel.G - foregroundColor.G) +
                Math.Abs(pixel.R - foregroundColor.R)) / 3)),
            foregroundColor).ToArgb();
        }
      }
    }
  }
  finally
  {
    if (data != null) image.UnlockBits(data);
  }
}

很遗憾 GDI/GDI+ 还没有这样做,但那是明智的,不是吗?:)

如果你不能使用unsafe上下文,你可以很容易地使用与Bitmap.GetPixeland相同的逻辑Bitmap.SetPixel,尽管它会慢得多。

于 2014-08-01T22:23:28.657 回答