Bad text rendering using DrawString on top of transparent pixels
Asked Answered
R

4

34

When rendering text into a bitmap, I find that text looks very bad when rendered on top of an area with non-opaque alpha. The problem is progressively worse as the underlying pixels become more transparent. If I had to guess I'd say that when underlying pixels are transparent, the text renderer draws any anti-aliased 'gray' pixels as solid black.

Here are some screenshots:

Text drawn on top of transparent pixels:

alt text

Text drawn on top of semi-transparent pixels:

alt text

Text drawn on opaque pixels:

alt text

Here is the code used to render the text:

g.SmoothingMode = SmoothingMode.HighQuality;
g.DrawString("Press the spacebar", Font, Brushes.Black, textLeft, textTop);
Rouault answered 7/6, 2010 at 16:56 Comment(4)
I believe the result will also depend if ClearType is enabled or not.Stylish
Looks you are not 'clearing' (or rather invalidating) the transparent background.Albacore
any final solution with full source code ?Shawndashawnee
Are you able to upload the images you included to Stack Overflow? They're not coming down from Dropbox anymore.Asceticism
I
40

The option I used to workaround this problem was:

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

There are some others useful options in TextRenderingHint

Hope it helps

Indiscrete answered 16/8, 2011 at 13:27 Comment(3)
Swapped answer for this one, although this is a very old question and I've not tested this answer.Rouault
user3470185's answer (g.TextRenderingHint = Drawing.Text.TextRenderingHint.AntiAliasGridFit) produces better result.Apophyllite
That works for the control I'm modding (But I used TextRenderingHint.ClearTypeGridFit just to fix the default bad hinting applied to that control text)Flin
I
22

There is a very simple answer to this...

g.TextRenderingHint = Drawing.Text.TextRenderingHint.AntiAliasGridFit

If you set this before you render your text, it will come out clear. In addition, this methods supports more font sizes (The default only goes up to size 56).

Thanks for reading this post.

Ipsambul answered 2/8, 2014 at 17:7 Comment(0)
S
14

The first output is what you get when you draw black text on a black background, probably Color.Transparent. The 2nd was drawn on an almost-black background. The 3rd was drawn on the same background it is being displayed with.

Anti-aliasing cannot work when on a transparent background. The colors used for the anti-aliasing pixels will not blend the letter shape into the background when the text is displayed with a different background. Those pixels will now become very noticeable and make the text look very bad.

Note that SmoothingMode doesn't affect text output. It will look slightly less bad if you use a lower quality TextRenderingHint and a background color that's grayish with a alpha of zero. Only TextRenderingHint.SingleBitPerPixelGridFit avoids all anti-aliasing troubles.

Getting a perfect fix for this is very difficult. Vista's glass effect on the window title bar uses very subtle shading to give the text a well defined background color. You'd need SysInternals' ZoomIt tool to really see it. DrawThemeTextEx() function with a non-zero iGlowSize.

Specie answered 7/6, 2010 at 17:16 Comment(0)
D
1

If you're looking for something that preserves antialiasing a bit better than GDI+ does by default, you can call Graphics.Clear with a chroma key, then manually remove the chroma artifacts that result. (See Why does DrawString look so crappy? and Ugly looking text problem.)

Here's how I ultimately ended up solving a similar 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);
  }
}

It's a shame GDI/GDI+ doesn't do this already, but that would be sensible, wouldn't it? :)

If you aren't able to use an unsafe context, you could easily use the same logic with Bitmap.GetPixel and Bitmap.SetPixel, though it will be significantly slower.

Dornick answered 1/8, 2014 at 22:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.