RenderTargetBitmap 图像滑动

4

我在使用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个回答

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


运行得非常好。显然,DrawingVisual / DrawingContext / VisualBrush 忽略了 VisualOffset,否则会影响 RenderTargetBitmap。我的代码: Rect rect = new Rect(new Point(), new Point(control.ActualWidth, control.ActualHeight));DrawingVisual dv = new DrawingVisual(); using (DrawingContext ctx = dv.RenderOpen()) { VisualBrush vb = new VisualBrush(control); ctx.DrawRectangle(vb, null, rect); } - aliceraunsbaek

3
您的主要问题源于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。在上面的示例中,"包围边框" 方法似乎是一个合理的解决方法,但它确实不像在那个博客页面中所示的强制使用未装饰上下文来渲染视觉效果那样干净。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接