图片相关类
常见的类
关系
Image和Bitmap
继承关系
System.Object
System.MarshalByRefObject
System.Drawing.Image
System.Drawing.Bitmap
Image
为源自 Bitmap 和 Metafile 的类提供功能的抽象基类。
1 | // Create image. |
Bitmap
封装 GDI+ 位图,此位图由图形图像及其属性的像素数据组成。
Bitmap 是用于处理由像素数据定义的图像的对象。
位图由图形图像及其属性的像素数据组成。
有许多标准格式可用于将位图保存到文件。
GDI+ 支持以下文件格式: BMP、GIF、EXIF、JPG、PNG 和 TIFF。
有关支持的格式的详细信息,请参阅位图类型。
您可以通过使用 Bitmap 构造函数之一,从文件、流和其他源创建图像,并使用 Save 方法将它们保存到流或文件系统。
Bitmap是从Image类继承的一个图像类,它封装了Windows位图操作的常用功能。
例如:
Bitmap::SetPixel
和Bitmap::GetPixel
分别用来对位图进行读写像素操作,从而可以为图像的柔化和锐化处理提供一种可能。
示例
1 | Image img = this.pictureBox1.Image; |
GetHbitmap
Bitmap的GetHbitmap()方法
此方法创建 GDI 位图对象的图柄。
官方示例方法
1 | [ ] |
Image和ImageSource
从源码中可以看出
我们加载图片的组件Image设置的是ImageSource
1 | namespace System.Windows.Controls |
ImageSource/BitmapSource/BitmapImage
继承关系
ImageSource=>BitmapSource=>BitmapImage
ImageSource
ImageSource表示具有宽度、高度和 ImageMetadata 的对象类型,这是一个抽象类。
BitmapSource
BitmapSource 也是一个抽象类。
BitmapSource 是 Windows Presentation Foundation (WPF) 图像处理管道的基本构建基块,从概念上讲,以特定大小和分辨率指定一组固定的像素。 BitmapSource 可以是解码器提供的图像文件中的单个帧,也可以是操作自身 BitmapSource 的转换的结果。 BitmapSource 不用于表示多帧图像或动画。
BitmapImage
BitmapImage从图像文件创建位图,并将其用作 Image 控件的源
1 | // Create the image element. |
RenderTargetBitmap
主要作用为保存页面组件为图片
其中myview
为其它组件的名称
XAML
1 | <Grid x:Name="myview"> |
C#
1 | RenderTargetBitmap bitmap = new RenderTargetBitmap(); |
RenderTargetBitmap和BitmapImage
RenderTargetBitmap和BitmapImage都是用于表示位图的类,但是两者有不同的用途和区别:
- 目标渲染位图(RenderTargetBitmap)是一个用于创建位图的WPF元素,它可以在屏幕上渲染一个WPF视觉对象,并将其转换为位图。它通常用于将WPF视觉元素转换为图片,如截图等操作。
- 位图图像(BitmapImage)是一个预定义的位图图像,它可以从本地文件或网络中加载。它通常用于显示图像,如在Image控件中显示图片。
因此,如果需要从WPF视觉元素创建位图,则使用RenderTargetBitmap;如果需要从本地或网络中加载位图,则使用BitmapImage。
图片转换
Bitmap <=> BitmapImage
1 | /// <summary> |
ImagePath => BitmapImage
1 | public static BitmapImage GetImage(string imagePath) |
Bitmap <=> ImageSource
1 | [ ] |
Bitmap => BitmapSource
1 | /// <summary> |
Icon => ImageSource
1 | /// <summary> |
RenderTargetBitmap => BitmapImage
1 | // RenderTargetBitmap --> BitmapImage |
BitmapImage <=> byte[]
1 | // BitmapImage --> byte[] |
byte[] <=> Bitmap
1 | public static System.Drawing.Bitmap ConvertByteArrayToBitmap(byte[] bytes) |
图片保存
Bitmap保存
1 | Bitmap img = ZScreenUtil.CaptureScreen(); |
保存
1 | var filePath = "E:\123.jpg"; |
保存设置压缩质量
1 | public static string CaptureScreenSave(string filePath) |
BitmapSource保存
Bitmap=>BitmapSource
BitmapSource=>写文件
1 | float factor = Graphics.FromHwnd(IntPtr.Zero).DpiX / 96; |
注意
上面的这种保存图片的方式是没有意义的,只是为了展示怎么把BitmapSource保存为图片文件。
BitmapSource=>写文件
1 | /// <summary> |
组件转图片
RenderTargetBitmap=>Bitmap
示例:获取组件的Bitmap
1 | public static Bitmap SaveUi2Bitmap(FrameworkElement element) |
组件转文件
1 | public static void SaveUI2JpegFile(FrameworkElement frameworkElement, int width, int height, string filepath) |
组件转BitmapImage
1 | public static Bitmap SaveUi2Bitmap(FrameworkElement element) |
截屏保存
获取图片
1 | using System; |
保存图片
1 | var filePath = "E:\123.jpg"; |
图片压缩
1 | using System; |
图片加载
加载本地
1 | BitmapImage bImage = new BitmapImage(new Uri("c:\\image.bmp")); |
加载本地图片并缩放
1 | private void SetSource(System.Windows.Controls.Image image, string fileName) |
调用方式
1 | SetSource(this.imageCur, “C:\1.png”); |
JPEG转WEBP
添加依赖
1 | Install-Package Imazen.WebP -Version 10.0.1 |
下载DLL
https://www.dll-files.com/libwebp.dll.html
注意32位和64位,我这里用的32位
项目根目录添加DLL
文件夹,把libwebp.dll
放进去
属性
=>生成事件
=>生成前事件命令行
添加
1 | xcopy /Y /d $(ProjectDir)\DLL\libwebp.dll $(TargetDir) |
转为WEBP
1 | if (targetFilePath.EndsWith("webp")) |
封装的工具类
1 | using Imazen.WebP; |
相关概念
GDI和GDI+
GDI在全称是Graphics Device Interface
,即图形设备接口。是图形显示与实际物理设备之间的桥梁。
GDI接口是基于函数,虽然使程序员省力不少,但是编程方式依然显得麻烦。
例如显示一张位图,我们需要进行“创建位图,读取位图文件信息,启用场景设备,调色板变化“等一系列操作。然而有了GDI+,繁琐的步骤再次被简化。
顾名思义,GDI+就是GDI的增强版,它是微软在Windows 2000以后操作系统中提供的新接口。
GDI+主要提供以下三种功能:
二维矢量图形:GDI+提供了存储图形基元自身信息的类(或结构体)、存储图形基元绘制方式信息的类以及实际进行绘制的类;
图像处理:大多数图片都难以划定为直线和曲线的集合,无法使用二维矢量图形方式进行处理。因此,GDI+为我们提供了Bitmap、Image等类,它们可用于显示、操作和保存BMP、JPG、GIF等图像格式。
文字显示:GDI+支持使用各种字体、字号和样式来显示文本。
相比于GDI,GDI+是基于C++类的对象化的应用程序接口,因此用起来更为简单。
GDI的核心是设备上下文,GDI函数都依赖于设备上下文句柄,其编程方式是基于句柄的;
GDI+无需时刻依赖于句柄或设备上下文,用户只需创建一个Graphics 对象,就可以用面向对象的方式调用其成员函数进行图形操作,编程方式是基于对象的。
GDI +提供了Image、Bitmap 和Metafile 类,方便用户进行图像格式的加载、操作和保存。
GDI+支持的图像格式有BMP、GIF、JPEG、EXIF、PNG、TIFF、ICON、WMF、 EMF等,几乎涵盖了所有的常用图像格式。
GDI+和GDI区别以及一些新特征
GDI+与GDI
GDI是硬件加速的,而GDI+不是的,而且GDI+2.0比GDI+更快。
GDI是有状态的,而GDI+是无状态的。
GDI绘图要使用设备环境和句柄;而GDI+全部交由Graphics类管理(
不创建句柄
)。GDI绘图时可以使用SelectObject频繁切换图形对象,而GDI+的图形对象是独立的。
GDI中存在一个
当前位置
(全局区),目的是提高绘图性能;而GDI+取消了它,以避免绘图时不确定这个当前位置
而带来非预期的错误。GDI总是将画笔和画刷绑定在一起,即使不需要填充一个区域也必须指定一个画刷;而GDI+则可以使用不同的函数分开使用画笔和画刷。
GDI+新特性
改进了颜色管理。GDI+不仅提供了更多可供选择使用的颜色,使其支持Alpha通道合成运算,而且还保持了与其他颜色的兼容性。
绘图支持反锯齿。通过设置GDI+对象的相关属性,GDI+可以与相关的显示驱动程序搭配完成图形绘制时的反锯齿功能,使得绘制的图形更加平滑,美观,而整个过程是由GDI+对象自动计算完成的。
提供渐变画刷。GDI+拓展了GDI的功能,提供线性渐变和路径渐变画刷来填充图形、路径和区域,甚至也可用来绘制直线、曲线等。
独立的路径对象。GDI+使用Graphics对象来进行绘图操作,并将路径操作从Graphics对象分离出来,提供一个Graphics类供用户使用,用户不必担心对象会受到Graphics对象操作的影响,从而可以使用同一个操作对象进行多次的路径绘制操作。
样条曲线。GDI+封装了绘制基数样条曲线和贝塞尔样条曲线的方法。
变形和矩阵运算。GDI+提供了功能强大的Matrix类来实现矩阵的旋转,错切、平移、比例等变换操作,以便产生复杂的新图形。
多图片格式的支持。GDI+该进了图形处理能力,通过GDI+,用户能够访问多种格式的图片文件,转换文件格式等,还能进行图像重新着色、色彩修正、消除走样等图像处理。
注意
GDI+对象比如Bitmap,是不会创建句柄的,GetHbitmap方法不是获取句柄而是创建句柄。不会受GDI句柄数量的限制。只有需要创建句柄进行其他操作时才要调用GetHbitmap创建句柄。
托管资源和非托管资源
概念
托管资源指的是.NET可以自动进行回收的资源,主要是指托管堆上分配的内存资源。托管资源的回收工作是不需要人工干预的,有.NET运行库在合适调用垃圾回收器进行回收。
非托管资源指的是.NET不知道如何回收的资源,最常见的一类非托管资源是包装操作系统资源的对象,
例如文件,窗口,网络连接,数据库连接,画刷,图标等。
这类资源,垃圾回收器在清理的时候会调用Object.Finalize()方法。默认情况下,方法是空的,对于非托管对象,需要在此方法中编写回收非托管资源的代码,以便垃圾回收器正确回收资源。
常见的非托管资源
ApplicationContext, Brush, Component, ComponentDesigner, Container, Context, Cursor, FileStream, Font, Icon, Image, Matrix, Object, OdbcDataReader, OleDBDataReader, Pen, Regex, Socket, StreamWriter, Timer, Tooltip, 文件句柄, GDI资源, 数据库连接等等资源。
非托管资源的释放
托管资源由垃圾回收器控制如何释放,不需要程序员过多的考虑(当然也程序员也可以自己释放)。
非托管资源需要自己编写代码来释放。
在一个包含非托管资源的类中,关于资源释放的标准做法是:
继承IDisposable接口;
实现Dispose()方法,在其中释放托管资源和非托管资源,并将对象本身从垃圾回收器中移除(垃圾回收器不在回收此资源);
实现类析构函数,在其中释放非托管资源。
那么编写好的释放非托管资源的代码(释非代码)由谁来调用呢。
有两种实现方式:
将释非代码放到构造函数析构函数中,由系统自动调用,系统会在资源对象不再使用了,会在某个时间调用构造函数析构函数来释放非托管资源。构造函数析构函数的目的就是用来释放或清理非托管资源的。但它有一个问题是调用的时间是系统说了算,不能在程序中自己想要调用时调用析构函数,这是C#规定的。那么就产生了第二种方式。
将释非代码放到另外一个函数中,当自己想调用时就调用。将释非代码放在一个方法中共用。
代码如下
1 | MyClass:IDisposable |
标准清理模式中多了一句GC.SuppressFinalize(this);
【该方法通知CLR不要调用该方法的析构函数,因为它已经被清理了。】如果没有这句代码,我认为不影响程序的正确性,不会发生安全问题,他只是告诉系统不要再调用构造函数了。那么为什么要加上这句代码呢?如果在调用了Dispose()之后再调用析构函数只是多此一举,所以告诉系统不要再调用了。这一点应该和性能有关系。【如果不需要构造函数就不要执行构造函数,他们会带来性能上的开销】。
释放非托管资源可参看:
https://www.cnblogs.com/niaomingjian/p/3516083.html
using
定义一个范围,在范围结束时处理对象。
只要离开了这个代码段就自动调用这个类实例的Dispose
。
1 | using (Class1 cls1 = new Class1(), cls2 = new Class1()) |
图片与Base64互转
1 | using System; |