Search code examples
wpfdrawingantialiasing

Turn off anti-aliasing for drawing to bitmap in WPF


I rendering text to bitmap using WPF. I would like to turn off anti-aliasing, I mean I want the pixels be white or black. But the text is still blured, some pixels are even grey.

Here is my code. Some lines are probably not needed.

RenderTargetBitmap bm = new RenderTargetBitmap(bitmapWidth, bitmapHeight, dpiX, dpiY, PixelFormats.Pbgra32);

DrawingVisual drawing_visual = new DrawingVisual();

RenderOptions.SetEdgeMode(drawing_visual, EdgeMode.Unspecified);            
RenderOptions.SetBitmapScalingMode(drawing_visual, BitmapScalingMode.Linear);

RenderOptions.SetEdgeMode(bm, EdgeMode.Unspecified);
RenderOptions.SetBitmapScalingMode(bm, BitmapScalingMode.Linear);

DrawingContext drawing_context = drawing_visual.RenderOpen();            

FormattedText ft = new FormattedText("my text", CultureInfo.InvariantCulture, System.Windows.FlowDirection.LeftToRight, typeface, fontSize, Brushes.Black);
        drawing_context.DrawText(ft, new Point(0, 0));

drawing_context.Close();
bm.Render(drawing_visual); 

Rendered image:

blured image

To check the solution, you can download source code from GitHub:

https://github.com/qub1n/Font-rendering.git

Solution

  • The important notion here is the TextRenderingMode. It's logical, since you don't want anti-aliased text, you must set the TextRenderingMode to Aliased. The difficulty is where to put it...

    I recommend you create a DrawingImage instead, like this, as the start object (use it as your canvas source, instead of a bitmap):

        public static DrawingImage CreateImage(int heightPixel, Typeface typeface, double fontSize, string text)
        {
            var group = new DrawingGroup();
            using (var context = group.Open())
            {
                var ft = new FormattedText(text, CultureInfo.InvariantCulture, FlowDirection.LeftToRight, typeface, fontSize, Brushes.Black, null,
                    TextFormattingMode.Display); // this is the important line here!
                context.DrawText(ft, new Point(0, 0));
            }
    
            return new DrawingImage(group);
        }
    

    Note the also important TextFormattingMode value set to Display. If you don't do this and keep the default value (Ideal), then you'll still have anti-aliasing.

    Now, for rendering on the display, you will have to use TextRenderingMode on a Visual element, so in your example, on your canvas element:

        <controls:ImageCanvas ... TextOptions.TextRenderingMode="Aliased" ... />
    

    and for rendering on a bitmap, you'll have to create an intermediary Visual, here an Image object, so you can apply the TextRenderingMode on it:

        public static void SaveToFile(string filePath, DrawingImage drawing)
        {
            var image = new Image { Source = drawing };
            image.Arrange(new Rect(new Size(drawing.Width, drawing.Height)));
    
            // this is the important line here!
            TextOptions.SetTextRenderingMode(image, TextRenderingMode.Aliased);
    
            // note if you use a different DPI than the screen (96 here), you still will have anti-aliasing
            // as the system is pretty automatic anyway
            var bitmap = new RenderTargetBitmap((int)drawing.Width, (int)drawing.Height, 96, 96, PixelFormats.Pbgra32);
            bitmap.Render(image);
    
            using (var fileStream = new FileStream(filePath, FileMode.Create))
            {
                var encoder = new PngBitmapEncoder();
                encoder.Frames.Add(BitmapFrame.Create(bitmap));
                encoder.Save(fileStream);
            }
        }