1

I'm using DrawingContext class to draw a bar graph, but the produced labels and ticks are blurry:

enter image description here

In the code I am using a class BarRenderer derived from FrameworkElement. Each bar is represented by a BarRenderer instance, and I do the drawing by overriding the OnRender method. Each "bar" has a label and a value where label is the text below and value defines the height of the bar. and The logic responsible for drawing labels and tick is as follows:

private void DrawLabel(DrawingContext drawingContext, FormattedText text, double x, double y, int direction)
{
    drawingContext.DrawLine(_blackPen, new Point(Width * 0.5, y), new Point(Width * 0.5, y + 6));

    y += 6;
    if (LabelRotationDegree != 0)
    {
        RotateTransform rotateTransform = new RotateTransform(-LabelRotationDegree, 0.5 * Width, y + text.Height * 0.5);
        TranslateTransform translateTransform = new TranslateTransform(0, direction * text.WidthIncludingTrailingWhitespace * 0.5);

        drawingContext.PushTransform(translateTransform);
        drawingContext.PushTransform(rotateTransform);
    }

    drawingContext.DrawText(text, new Point(x, y));

    if (LabelRotationDegree != 0)
    {
        drawingContext.Pop();
        drawingContext.Pop();
    }
}

I doubt that the issue is with the rotation transform, since when the labels are not transformed they are no longer as blurry as before:

enter image description here

But as can be seen some of the ticks are darker than the others. So what am I doing wrong?

EDIT:

I've already set SnapsToDevicePixels to true, and when I set RenderOptions.SetEdgeMode to Aliased some ticks disappear and text is still blurry:

enter image description here

Edit 2: More code

public class GraphUnitRenderer : FrameworkElement
{
    private const double TEXT_SIZE = 12;
    private const double KEY_LABELS_HEIGHT = 50;

    private readonly Typeface _typeface;
    private readonly CultureInfo _cultureInfo;
    private readonly Pen _blackPen;
    private readonly Pen _bluePen;
    private readonly Pen _tickBlackPen;
    private readonly Pen _whitePen;



    public BarData Data { get; set; }

    //Some properties

    public GraphUnitRenderer()
    {
        _typeface = new Typeface("Segoe UI");
        _cultureInfo = CultureInfo.CurrentCulture;

        _blackPen = new Pen(Brushes.Black, 1);
        _bluePen = new Pen(Brushes.Blue, 1);
        _tickBlackPen = new Pen(Brushes.Black, 2);
        _whitePen = new Pen(Brushes.White, 1);

        /*
        _blackPen.Freeze();
        _bluePen.Freeze();
        _tickBlackPen.Freeze();
        _whitePen.Freeze();
        */

        RenderOptions.SetEdgeMode(this, EdgeMode.Aliased);
        SnapsToDevicePixels = true;

    }

    protected override void OnRender(DrawingContext drawingContext)
    {
        TextOptions.SetTextFormattingMode(this, TextFormattingMode.Display);


        FormattedText keyText = GetText(Data.Key);

        //Bar drawing logic 

        if (LabelDisplayIsForced || (LabelRotationDegree == 0 && keyText.WidthIncludingTrailingWhitespace < Width) || (LabelRotationDegree != 0 && keyText.Height < Width))
            DrawLabel(drawingContext, keyText, 0.5 * (Width - keyText.WidthIncludingTrailingWhitespace), GetYPosition(1) + 1, 1);

    }


    private void DrawLabel(DrawingContext drawingContext, FormattedText text, double x, double y, int direction)
    {
        drawingContext.DrawLine(_blackPen, new Point(Width * 0.5, y), new Point(Width * 0.5, y + 6));

        y += 6;
        if (LabelRotationDegree != 0)
        {
            RotateTransform rotateTransform = new RotateTransform(-LabelRotationDegree, 0.5 * Width, y + text.Height * 0.5);
            TranslateTransform translateTransform = new TranslateTransform(0, direction * text.WidthIncludingTrailingWhitespace * 0.5);

            drawingContext.PushTransform(translateTransform);
            drawingContext.PushTransform(rotateTransform);
        }

        drawingContext.DrawText(text, new Point(x, y));

        if (LabelRotationDegree != 0)
        {
            drawingContext.Pop();
            drawingContext.Pop();
        }
    }


    private double GetYPosition(double position) => Height - position - KEY_LABELS_HEIGHT;
    private FormattedText GetText(string text) => new FormattedText(text, _cultureInfo, FlowDirection.LeftToRight, _typeface, TEXT_SIZE, Brushes.Black, VisualTreeHelper.GetDpi(this).PixelsPerDip);
}
Ayoub.A
  • 1,943
  • 3
  • 27
  • 34
  • This is due to sub-pixel rendering. You could try setting [SnapToDevicePixels](https://learn.microsoft.com/en-us/dotnet/api/system.windows.uielement.snapstodevicepixels) to true and [RenderOptions.EdgeMode](https://learn.microsoft.com/en-us/dotnet/api/system.windows.media.renderoptions.edgemode?view=netframework-4.8#System_Windows_Media_RenderOptions_EdgeMode) to Aliased. – Clemens Jan 15 '20 at 18:37
  • @Clemens, I already tried this, in fact when I set `RenderOptions.EdgeMode` to Aliased, some of the ticks disappear. – Ayoub.A Jan 15 '20 at 18:46
  • @Clemens, I have included the code in the question. Please notice that I commented pen freezing since I doubted it was the reason behind it. What do you think? – Ayoub.A Jan 15 '20 at 19:01
  • 1
    There's also [TextOptions.TextRenderingMode](https://learn.microsoft.com/en-us/dotnet/api/system.windows.media.textoptions.textrenderingmode?view=netframework-4.8). For the missing ticks, you may perhaps use a slightly wider Pen Thickness, e.g. 1.5. – Clemens Jan 15 '20 at 19:16
  • @Clemens, Te test is still blurry even when I set `TextOptions.TextRenderingMode` to Aliased. As for the ticks, when I increase the pen thickness, I get some of the ticks wider then others, like the picture without rotated labels in the question. Is there some other way to render this without falling in this issue of sub-pixel rendering and keep the performance high? – Ayoub.A Jan 16 '20 at 08:52

0 Answers0