3

I am writing C# code in VS2010 to achieve the following.

I am trying to draw text on a transparent bitmap and running into ugly looking text. Below is the pic of three texts (i.e. A, B and C)

Note: A and C have transparent background, B has White as background.enter image description here Problem:

A: I have a bitmap with ARGB pixel format. And I want to draw a text into it. The text drawn has sharp edges and it does not honor the transparency.

B: I write the exact same code, but only difference from A is that the pixels in bitmap are not transparent anymore. (I will fill in a rectangle). But if you notince the pixels of the text, you will see a smooth blend of black (text color) and white (background color).

C: This is what I am expecting the text to look when I am drawing a text onto a bitmap with transparent pixels.

public partial class UserControl1 : UserControl
{
    public UserControl1()
    {
        InitializeComponent();
    }

    private void UserControl1_Paint(object sender, PaintEventArgs e)
    {
        //e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(0, Color.Red)), e.ClipRectangle);
        e.Graphics.DrawString("0123456789", new Font("Verdana", 20), Brushes.Red, new PointF(5, 10));
        e.Graphics.DrawEllipse(Pens.Black, e.ClipRectangle);
    }

    // This is a public method, using which I want to save my usercontrol to an image to save to file later.
    public Image ToImage(Size size, PixelFormat p)
    {
        Bitmap b = new Bitmap(Width, Height, p);

        using (Graphics g = Graphics.FromImage(b))
        {
            g.SmoothingMode = SmoothingMode.AntiAlias;
            g.InterpolationMode = InterpolationMode.HighQualityBicubic;
            g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
            PaintEventArgs e = new PaintEventArgs(g, new Rectangle(0, 0, size.Width, size.Height));
            base.OnPaint(e);
            this.OnPaint(e);
        }

        return b;
    }

How do I make A look like C with the transparent background. I am pretty sure this can be done, because softwares like photoshop, paint.net handle this very nicely.

Thanks a lot!

Datte

dattebayo
  • 2,012
  • 4
  • 30
  • 40
  • Without posting some code, you're relying on people to help you without knowing anything you've tried. Rather than shoot down their answers one at a time as you try them, why don't you show us what you've done, so we can try to improve it or understand it? That'd go a long way towards helping you get a good answer than what you've got currently going on. – Bob G Mar 02 '11 at 13:35
  • Hey Bob, I've uploaded some code. The code is pretty straight forward. – dattebayo Mar 02 '11 at 14:08
  • Wow, now you are maybe looking for this: http://stackoverflow.com/questions/1784295/rendering-form-to-bitmap - every control has DrawToBitmap() method, and it automatically does what you need. – Daniel Mošmondor Mar 02 '11 at 14:49
  • Daniel, I really appreciate your reply. Thanks. The thing is, it is the bitmap that I really want to save, not the user control. So, when the text is drawn, it has the hard edges for the text. – dattebayo Mar 02 '11 at 17:14
  • Is there a Known Issue on MSDN regarding this? – dattebayo Mar 02 '11 at 17:15
  • @dattebayo, is this question nearly the same as yours? http://stackoverflow.com/questions/1291061/antialiased-text-on-transparent-bitmap – hometoast Mar 02 '11 at 18:16
  • @dattebayo - DrawToBitmap will 'convert' the control to the bitmap and you'll have what you need. – Daniel Mošmondor Mar 03 '11 at 13:46
  • I think the question is whether or not the resulting bitmap needs transparency. – snarf Jun 20 '11 at 23:21
  • I had the very same problem until now. For me the solution is `g.TextRenderingHint = TextRenderingHint.AntiAlias;`. The text looks fine now. – Bitterblue Mar 06 '14 at 11:07

3 Answers3

2

Drawing the text WON'T affect alpha channel on your target bitmap, and will always look like that if you draw to intermediate ARGB bitmap that you want to blend to some other bitmap.

What you need is to manipulate pixels of the bitmap after drawing the text, so you re-calculate alpha value for every pixel so it is equal to your grayscale text. You can do it either with GetPixel/PutPixel combination when traversing the bitmap (slow) or with unsafe portion of the code (unsafe, pointers, fast).

Daniel Mošmondor
  • 19,718
  • 12
  • 58
  • 99
1

What SmoothingMode are you using for your Graphics object?

If you haven't set it then try to change its value:

http://msdn.microsoft.com/en-us/library/system.drawing.graphics.smoothingmode.aspx

Rune Grimstad
  • 35,612
  • 10
  • 61
  • 76
  • Hi Nick, The smoothing mode is not helping me either. – dattebayo Mar 02 '11 at 13:29
  • @dattebayo: There are other options such as `TextRenderingHint` and `InterpolationMode`, have you test with these? – Grant Thomas Mar 02 '11 at 13:46
  • Hi there, InterpolationMode works only for primitives like lines, poligons etc. TextRenderingMode.Alias works, but it makes the font look blurry. Text with the gridFit textRenderingModes will have harsh edges and looks bad for large fonts. – dattebayo Mar 02 '11 at 14:06
  • I think it's about finding the correct combination of these modes for your scenario. – Grant Thomas Mar 02 '11 at 15:11
1

Looks like GDI+ does not support this out of the box. What you can do is render black text on a white background and then modify the pixel data to convert all pixels to black with an alpha value based on the original brightness.

    static void GrayscaleToAlpha(Bitmap image) {
        var lockData = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
        unsafe {
            // Pointer to the current pixel
            uint* pPixel = (uint*)lockData.Scan0;
            // Pointer value at which we terminate the loop (end of pixel data)
            var pLastPixel = pPixel + image.Width * image.Height;

            while (pPixel < pLastPixel) {
                // Get pixel data
                uint pixelValue = *pPixel;
                // Average RGB
                uint brightness = ((pixelValue & 0xFF) + ((pixelValue >> 8) & 0xFF) + ((pixelValue >> 16) & 0xFF)) / 3;

                // Use brightness for alpha value, leave R, G, and B zero (black)
                pixelValue = (255 - brightness) << 24;

                // Copy back to image
                *pPixel = pixelValue;
                // Next pixel
                pPixel++;
            }

        }
        image.UnlockBits(lockData);
    }
snarf
  • 2,684
  • 1
  • 23
  • 26