DrawingContext.DrawLine: Pen has no full opacity?
Asked Answered
K

4

5

when I draw something like that (just random drawings here):

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        DrawingVisual visual = new DrawingVisual();
        DrawingContext context = visual.RenderOpen();

        Pen pen = new Pen(Brushes.Black, 1);

        context.DrawEllipse(Brushes.YellowGreen, pen, new Point(0,0), 40, 40);

        for (int i = 0; i <= 100 - 1; i++)
          context.DrawLine(new Pen(Brushes.Black, 1), new Point(0, i), new Point(i, i));

        context.Close();

        RenderTargetBitmap bmp = new RenderTargetBitmap(100, 100, 96, 96, PixelFormats.Pbgra32);

        bmp.Render(visual);
        image1.Source = bmp;
    }
}

the colors of DrawLine and DrawEllipse mix. (I figured out that it's only with DrawLine which uses a pen, and not with other forms like Rectangle and Ellipse, that use a Brush). Strangely even with colors from the LinearGradientBrush of a underlying Grids' Background (argh). I would like them to be z-Ordered with full opacity each.

Here the XAML code:

<Window x:Class="WpfApplication2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Image Name="image1" Stretch="None" />
</Window>

Thanks for any help.

Kongo answered 12/6, 2012 at 16:31 Comment(2)
I made a workaround by using DrawRectangle with a filling brush insted DrawLine with a pen. Still I wonder why Pens have no full opacity.Kongo
Pen has got full opacity, but in your case, lines are so thin that they got blurred and seem to be semi-transparent. Read my answer for more information and suggested solutions :). Drawing rectangles allow you to simulate only horizontal and vertical lines. Drawing lines, but with greater width should do the trick ;).Asclepius
M
5

There are two issues of antialiasing, or sub-pixeling, with RenderTargetBitmap:

1.

Disabling sub-pixeling for the bitmap itself (for example, when it is rendered within a UIElement). this is resolved by applying:

    RenderOptions.SetBitmapScalingMode(image1, BitmapScalingMode.NearestNeighbor);

(where image1 is the WPF Image object).

It is only supported for .NET 4 and above. In your specific case it doesn't matter as the lines are rendered pixel-after-pixel.

2.

Disabling sub-pixeling when rendering INTO the RenderTargetBitmap. It can be achieved by the method RenderOptions.SetEdgeMode with the parameter value of EdgeMode.Aliased.

HOWEVER, this method will work only if:

  • The method is called for a DrawingGroup object.

  • The antialiased geometry is nested only through regular drawing composite (for example, if the DrawingGroup contains a rectangle with a VisualBrush encapsulates a DrawingVisual, the content of that DrawingVisual will be antialiased even if you used that method).

Thus you can rewrite your code as follows:

    DrawingVisual visual = new DrawingVisual();
    DrawingGroup group = new DrawingGroup();

    Pen pen = new Pen(Brushes.Black, 1);

    group.Children.Add(new GeometryDrawing(Brushes.YellowGreen, pen, new EllipseGeometry(new Point(0,0), 40, 40)));

    for (int i = 0; i <= 100 - 1; i++)
      group.Children.Add(new GeometryDrawing(null, new Pen(Brushes.Black, 1), new LineGeometry(new Point(0, i), new Point(i, i))));

    RenderOptions.SetEdgeMode(group, EdgeMode.Aliased);

    DrawingContext context = visual.RenderOpen();
    context.DrawDrawing(group);
    context.Close();

    RenderTargetBitmap bmp = new RenderTargetBitmap(100, 100, 96, 96, PixelFormats.Pbgra32);

    bmp.Render(visual);
    image1.Source = bmp;
Moonfaced answered 7/6, 2013 at 13:5 Comment(0)
A
2

The code You posted is drawing mutliple thin lines next to each other. Each of them is antialiased and has got its sides blurred. I suppose the opacity mixing effect You described occurs because of this.

If you drew one thick line (i.e. with 10 width) the effect would not appear.

So, the solution depends on what exactly You are trying to achieve:

  • you can try to disable antialiasing, if this is satisfactory, for more information about this, take a look here: Disabling antialiasing on a WPF image
  • draw lines with a Pen that has got greater width, i.e. new Pen(Brushes.Black, 2)
  • in this particular case of drawing lines close to each other, you can increment the counter by 0.5f instead of 1, so replace for (int i = 0; i <= 100 - 1; i++) with for (float i = 0.0f; i <= 100 - 1; i+=0.5f).
  • if you don't mind writing some more code, you can create your own custom Bitmap class that doesn't make images blurry. Some solution is available here http://www.nbdtech.com/Blog/archive/2008/11/20/blurred-images-in-wpf.aspx and in the site referenced there
Asclepius answered 12/6, 2012 at 17:17 Comment(1)
I failed to draw a 1px width line, but 2px line is drawn completely opaque. Here is a good example: daniel-albuschat.blogspot.com/2011/04/…Mcloughlin
M
1

Another (easier) way here:

https://mcmap.net/q/911570/-forcing-drawingcontext-lines-to-snap-to-pixel-boundaries

For your example, you just have to create a class like:

    public class AliasedDrawingVisual : DrawingVisual
    {
        public AliasedDrawingVisual()
        {
            this.VisualEdgeMode = EdgeMode.Aliased;
        }
    }

and replace your DrawingVisual instance with AliasedDrawingVisual:

    DrawingVisual visual = new AliasedDrawingVisual(); // Here is the only change
    DrawingContext context = visual.RenderOpen();

    Pen pen = new Pen(Brushes.Black, 1);

    context.DrawEllipse(Brushes.YellowGreen, pen, new Point(0,0), 40, 40);

    for (int i = 0; i <= 100 - 1; i++)
      context.DrawLine(new Pen(Brushes.Black, 1), new Point(0, i), new Point(i, i));

    context.Close();

    RenderTargetBitmap bmp = new RenderTargetBitmap(100, 100, 96, 96, PixelFormats.Pbgra32);

    bmp.Render(visual);
    image1.Source = bmp;
Moonfaced answered 7/6, 2013 at 15:26 Comment(0)
B
1

I have encountered the same issue with programmatic rendering to DrawingContext. The only only way that works is to use GuidelineSet: http://wpftutorial.net/DrawOnPhysicalDevicePixels.html

Barnabas answered 5/5, 2015 at 12:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.