RenderTargetBitmap图像滑动

时间:2019-04-06 03:52:02

标签: c# wpf

每当我渲染画布并清除其子级并将渲染的位图设置为它向右下滑动的画布背景时,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 个答案:

答案 0 :(得分:1)

您的主要问题来自于这样一个事实,由于Canvas周围有1个像素的边界,其VisualOffset向量是(1,1)。因此,任何视觉效果(例如背景笔刷)都将以该偏移量应用。将视觉效果渲染为位图时,它会捕获当前外观,然后将位图设置为画笔时,它会移位。

具有讽刺意味的是,解决此问题的最简单方法之一是在XAML中插入另一个<Border/>元素:

<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/>元素。

仅此一项更改就能几乎完全修复您的代码。但是,您可能仍然会注意到另外一个小瑕疵:每次渲染视觉效果时,它只会变得有点模糊。这是因为Brush对象的Stretch属性的默认值为Stretch.Fill,并且因为<Canvas/>元素并不是精确的整数宽度或高度,位图( 确实具有完整的宽度和高度)在渲染时会被拉长一些。随着每次迭代,这一点变得越来越明显。

您可以通过将Stretch属性设置为Stretch.None来解决此问题。同时,您还需要将笔刷的对齐方式设置为LeftTop

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。在上面的示例中,“环绕边界”方法似乎是一种合理的解决方法,但是,诚然,它并没有像在博客页面上显示的那样强迫将未经修饰的上下文呈现为可视效果那样干净。 >

答案 1 :(得分:1)

为避免将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的其他任何属性(例如,其视口),它将填充整个Rectangle区域。有关详细信息,请参见TileBrush Overview