RenderTargetBitmap Image Sliding - PullRequest
       1

RenderTargetBitmap Image Sliding

3 голосов
/ 06 апреля 2019

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

не может вставлять изображения до 10 репутации: (.

WPF:

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="500" Width="700"
        KeyDown="Window_KeyDown">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="50"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="50"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="50"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="50"/>
        </Grid.ColumnDefinitions>
        <Border BorderBrush="Black" BorderThickness="1" Grid.Row="1" Grid.Column="1">
            <Canvas x:Name="Pad">
                <Rectangle Height="100" Width="100" Fill="Red" Canvas.Left="10" Canvas.Top="10"></Rectangle>
        </Canvas>
        </Border>
    </Grid>
</Window>

c # код:

namespace WpfApp1
{
    public partial class MainWindow : Window
    {

         public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_KeyDown(object sender, KeyEventArgs e)
        {
            RenderTargetBitmap rendrer = new RenderTargetBitmap(Convert.ToInt32(Pad.ActualWidth),                                                    Convert.ToInt32(Pad.ActualHeight), 96, 96, PixelFormats.Pbgra32);
            rendrer.Render(Pad);
            Pad.Background = new ImageBrush(rendrer);
            Pad.Children.Clear();
        }

    }

}

Ответы [ 2 ]

1 голос
/ 06 апреля 2019

Чтобы избежать проблем со смещением при рисовании Visual в RenderTargetBitmap, вы можете использовать промежуточный DrawingVisual:

var rect = new Rect(Pad.RenderSize);
var visual = new DrawingVisual();

using (var dc = visual.RenderOpen())
{
    dc.DrawRectangle(new VisualBrush(Pad), null, rect);
}

var bitmap = new RenderTargetBitmap(
    (int)rect.Width, (int)rect.Height, 96, 96, PixelFormats.Default);
bitmap.Render(visual);

Pad.Background = new ImageBrush(bitmap);
Pad.Children.Clear();

Обратите внимание, что без установки каких-либо дополнительных свойств ImageBrush (например, его видового экрана), онзаполнит всю площадь прямоугольника.Подробнее см. Обзор TileBrush .

1 голос
/ 06 апреля 2019

Ваша основная проблема связана с тем, что из-за 1-пиксельной границы вокруг Canvas его вектор VisualOffset равен (1,1). Таким образом, любой визуальный эффект, например кисть фона, будет применен с этим смещением. Когда вы визуализируете визуальное изображение в растровое изображение, оно захватывает текущий вид, а затем, когда вы устанавливаете растровое изображение в качестве кисти, оно смещается.

По иронии судьбы, один из самых простых способов исправить это - вставить еще один элемент <Border/> в ваш XAML:

<Border BorderBrush="Black" BorderThickness="1" Grid.Row="1" Grid.Column="1">
  <Border>
    <Canvas x:Name="Pad">
      <Rectangle Height="100" Width="100" Fill="Red" Canvas.Left="10" Canvas.Top="10"/>
    </Canvas>
  </Border>
</Border>

Тогда смещение, вызванное внешним элементом <Border/>, обрабатывается преобразованием нового элемента <Border/>, а не применяется к элементу <Canvas/>.

Это изменение само по себе почти полностью исправит ваш код. Тем не менее, есть еще один маленький артефакт, который вы все равно можете заметить: каждый раз, когда вы визуализируете визуальное изображение, оно становится чуть более размытым. Это связано с тем, что значение по умолчанию для свойства Stretch объекта Brush равно Stretch.Fill, а элемент <Canvas/> не является целой шириной или высотой, поэтому растровое изображение (которое обязательно имеет интегральная ширина и высота) при рендеринге растягивается чуть-чуть. С каждой итерацией это становится все более очевидным.

Это можно исправить, установив для свойства Stretch значение Stretch.None. В то же время вы также захотите установить выравнивание кисти на Left и Top:

private void Window_KeyDown(object sender, KeyEventArgs e)
{
    RenderTargetBitmap renderer = new RenderTargetBitmap(
        Convert.ToInt32(Pad.ActualWidth), Convert.ToInt32(Pad.ActualHeight), 96, 96, PixelFormats.Pbgra32);
    renderer.Render(Pad);
    ImageBrush brush = new ImageBrush(renderer);
    brush.AlignmentX = AlignmentX.Left;
    brush.AlignmentY = AlignmentY.Top;
    brush.Stretch = Stretch.None;
    Pad.Background = brush;
    Pad.Children.Clear();
}

Значения по умолчанию: Center, что снова вызывает ошибку округления и приведет к перемещению и размытию изображения после многократных итераций процесса.

С учетом вышеуказанных изменений я нашел идеально стабильное изображение независимо от количества итераций.

Отсюда возникла идея "завернуть в рамку": https://blogs.msdn.microsoft.com/jaimer/2009/07/03/rendertargetbitmap-tips/

На этой странице вы найдете более универсальное решение, которое не требует модификации фактического XAML. В приведенном выше примере подход «обтекание границ» выглядит как разумный обходной путь, но он, правда, не такой чистый, как принудительное добавление неукрашенного контекста, в который можно визуализировать визуальный элемент, как показано на этой странице блога.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...