【问题标题】:RenderTargetBitmap Image SlidingRenderTargetBitmap 图片滑动
【发布时间】:2019-04-06 03:52:01
【问题描述】:

每当我渲染画布并清除其子级并将渲染的位图设置为画布的背景时,我都会遇到 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();
        }

    }

}

【问题讨论】:

    标签: c# wpf


    【解决方案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

    【讨论】:

    • 像魅力一样工作。显然 DrawingVisual/DrawingContext/VisualBrush 忽略了 VisualOffset,否则会打扰 RenderTargetBitmap。我的代码: Rect rect = new Rect(new Point(), new Point(control.ActualWidth, control.ActualHeight)); DrawingVisual dv = new DrawingVisual();使用 (DrawingContext ctx = dv.RenderOpen()) { VisualBrush vb = new VisualBrush(control); ctx.DrawRectangle(vb, null, rect); }
    【解决方案2】:

    您的主要问题源于这样一个事实,即由于 Canvas 周围的 1 像素边框,它的 VisualOffset 向量是 (1,1)。因此,任何视觉效果,如背景画笔,都将应用在该偏移处。当您将视觉对象渲染为位图时,它会捕获当前的外观,然后当您将位图设置为画笔时,它会发生偏移。

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

    <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>
    

    然后由外部&lt;Border/&gt; 元素引起的偏移由新&lt;Border/&gt; 元素的变换处理,而不是应用于&lt;Canvas/&gt; 元素。

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

    您可以通过将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。在您上面的示例中,“在边框中环绕”方法似乎是一种合理的解决方法,但诚然,它不如强制一个可以呈现视觉效果的朴素上下文那么干净,如该博客页面所示。​​

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-04-04
      • 1970-01-01
      • 2018-10-05
      • 1970-01-01
      • 2012-10-25
      • 1970-01-01
      相关资源
      最近更新 更多