在透明像素上使用DrawString进行错误文本渲染

时间:2010-06-07 16:56:11

标签: c# winforms gdi+ system.drawing drawstring

将文本渲染到位图时,我发现当在具有非不透明alpha的区域上渲染时,文本看起来非常糟糕。随着基础像素变得更加透明,问题逐渐恶化。如果我不得不猜测我会说当底层像素是透明的时,文本渲染器会将任何消除锯齿的“灰色”像素绘制为纯黑色。

以下是一些截图:

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

alt text

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

alt text

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

alt text

以下是用于呈现文本的代码:

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

4 个答案:

答案 0 :(得分:27)

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

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

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

希望有所帮助

答案 1 :(得分:15)

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

g.TextRenderingHint = Drawing.Text.TextRenderingHint.AntiAliasGridFit

如果在渲染文本之前设置此项,则会清楚显示。此外,此方法支持更多字体大小(默认值仅为56)。

感谢您阅读此帖。

答案 2 :(得分:12)

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

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

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

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

答案 3 :(得分:2)

如果你正在寻找比GDI +更好地保留抗锯齿的东西,你可以用色度键调用Graphics.Clear,然后手动删除产生的色度伪像。 (请参阅 Why does DrawString look so crappy? Ugly looking text problem 。)

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

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.GetPixelBitmap.SetPixel相同的逻辑,但速度会明显变慢。