Сглаживание артефактов в WPF - PullRequest
3 голосов
/ 22 января 2009

У меня странная проблема с рендерингом при попытке использовать сглаженную графику в WPF.

Вот упрощенная версия.

Если я использую следующий 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>

и следующие 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);
        }
    }
}

У меня возникает странная проблема рендеринга с сглаживанием, где находится край прямоугольника обрезки (при запуске программы это будет довольно очевидно, но это размытая серая линия, где прямоугольник обрезки обрезает пути.)

Я пробовал различные методы, такие как выравнивание элементов управления по определенным пикселям и установка SnapsToDevicePixels на различные элементы управления в надежде, что это решит эту проблему (уберет лишнюю мутную серую полосу), но, похоже, ничего не помогает.

Есть идеи?

1 Ответ

3 голосов
/ 26 января 2009

Оказывается, это потому, что хотя окно, конечно, выровнено по границе пикселя, сетка внутри окна может не быть. Исправить это не слишком сложно, но может потребоваться некоторое время, чтобы понять, что делать.

Есть замечательная функция SnapsToDevicePixels, которая должна правильно выровнять все. И, к сожалению, по какой-то причине, это, кажется, не работает вообще (это, кажется, понятая ошибка). Так что же делать?

Во-первых, сетка должна быть выровнена по границе пикселей (то есть не по центру или что-то в этом роде, поскольку, если окно имеет нечетное количество пикселей в горизонтальном или вертикальном направлении, то сетка и, следовательно, содержимое сетки, будет смещено.)

Но есть и другие проблемы, с которыми нужно разобраться ... как только вы начнете прокручивать полосы прокрутки, артефакт снова появится! Это связано с тем, что полосы прокрутки не обязательно прокручивают содержимое целым числом пикселей. Чтобы справиться с этим, я фиксирую некоторые события в ScrollViewer, чтобы установить в месте прокрутки целочисленные значения.

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

Сделай это, и все вроде бы хорошо.

(Как примечание: для людей, у которых возникают проблемы с изображениями внутри ScrollViews ... если вы столкнулись с той же проблемой, это также следует исправить, если изображение не масштабируется, не поворачивается, и т.д. ... до тех пор, пока вы просто пытаетесь выровнять изображение по границе пикселя, это должно сработать.)

...