WPF 如何将画布的一部分复制到图像中并覆盖图像文件
Posted
技术标签:
【中文标题】WPF 如何将画布的一部分复制到图像中并覆盖图像文件【英文标题】:WPF How to copy a portion of a canvas into an image and overwrite the image file 【发布时间】:2022-01-15 20:11:27 【问题描述】:我正在尝试做的事情:
-
从保存到磁盘的多个 .png 文件中加载图像(如果文件不存在,则创建一个新图像并用给定的颜色填充)
将这些图像添加到画布(它们与画布大小相同,并且预计大部分都在屏幕外,其中一部分与实际画布区域重叠)
使用线条、椭圆等在画布上绘图
(保存时)获取 Canvas 并将其转换为内存中保存的平面图像(我们称之为 CanvasImage)
对于我在第 1 步中加载的每个图像,计算与实际 Canvas 区域重叠的部分,并将 CanvasImage 的该部分复制到原始图像的适当部分中
将生成的图像保存到磁盘,覆盖现有文件
我在不同的时间使用了我通过搜索如何执行每个确切步骤找到的代码,但从来没有完整的过程。我主要在第 4 步到第 6 步中苦苦挣扎,因为我并不真正了解所有不同的与图像相关的类、如何在它们之间进行转换以及在该过程的每个步骤中使用哪个类。虽然我在第 1 步也遇到了一些问题,因为我需要能够在最后覆盖文件。
我已经阅读了很多相关问题(覆盖图像、将画布转换为图像、复制图像的一部分等)并尝试了解决方案(其中一些在更简单的示例中有效),但我我正在努力把它们放在一起。
任何帮助或建议将不胜感激。如果有什么你想让我详细说明的,我很乐意,如果你想看一些我可以分享的代码(我刚刚尝试了很多不同的东西,所以我不是确定它会有多大用处)
【问题讨论】:
您可以使用 RenderTargetBitmap 将 UIElement 转换为图像文件。你可以找到像***.com/questions/14118003/…这样的例子。 【参考方案1】:我已经设法解决了这个问题。我将在下面发布我遇到问题的每个步骤的代码,以防其他人发现它有用。这里的代码稍作修改以适应一般情况。
第 1 步:从磁盘加载可被覆盖的映像
public static System.Windows.Controls.Image LoadImage(string filePath)
var image = new System.Windows.Controls.Image();
image.Stretch = Stretch.Uniform;
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
bitmapImage.UriSource = new Uri(filePath, UriKind.Absolute);
bitmapImage.EndInit();
image.Width = bitmapImage.PixelWidth;
image.Height = bitmapImage.PixelHeight;
image.Source = bitmapImage;
return image;
第 4 步:将画布转换为 BitmapImage
public static BitmapImage Convert(Canvas canvas)
var sizeRender = new Size(canvas.ActualWidth, canvas.ActualHeight);
var dv = new DrawingVisual();
using (DrawingContext ctx = dv.RenderOpen())
VisualBrush vb = new VisualBrush(canvas);
ctx.DrawRectangle(vb, null, new Rect(new Point(), sizeRender));
var renderBitmap =
new RenderTargetBitmap(
(int)canvas.ActualWidth,
(int)canvas.ActualHeight,
96d,
96d,
PixelFormats.Pbgra32);
renderBitmap.Render(dv);
BitmapEncoder pngEncoder = new PngBitmapEncoder();
pngEncoder.Frames.Add(BitmapFrame.Create(renderBitmap));
BitmapImage bitmap;
using (var stream = new MemoryStream())
pngEncoder.Save(stream);
bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.StreamSource = stream;
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.EndInit();
bitmap.Freeze();
return bitmap;
第 5 步:将图像的一部分复制到另一个图像中
public static void SaveImage(Image inputImage, BitmapImage canvasImage, string fileName)
... Calculate the portion that overlaps (left, top, width, height) ...
var canvasImageAsBitmap = BitmapConverter.ConvertBitmapImageToBitmap(canvasImage);
var croppingRectangle = new Rectangle(left, top, width, height);
var croppedRegion = canvasImageAsBitmap.Clone(croppingRectangle, canvasImageAsBitmap.PixelFormat);
var image = BitmapConverter.ConvertBitmapImageToBitmap((BitmapImage)inputImage.Source);
var bitmap = new Bitmap((int)inputImage.Width, (int)inputImage.Height);
... Calculate the position of the cropped image in the input image (xPos, yPos) ...
using (var graphics = Graphics.FromImage(bitmap))
graphics.DrawImage(image, 0, 0);
graphics.DrawImage(croppedRegion, xPos, yPos);
graphics.Flush();
bitmap.Save(fileName);
这一步依赖于以下代码来工作:
public static Bitmap ConvertBitmapImageToBitmap(BitmapImage bitmapImage)
using (MemoryStream memoryStream = new MemoryStream())
BitmapEncoder encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmapImage));
encoder.Save(memoryStream);
Bitmap bitmap = new Bitmap(memoryStream);
return new Bitmap(bitmap);
第 6 步:覆盖图像文件以保存在磁盘中。
这只是第 5 步中显示的行 bitmap.Save(fileName);
,由于第 1 步的实施而有效
【讨论】:
以上是关于WPF 如何将画布的一部分复制到图像中并覆盖图像文件的主要内容,如果未能解决你的问题,请参考以下文章