Domanda

Ho un bizzarro problema di rendering quando sto cercando di utilizzare la grafica anti-aliasing in WPF.

Ecco una versione semplificata.

Se uso il seguente 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>

E i seguenti 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);
        }
    }
}

Ho uno strano problema di rendering con l'antialiasing in cui si trova il bordo del rettangolo di ritaglio (eseguendo il programma, sarà abbastanza ovvio, ma è una linea grigia sfumata in cui il rettangolo di ritaglio sta troncando i percorsi.)

Ho provato varie tecniche come allineare i controlli a pixel specifici e impostare SnapsToDevicePixels sui vari controlli nella speranza che ciò risolva questo problema (rimuova la banda grigia extra sfumata), ma nulla sembra aiutare.

Qualche idea?

È stato utile?

Soluzione

Si scopre che ciò è dovuto al fatto che sebbene la finestra sia, ovviamente, allineata su un pixel diverso, la griglia all'interno della finestra potrebbe non esserlo. Risolvere il problema non è eccessivamente difficile, ma può richiedere del tempo per capire cosa fare.

C'è una bella funzione chiamata SnapsToDevicePixels che dovrebbe allineare tutto correttamente. E, purtroppo, per qualsiasi motivo, non sembra funzionare affatto (questo sembra essere un bug compreso). Quindi cosa fare?

Innanzitutto, la griglia deve essere allineata con un pixel diverso (cioè non centrato, o qualcosa del genere, poiché se la finestra ha un numero dispari di pixel in direzione orizzontale o verticale, allora la griglia, e quindi il contenuto della griglia, sarà disallineato.)

Ma poi, ci sono altri problemi da affrontare ... non appena inizi a scorrere le barre di scorrimento, ricompare l'artefatto! Questo perché le barre di scorrimento non fanno necessariamente scorrere il contenuto di un numero intero di pixel. Per far ciò, acquisisco alcuni eventi in ScrollViewer per impostare la posizione di scorrimento su valori interi.

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

Fallo e tutto sembra andare bene.

(Come nota a margine, per le persone che hanno problemi di immagine all'interno di ScrollViews ... se stai riscontrando lo stesso problema, questo dovrebbe risolverlo anche se l'immagine non viene ridimensionata, ruotata, ecc ... fintanto che stai solo cercando di allineare l'immagine a un pixel diverso, questo dovrebbe fare il lavoro.)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top