Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DrawingContext.DrawLine: Pen has no full opacity?

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.

like image 870
Julian Avatar asked Jun 12 '12 16:06

Julian


2 Answers

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;
like image 179
MaMazav Avatar answered Sep 16 '22 23:09

MaMazav


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
like image 41
Lukasz M Avatar answered Sep 18 '22 23:09

Lukasz M