views:

1298

answers:

5

If I use TextRenderer.DrawText() using the Graphics object provided in the OnPaintBackground my text looks perfect. If I create my own Bitmap and use the Graphics object obtained from my Bitmap my text looks terrible. It looks like it is anti-aliasing the text using black, not the bitmap's background color. I can avoid this problem if I use Graphics.DrawString(), but this method has horrible kerning problems. What should I do? How can I get TextRenderer.DrawText() to anti-alias properly using the Bitmap's contents?

Looks terrible:

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

Looks good, but I want to render this onto a bitmap, NOT onto the control's surface:

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

What is the difference?

A: 

If your bitmap is not the same size as your display area, it might just be a resizing issue, where .NET scales the bitmap to the display size and you get funny looking text.

Can you test with a bitmap created at the same size as your display area?

Mike
They are both the same size and DPI.
Jon Tackabury
I'm guessing then that as bitmaps don't have built in transparency, you might try using a different Image class as your in-memory canvas. The Color.Transparent is probably being interpreted as Color.Black in this case. Worth a try?
Mike
I have tried it using Image or Bitmap, and using a variety of different PixelFormats. The real issue is that TextRenderer isn't anti-aliasing the text with what is in the Bitmap, but instead with the color black (for some reason).
Jon Tackabury
A: 

Can you post the smallest program that suffers from this problem? I can't reproduce it like this -- the antialiasing looks fine:

    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);
 }
}
Tim Robinson
Try adding this line: g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; Also, if you use "Color.LightBlue" as the background the problem is much more visible. It is the text-antialiasing that messes it up.
Jon Tackabury
Also, it doesn't matter if "Application.SetCompatibleTextRenderingDefault(false);" is set to true or false.
Jon Tackabury
+1  A: 

The answer is not to use TextRenderer. TextRenderer is a wrapper for the GDI implementation of text rendering, which has lots of features, but doesn't interoperate well with in-memory DCs as you have discovered.

Use Graphics.DrawString & Graphics.MeasureString, but remember to pass it StringFormat.GenericTypographic to get accurate size and positioning.

The reason TextRenderer was introduced initially was that GDI+ didn't support all the complex scripts that GDI's Uniscribe engine did. Over time however GDI+ support for complex scripts has been expanded, and these days the only reason you may still want to use TextRenderer is that it can be faster.

Really, though, unless you are running into serious, measurable performance issues just use Graphics.DrawString.

romkyns
A: 

Another possible solution: Draw the whole thing to the screen, bitmap with text on top, and then write some code to 'screen capture' that portion of the screen. Not practical in all cases but you're right, DrawString creates weird text and DrawText onto a bitmap looks horrible.

Michael
A: 

I believe the problem is that the clear type text rendering doesn't work if the background is transparent. A few possible solutions.

Option 1. Fill the background of your bitmap with a color.

If you do this (as Tim Robinson did above in his code example by using g.Clear(Color.Red)) clear type will do the right thing. But your bitmap won't be completely transparent which might not be acceptable. If you use Graphics.MeasureText, you can fill just the rectangle around your text, if you like.

Option 2. Set TextRenderingHint = TextRenderingHintAntiAliasGridFit

This appears to turn off clear type. The text will be rendered at a lower quality than clear type on a background, but much better than the mess clear type on no background creates.

Option 3. Fill the text rectangle with white, draw the text and then find all the non-text pixels and put them back to transparent.

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 (c.A == 255 && c.R == 255 && c.G == 255 && c.B == 255)
                {
                    bmp.SetPixel(x, y, Color.Transparent);
                }
            }
        }
    }
}

I know, it's a horrible hack, but it appears to work.

Matthew Hess