前面我们遇到了一个问题,就是将UI保存成图片时,图片不完整,需要将UI的HorizontalAlignment设置为left,VerticalAlignment设置为Top,原因可能是UI保存成图片时是从左上角开始计算宽和高。
今天突然想到一个思路,如果我把包含目标图像在内的一张大图保存下来,然后从大图中提取目标图像,是不是可行,于是按照这个思路进行了尝试,发现问题就这么被解决了。
<Window x:Class="Image_Zoom_Save.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:Image_Zoom_Save"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<ScrollViewer Name="svProcess" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Image Name="imgProcess" Stretch="Uniform" RenderOptions.BitmapScalingMode="HighQuality"/>
</ScrollViewer>
<StackPanel Grid.Row="1" Orientation="Horizontal">
<Button Content="Load" Margin="5" Click="Load_Click"/>
<Button Content="Save" Margin="5" Click="Save_Click"/>
<Slider Name="sliderZoom" Minimum="0.2" Maximum="5" Value="1" VerticalAlignment="Center" Width="200"
ValueChanged="SliderZoom_ValueChanged"/>
<Label Name="lblInfo"/>
<Button Content="Change Position" Margin="5" Click="Position_Click"/>
</StackPanel>
</Grid>
</Window>
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace Image_Zoom_Save
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
private BitmapImage bitmapImage;
public MainWindow()
{
InitializeComponent();
}
private void SliderZoom_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (bitmapImage == null)
{
return;
}
imgProcess.Width = sliderZoom.Value * bitmapImage.Width;
imgProcess.Height = sliderZoom.Value * bitmapImage.Height;
lblInfo.Content = "width = " + ((int)imgProcess.Width).ToString() + "; height = " + ((int)imgProcess.Height).ToString();
}
private void Load_Click(object sender, RoutedEventArgs e)
{
bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.UriSource = new Uri("D:/MyPrograms/图标/qq.png", UriKind.RelativeOrAbsolute);
bitmapImage.EndInit();
imgProcess.Source = bitmapImage;
imgProcess.Width = bitmapImage.Width;
imgProcess.Height = bitmapImage.Height;
imgProcess.VerticalAlignment = VerticalAlignment.Center;
imgProcess.HorizontalAlignment = HorizontalAlignment.Center;
lblInfo.Content = "width = " + ((int)imgProcess.Width).ToString() + "; height = " + ((int)imgProcess.Height).ToString();
}
private void UISaveToImage(FrameworkElement ui, string fileName, int width, int height)
{
RenderTargetBitmap bmp = new RenderTargetBitmap(width, height, 96d, 96d, PixelFormats.Pbgra32);
bmp.Render(ui);
BitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bmp));
FileStream fs = new FileStream(fileName, FileMode.Create);
encoder.Save(fs);
fs.Close();
}
private Bitmap ImageSourceToBitmap(ImageSource imageSource)
{
BitmapSource bitmapSource = (BitmapSource)imageSource;
Bitmap bmp = new Bitmap(bitmapSource.PixelWidth, bitmapSource.PixelHeight, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
BitmapData data = bmp.LockBits(
new Rectangle(System.Drawing.Point.Empty, bmp.Size), ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
bitmapSource.CopyPixels(Int32Rect.Empty, data.Scan0, data.Height * data.Stride, data.Stride);
bmp.UnlockBits(data);
return bmp;
}
private void Save_Click(object sender, RoutedEventArgs e)
{
//UISaveToImage(imgProcess, "test.png", (int)imgProcess.Width, (int)imgProcess.Height);
string tempPath = Path.GetTempPath() + Guid.NewGuid().ToString() + ".png";
int offsetX = Math.Max((int)(0.5 * (svProcess.ActualWidth - imgProcess.ActualWidth)), 0);
int offsetY = Math.Max((int)(0.5 * (svProcess.ActualHeight - imgProcess.ActualHeight)), 0);
int bigX = Math.Max((int)svProcess.ActualWidth, (int)imgProcess.ActualWidth);
int bigY = Math.Max((int)svProcess.ActualHeight, (int)imgProcess.ActualHeight);
UISaveToImage(imgProcess, tempPath, bigX, bigY);
BitmapSource bmpSource = new CroppedBitmap(BitmapFrame.Create(new Uri(tempPath, UriKind.Relative)), new Int32Rect(offsetX, offsetY, (int)imgProcess.ActualWidth, (int)imgProcess.ActualHeight));
ImageSourceToBitmap(bmpSource).Save("test.png");
}
private void Position_Click(object sender, RoutedEventArgs e)
{
imgProcess.VerticalAlignment = VerticalAlignment.Top;
imgProcess.HorizontalAlignment = HorizontalAlignment.Left;
}
}
}
补充说明:这里新增了一个函数,将ImageSource转化成Bitmap;由于Image在ScrollViewer中默认是居中显示的,因此两者宽和高的一半即为目标图像的起点位置,主要是利用了CroppedBitmap函数来进行图像拷贝;至于为什么要保存到临时文件夹的唯一文件名,是考虑到重复保存可能会出现文件占用而无法保存的错误,并且也不想别人看到这个文件。