views:

1325

answers:

2

I have a bizarre rendering issue when I'm trying to use anti-aliased graphics in WPF.

Here's a simplified version.

If I use the following XAML

<Window x:Class="RenderingBug.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Width="300" Height="300">
    <Grid Name="myGrid" Background="AntiqueWhite" Width="250" Height="250">
        <ScrollViewer Name="myScrollViewer" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
            <Canvas Height="500" Width="500" Name="myCanvas" />
        </ScrollViewer>        
    </Grid>
 </Window>

And the following cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace RenderingBug
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();

            PathFigureCollection pfc = new PathFigureCollection();
            PathFigure pf = new PathFigure();
            pf.StartPoint = new Point(100, 20);
            LineSegment ls = new LineSegment();
            ls.Point = new Point(20, 100);
            PathSegmentCollection psc = new PathSegmentCollection();
            psc.Add(ls);
            pf.Segments = psc;
            pfc.Add(pf);
            PathGeometry pg = new PathGeometry(pfc);

            RectangleGeometry clippingRectangle = new RectangleGeometry(new Rect(0, 0, 80, 80));

            Path p1 = new Path();
            p1.ClipToBounds = true;
            p1.Clip = clippingRectangle;
            p1.StrokeDashCap = PenLineCap.Square;
            p1.Stroke = Brushes.Black;
            p1.StrokeThickness = 30;
            p1.Data = pg;
            myCanvas.Children.Add(p1);

            Path p2 = new Path();
            p2.ClipToBounds = true;
            p2.Clip = clippingRectangle;
            p2.StrokeDashCap = PenLineCap.Square;
            p2.Stroke = Brushes.White;
            p2.StrokeThickness = 10;
            p2.Data = pg;
            myCanvas.Children.Add(p2);
        }
    }
}

I get a strange rendering issue with the anti-aliasing where the cropping rectangle edge is (running the program, it will be fairly obvious, but it's a hazy gray line where the cropping rectangle is truncating the paths.)

I've tried various techniques like aligning the controls to specific pixels, and setting SnapsToDevicePixels on the various controls in the hopes that this would solve this issue (remove the extra hazy gray band), but nothing seems to help.

Any ideas?

A: 

Not sure what the bug is you are seeing? Also you dont have a Height defined in your grid in the sample code provided. Stab in the dark. Maybe snaptopixel might help...?

I've changed the code in the example slightly since your answer was provided...but the basics are still there. Unfortunately, setting height and width don't seem to help in this case, and setting SnapToDevicePixels doesn't work either (not sure why...perhaps of the layers doesn't support it?)
Beska
+3  A: 

Turns out that this is because although the window is, of course, aligned on a pixel boundry, the grid within the window may not be. Fixing this isn't overly difficult, but it can take a while to figure out what to do.

There is a nice feature called SnapsToDevicePixels that should get everything aligned correctly. And, sadly, for whatever reason, it doesn't seem to work at all (this seems to be an understood bug). So what to do?

First, the grid must be aligned at a pixel boundry (ie not centered, or something like that since if the window has an odd number of pixels in either the horizontal or vertical direction then the grid, and hence the grid contents, will be misaligned.)

But then, there are other issues to deal with...as soon as you start to scroll the scroll bars, the artifact reappears! This is because the scrollbars do not necessarily scroll the content an integer number of pixels. To deal with that, I capture some events in the ScrollViewer to set the scrolling location to integer values.

private void workingAreaScrollViewer_SizeChanged(object sender, SizeChangedEventArgs e)
{
    double w = e.NewSize.Width;
    double h = e.NewSize.Height;
    workingAreaScrollViewer.Width = Math.Round(w);
    workingAreaScrollViewer.Height = Math.Round(h);
}

private void Window_KeyDown(object sender, KeyEventArgs e)
{
    if (e.Key == Key.A)
    {
        workingAreaCanvas.Children.Remove(p2);
    }
    if (e.Key == Key.Z && p2.Parent != workingAreaCanvas)
    {
        workingAreaCanvas.Children.Add(p2);
    }
}

Do that, and all seems to be well.

(As a side note, for people that are having Image problems inside ScrollViews...if you're running into the same issue, this should fix that as well, so long as the Image isn't scaled, rotated, etc...so long as you're just trying to align the image to a pixel boundry, this should do the job.)

Beska