Tomas Kubes
Tomas Kubes

Reputation: 25108

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

Upvotes: 3

Views: 2211

Answers (2)

ghord
ghord

Reputation: 13797

It turns out that setting TextOptions.SetTextRenderingMode() on drawinVisual does not work. Accepted answer still has aliasing issues in the bitmap generated (it's better with TextFormattingMode.Display but still not exactly unaliased).

What worked for me was setting internal property on drawing visual:


    var visual = new DrawingVisual();
    var property = typeof(Visual).GetProperty("VisualTextRenderingMode", 
                BindingFlags.NonPublic | BindingFlags.Instance);

    property.SetValue(visual, TextRenderingMode.Aliased);

Upvotes: 0

Simon Mourier
Simon Mourier

Reputation: 138915

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

Upvotes: 3

Related Questions