我关注了this文章,并保存了我的画布,但是,我想扩展代码的功能,并将画布的特定部分保存为图像,而不是整个画布。我尝试设置rect.Offset和rect.Location属性,但图像总是从画布的左上角保存。有没有人知道如何以类似的方式实现我想要的功能?谢谢你,谢谢
rect.Offset
rect.Location
vfwfrxfs1#
一个简单的方法是在渲染整个画布后使用CroppedBitmap。如果需要多个映像,可以重用相同的RenderTargetBitmap。
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); }
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"); } }
rpppsulh3#
看看你发布的链接,显然你可以在这里选择渲染的目标坐标。
RenderTargetBitmap rtb = new RenderTargetBitmap((int)rect.Right, (int)rect.Bottom, 96d, 96d, System.Windows.Media.PixelFormats.Default);
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());
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); }
5条答案
按热度按时间vfwfrxfs1#
一个简单的方法是在渲染整个画布后使用
CroppedBitmap
。如果需要多个映像,可以重用相同的RenderTargetBitmap
。如果要保存到位图对象而不是文件,可以用途:
nbysray52#
我知道这是一个老问题,但我花了一段时间寻找和尝试不同的答案,想出了一些可靠的工作。因此,为了为将来的工作节省保存一些时间,这里提供了一个小服务,可以将画布保存到文件中,或者返回ImageSource以在应用程序的其他地方显示。
对于生产应用程序,它应该更加健壮,额外的空值和错误检查等。
一个示例WPF应用程序演示了它的使用。
MainWindow.xaml
以及调用服务背后的代码。
rpppsulh3#
看看你发布的链接,显然你可以在这里选择渲染的目标坐标。
vq8itlhq4#
看看这个解决方案是否适合你。
qkf9rpyu5#
在我的例子中,我需要对结果图像应用大小约束,因为图像需要存储,以后用作小图标。
我能够将图像缩小到一个合理的大小,使用下面的ScaledRect结合GetScaledRenderTargetBitmapFromControl(感谢@Andy Stagg的帖子中的代码)。
然后,为了存储图像供以后使用,我使用了下面的SaveImageOfControlToStream方法。