【问题标题】:Bad text rendering using DrawString on top of transparent pixels在透明像素上使用 DrawString 渲染错误的文本
【发布时间】:2011-02-28 19:23:27
【问题描述】:

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

以下是一些截图:

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

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

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

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

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

【问题讨论】:

  • 我相信结果也将取决于是否启用了 ClearType。
  • 看起来你没有“清除”(或者更确切地说是无效)透明背景。
  • 有完整源代码的最终解决方案吗?
  • 您能否将您包含的图片上传到 Stack Overflow?他们不再从 Dropbox 下来了。

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


【解决方案1】:

我用来解决这个问题的方法是:

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

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

希望对你有帮助

【讨论】:

  • 交换了这个答案,虽然这是一个非常古老的问题,我还没有测试过这个答案。
  • user3470185 的回答 (g.TextRenderingHint = Drawing.Text.TextRenderingHint.AntiAliasGridFit) 产生更好的结果。
  • 这适用于我正在修改的控件(但我使用 TextRenderingHint.ClearTypeGridFit 只是为了修复应用于该控件文本的默认错误提示)
【解决方案2】:

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

g.TextRenderingHint = Drawing.Text.TextRenderingHint.AntiAliasGridFit

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

感谢您阅读这篇文章。

【讨论】:

    【解决方案3】:

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

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

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

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

    【讨论】:

    • 呃。我想我可以在临时位图上将黑色文本绘制到白色上,将白色转换为透明,将灰色转换为半透明黑色,然后将其绘制到最终位图。我会尝试不同的 TextRenderingHints。否则,我认为部分背景透明度的效果还不错。但有一件事 - 在每种情况下,背景都是相同的颜色(灰白色)但透明度不同,所以我不希望文本以黑色的“有效”背景颜色绘制。
    • 不确定你的意思。关键问题是 GDI 看不到透明度,它看到的是透明颜色的 RGB 值。 Color.Transparent 是一个非常糟糕的选择,它的 RGB 值为零。黑色。
    • 我的意思是背景是用灰白色绘制的,带有一个 alpha 分量。不知道你从哪里得到 Color.Transparent。
    • @mack - 是的,对于在计算机上花费太多时间的人来说,这是一种常见的责任。它不在家庭中运行,好东西。请求很简单,请去掉答案标记。
    • +1 用于解决此问题的 SingleBitPerPixelGridFit 指针 - 谢谢。
    【解决方案4】:

    如果您正在寻找比 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 的相同逻辑,但速度会慢很多。

    【讨论】:

      猜你喜欢
      • 2011-05-29
      • 2012-07-26
      • 2018-03-27
      • 2011-03-13
      • 2012-07-27
      • 1970-01-01
      • 2018-01-23
      • 1970-01-01
      • 2013-03-07
      相关资源
      最近更新 更多