RenderTargetBitmap Image Sliding

后端 未结 2 709
小鲜肉
小鲜肉 2020-12-21 04:15

I\'m having a problem with RenderTargetBitmap whenever I render canvas and clear its children and set the rendered bitmap as background of canvas it slide toward bottom righ

相关标签:
2条回答
  • 2020-12-21 04:42

    Your primary problem stems from the fact that, due to the 1-pixel border around the Canvas, its VisualOffset vector is (1,1). Thus, any visual effect, like the background brush, will be applied at that offset. When you render the visual into a bitmap, it captures the present appearance, and then when you set the bitmap as the brush, it gets shifted.

    Ironically, one of the easiest ways to fix this is to insert another <Border/> element into your 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>
    

    Then the offset caused by the outer <Border/> element is handled by the new <Border/> element's transform, rather than being applied to the <Canvas/> element.

    That change alone will almost fix your code completely. However, there's one other little artifact that you may still notice: every time you render the visual, it gets just a teensy bit blurrier. This is because the default value for the Brush object's Stretch property is Stretch.Fill, and because your <Canvas/> element is not precisely an integral width or height, the bitmap (which necessarily does have integral width and height) gets stretched just a teensy bit when rendered. With each iteration, this becomes more and more apparent.

    You can fix that by setting the Stretch property to Stretch.None. At the same time, you'll also want to set the brush's alignment to Left and 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();
    }
    

    The defaults are Center, which again incurs the rounding error and will cause both movement and blurring of the image after repeated iterations of the process.

    With the above changes, I found a perfectly stable image, regardless of the number of iterations.

    The "wrap in a border" idea came from here: https://blogs.msdn.microsoft.com/jaimer/2009/07/03/rendertargetbitmap-tips/

    On that page you'll find a more general-purpose solution which does not require modification of the actual XAML. In your example above, the "wrap in a border" approach seems like a reasonable work-around, but it is admittedly not as clean as forcing an unadorned context into which you can render the visual, as shown on that blog page.

    0 讨论(0)
  • 2020-12-21 04:46

    To avoid any offset problems with drawing a Visual into a RenderTargetBitmap, you may use an intermediate 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();
    

    Note that without setting any further properties of the ImageBrush (like e.g. its Viewport), it will fill the entire area of the Rectangle. For details, see TileBrush Overview.

    0 讨论(0)
提交回复
热议问题