将WPF画布另存为图像

0pizxfdo  于 12个月前  发布在  其他
关注(0)|答案(5)|浏览(146)

我关注了this文章,并保存了我的画布,但是,我想扩展代码的功能,并将画布的特定部分保存为图像,而不是整个画布。
我尝试设置rect.Offsetrect.Location属性,但图像总是从画布的左上角保存。
有没有人知道如何以类似的方式实现我想要的功能?
谢谢你,谢谢

vfwfrxfs

vfwfrxfs1#

一个简单的方法是在渲染整个画布后使用CroppedBitmap。如果需要多个映像,可以重用相同的RenderTargetBitmap

RenderTargetBitmap rtb = new RenderTargetBitmap((int)canvas.RenderSize.Width,
    (int)canvas.RenderSize.Height, 96d, 96d, System.Windows.Media.PixelFormats.Default);
rtb.Render(canvas);

var crop = new CroppedBitmap(rtb, new Int32Rect(50, 50, 250, 250));

BitmapEncoder pngEncoder = new PngBitmapEncoder();
pngEncoder.Frames.Add(BitmapFrame.Create(crop));

using (var fs = System.IO.File.OpenWrite("logo.png"))
{
    pngEncoder.Save(fs);
}

如果要保存到位图对象而不是文件,可以用途:

using (Stream s = new MemoryStream())
{
    pngEncoder.Save(s);
    Bitmap myBitmap = new Bitmap(s);
}
nbysray5

nbysray52#

我知道这是一个老问题,但我花了一段时间寻找和尝试不同的答案,想出了一些可靠的工作。因此,为了为将来的工作节省保存一些时间,这里提供了一个小服务,可以将画布保存到文件中,或者返回ImageSource以在应用程序的其他地方显示。
对于生产应用程序,它应该更加健壮,额外的空值和错误检查等。

public static class RenderVisualService
{
    private const double defaultDpi = 96.0;

    public static ImageSource RenderToPNGImageSource(Visual targetControl)
    {
        var renderTargetBitmap = GetRenderTargetBitmapFromControl(targetControl);

        var encoder = new PngBitmapEncoder();
        encoder.Frames.Add(BitmapFrame.Create(renderTargetBitmap));

        var result = new BitmapImage();

        using (var memoryStream = new MemoryStream())
        {
            encoder.Save(memoryStream);
            memoryStream.Seek(0, SeekOrigin.Begin);

            result.BeginInit();
            result.CacheOption = BitmapCacheOption.OnLoad;
            result.StreamSource = memoryStream;
            result.EndInit();
        }

        return result;
    }

    public static void RenderToPNGFile(Visual targetControl, string filename)
    {
        var renderTargetBitmap = GetRenderTargetBitmapFromControl(targetControl);

        var encoder = new PngBitmapEncoder();
        encoder.Frames.Add(BitmapFrame.Create(renderTargetBitmap));

        var result = new BitmapImage();

        try
        {
            using (var fileStream = new FileStream(filename, FileMode.Create))
            {
                encoder.Save(fileStream);
            }
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine($"There was an error saving the file: {ex.Message}");
        }
    }

    private static BitmapSource GetRenderTargetBitmapFromControl(Visual targetControl, double dpi = defaultDpi)
    {
        if (targetControl == null) return null;

        var bounds = VisualTreeHelper.GetDescendantBounds(targetControl);
        var renderTargetBitmap = new RenderTargetBitmap((int)(bounds.Width * dpi / 96.0),
                                                        (int)(bounds.Height * dpi / 96.0),
                                                        dpi,
                                                        dpi,
                                                        PixelFormats.Pbgra32);

        var drawingVisual = new DrawingVisual();

        using (var drawingContext = drawingVisual.RenderOpen())
        {
            var visualBrush = new VisualBrush(targetControl);
            drawingContext.DrawRectangle(visualBrush, null, new Rect(new Point(), bounds.Size));
        }

        renderTargetBitmap.Render(drawingVisual);
        return renderTargetBitmap;
    }
}

一个示例WPF应用程序演示了它的使用。
MainWindow.xaml

<Window x:Class="CanvasToBitmapDemo.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:CanvasToBitmapDemo"
    mc:Ignorable="d"
    Title="MainWindow" Height="450" Width="800">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>

    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="1*" />
        <ColumnDefinition Width="1*" />
    </Grid.ColumnDefinitions>

    <StackPanel Grid.Row="0" Grid.ColumnSpan="2" Orientation="Horizontal" HorizontalAlignment="Center">
        <Button Click="Button_Click" Content="Capture Image" Width="100"/>
        <Button Click="Button_Click_1" Content="Save To Disk" Width="100"/>
    </StackPanel>

    <Canvas x:Name="PART_Canvas" Grid.Row="1" Grid.Column="0">
        <Ellipse Canvas.Top="50"
                 Canvas.Left="60"
                 Fill="Gold"
                 Width="250"
                 Height="250" />

        <Polyline Stroke="#FF853D00"
                  StrokeThickness="10"
                  StrokeEndLineCap="Round"
                  StrokeStartLineCap="Round"
                  Points="110,100 120,97 130,95 140,94 150,95 160,97 170,100" />

        <Ellipse Canvas.Top="115"
                 Canvas.Left="114"
                 Fill="#FF853D00"
                 Width="45"
                 Height="50" />

        <Polyline Stroke="#FF853D00"
                  StrokeThickness="10"
                  StrokeEndLineCap="Round"
                  StrokeStartLineCap="Round"
                  Points="205,120 215,117 225,115 235,114 245,115 255,117 265,120" />

        <Ellipse Canvas.Top="120"
                 Canvas.Left="208"
                 Fill="#FF853D00"
                 Width="45"
                 Height="50" />

        <Polyline Stroke="#FF853D00"
                  StrokeThickness="10"
                  StrokeEndLineCap="Round"
                  StrokeStartLineCap="Round"
                  Points="150,220 160,216 170,215 180,215 190,216 202,218 215,221" />

    </Canvas>

    <Image x:Name="PART_Image" Grid.Row="1" Grid.Column="1" Stretch="None"/>
</Grid>

以及调用服务背后的代码。

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        PART_Image.Source = RenderVisualService.RenderToPNGImageSource(PART_Canvas);
    }

    private void Button_Click_1(object sender, RoutedEventArgs e)
    {
        RenderVisualService.RenderToPNGFile(PART_Canvas, "myawesomeimage.png");
    }
}
rpppsulh

rpppsulh3#

看看你发布的链接,显然你可以在这里选择渲染的目标坐标。

RenderTargetBitmap rtb = new RenderTargetBitmap((int)rect.Right,
     (int)rect.Bottom, 96d, 96d, System.Windows.Media.PixelFormats.Default);
vq8itlhq

vq8itlhq4#

看看这个解决方案是否适合你。

Size size = new Size(width, height);
canvas.Measure(size);
canvas.Arrange(new Rect(X, Y, width, height));

//Save Image
...  
...

// Revert old position
canvas.Measure(new Size());
qkf9rpyu

qkf9rpyu5#

在我的例子中,我需要对结果图像应用大小约束,因为图像需要存储,以后用作小图标。
我能够将图像缩小到一个合理的大小,使用下面的ScaledRect结合GetScaledRenderTargetBitmapFromControl(感谢@Andy Stagg的帖子中的代码)。
然后,为了存储图像供以后使用,我使用了下面的SaveImageOfControlToStream方法。

private static Rect CreateScaledRect(Visual targetControl)
{
    Rect scaledRect;
    var bounds = VisualTreeHelper.GetDescendantBounds(targetControl);

    // maintain aspect ratio and make sure scaledRect is at least 64 wide or 64 high
    if (bounds.Width < bounds.Height)
    {
       scaledRect = new Rect(new Point(), new Size(64, bounds.Height / bounds.Width * 64));
    }
    else
    {
       scaledRect = new Rect(new Point(), new Size(bounds.Width / bounds.Height * 64, 64));
    }

    return scaledRect;
}

private static BitmapSource GetScaledRenderTargetBitmapFromControl(Visual targetControl, double dpi = defaultDpi)
{
    if (targetControl == null) return null;

    // Calling CreateScaledRect here to reduce image size
    var bounds = CreateScaledRect(targetControl);
    var renderTargetBitmap = new RenderTargetBitmap((int)(bounds.Width * dpi / 96.0),
                                                    (int)(bounds.Height * dpi / 96.0),
                                                    dpi,
                                                    dpi,
                                                    PixelFormats.Pbgra32);

    var drawingVisual = new DrawingVisual();

    using (var drawingContext = drawingVisual.RenderOpen())
    {
        var visualBrush = new VisualBrush(targetControl);
        drawingContext.DrawRectangle(visualBrush, null, new Rect(new Point(), bounds.Size));
    }

    renderTargetBitmap.Render(drawingVisual);
    return renderTargetBitmap;
}

public static void SaveImageOfControlToStream(Stream outputStream, Visual targetControl)
{
    var bitmapSource = GetScaledRenderTargetBitmapFromControl(targetControl);
            
    PngBitmapEncoder encoder = new PngBitmapEncoder();
    encoder.Frames.Add(BitmapFrame.Create(bitmapSource ));
    encoder.Save(outputStream);
}

相关问题