使用 Magick.NET 在 .NET 程序中读取AVIF、HEIC、WEBP图片
随着科技的进步,越来越多新的、更先进的文件格式应运而生,就比如谷歌的 WebP 与苹果的 AVIF、HEIC 图片格式。
这些图片格式具有更高的压缩比、更低的图像精度损失,并且大部分先进的浏览器都已经支援这些新的格式。(Edge目前还不支持AVIF,垃圾微软!)
既然如此,那么该如何在 .NET 中处理这些新兴的图片格式呢? Magick.NET 类库或许可以帮你解决这些烦恼!
ImageMagick 是一款免费、开源、跨平台的图片处理工具,可以解析、压缩、转换几乎所有类型的图形文件。
官方提供了控制台程序与运行库,可以轻松在各个平台处理图片,方便集成到各类服务中去。
不过 ImageMagick 官方只提供了执行程序和库文件,一般都是用命令行来进行调用,或者是集成到C、C++等程序中,并未直接提供基于 .NET 程序的接口。
所以有大佬基于 ImageMagick 的源码,创建了 Magick.NET 项目:
https://github.com/dlemstra/Magick.NET
https://www.nuget.org/packages?q=magick.net
安装 Magick.NET 运行环境
首先,Magick.NET 是针对于 ImageMagick 的链接库文件的包装,核心的图片解析、转换、存储、压缩等都是基于本地代码,所以需要依据目标客户端来选择 x86 或者是 x64 版本的运行库。Magick.NET 是一个非常庞大的项目,其单环境的运行库文件目前已经达到了20MB。
如果你需要同时兼容x86和x64,则程序可能要带40MB的运行库。
而 ImageMagick 的运行库除了CPU版本不同,还额外分为
Q8
、Q16
、Q16-HDRI
这三个版本。这三个版本的主要区别是对于图片像素精度的处理,Q8为8bit,Q16为16bit,Q16-HDRI为精度更高的32bit浮点值。
一般用Q16即可,否则内存占用太大(Q16-HD为Q8的4倍、Q16的2倍)。
推荐使用 Nuget 进行安装:
https://www.nuget.org/packages?q=magick.net
在搜索结果中,先安装
Magick.NET.Core
,然后选择 Magick.NET-质量-平台
的运行库。质量一般选
Q16
,平台一般选 x64
。如果你不确定目标CPU的位数,或者需要兼容x86的老旧设备,则可以选择
AnyCPU
。安装完成后,Nuget 会自动帮你下载和引用运行库,以及所依赖的 ImageMagick 的本地动态链接库。
选择完运行库后,你还可以根据运行的环境,选择以下两个增强用的类库:
Magick.NET.SystemDrawing
= 提供一个扩展类,用于将 Magick.NET 的 MagickImage
图像类型转化为 .NET 所支持的 Bitmap
类型。Magick.NET.SystemWindowsMedia
= 提供一个扩展类,用于将 MagickImage
类型转化为 WPF 所支持的 BitmapSource
类型。如果安装顺利完成,我们就可以测试能否解析 .NET 不支持的图片格式了:
Imports ImageMagick
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim fp = "x:\path\xxx.avif"
Dim img As New MagickImage(fp)
Dim bmp As Bitmap = img.ToBitmap()
Me.BackgroundImage = bmp
End Sub
这里我们直接用 MagickImage
装载一个AVIF图片,然后再利用 Magick.NET.SystemDrawing
所提供的扩展方法 ToBitmap
将 MagickImage
转化为 Bitmap
,最后显示在窗体的背景图中。代码示例
原谅我是个VB用户,不过VB语法与C#相差不大,各位应该能看懂吧。😏设置缓存目录
在某些比较老旧的 Magick.NET 版本中,Magick.NET-Q16-AnyCPU.dll
是将链接库集成到程序集内的,只有在程序第一次解析时再将DLL文件释放到临时目录内进行加载的。一般情况下,它都会将DLL放到系统的TEMP文件夹下,但你也可以手动设置这个缓存目录的位置,方便后续的其他操作(比如清理、更新内容什么的)。
Imports ImageMagick
Imports System.IO
' 将缓存目录设置为程序目录下的DLL文件夹
Dim cachePath = Path.Combine(Application.StartupPath, "dll")
If Not Directory.Exists(cachePath) Then Directory.CreateDirectory(cachePath)
MagickAnyCPU.CacheDirectory = cachePath
新版本的 Magick.NET 都是将 .NET 程序集与 DLL 运行库分开的,如果是 AnyCPU 的话,会同时包含x86和x64的DLL文件,但是占用空间也会是两倍了。读取图片的几种方式
' 通过文件路径读取
Dim fp = "x:\path\xxx.avif"
Dim img As New MagickImage(fp) '初始化时直接读取图片数据
img.Read(fp) '等效方法,可以在初始化之后再读取图片数据
' 从Bytes中读取
Dim bs = IO.File.ReadAllBytes("x:\path\xxx.avif")
Dim img As New MagickImage(bs)
' 从文件流、内存流中读取
Dim ms As New MemoryStream(bs)
Dim img As New MagickImage(ms)
调整图片大小
Dim fp = "x:\path\xxx.avif"
Dim img As New MagickImage(fp)
' 设置参数为0时,会按另一个参数进行等比例缩放
img.Resize(1920, 0)
img.Resize(0, 1080)
' 保存编辑过的图片,可以是路径、文件流
' Magick.NET 会按文件扩展名来自动转换图片格式
img.Write("x:\path\new xxx.avif")
' 你也可以指定文件格式
img.Write("x:\path\new xxx.png", MagickFormat.Png)
' 还可以直接获取 Bytes
Dim bs1 As Byte() = img.ToByteArray()
Dim bs2 As Byte() = img.ToByteArray(MagickFormat.Bmp)
使用 Resize
可以快速调整图片大小,如果参数是0的话,就会根据另一个参数进行等比例调整,非常方便。裁剪图片
Dim fp = "x:\path\xxx.avif"
Dim img As New MagickImage(fp)
' 从中间裁剪
img.Crop(500, 500, Gravity.Center)
Crop
可以裁剪图片到指定大小,Gravity.Center
指示图片以中间为基点开始裁剪,放弃周边的图像内容。只加载图片信息
Dim fp = "x:\path\xxx.avif"
Dim img As New MagickImageInfo(fp)
MsgBox(img.Format.ToString) '图片格式
MsgBox(img.Width) '宽度
MsgBox(img.Height) '高度
MsgBox(img.Quality) '图片质量,只有部分格式有效
MagickImageInfo
类只加载图片头部数据,而不加载图片数据,可以用于预先判断图片类型、获取宽高等数据。检测图片是否为动画
Dim fp = "x:\path\xxx.gif"
' .NET 中只能判断 GIF 图片是否为动画
Dim img = Image.FromFile(fp)
Dim isAnimate = ImageAnimator.CanAnimate(img)
' MagickImage 可以通过头部信息来确定图片内的帧数量
Dim minfos = MagickImageInfo.ReadCollection(fp)
MsgBox(minfos.Count) '获取帧数
获取图片所有帧与帧间隔
Dim fp = "x:\path\xxx.gif"
Dim mimgs = New MagickImageCollection(fp)
' 报告下一帧的间隔
For Each ming In mimgs
MsgBox(ming.AnimationTicksPerSecond)
Next
MagickImageCollection
将解析一个图片所包含的所有帧图片,如果不是动画,则只返回一个主帧图(原图)。可以通过循环迭代每一张图片,还可以通过图片的
AnimationTicksPerSecond
属性获取下一帧的间隔时间。如果想要显示动画效果,则需要自己写绘图过程,暂时还没有比较方便的自动动画方式。
目前 MagickImage 可以正确解析 GIF、WEBP 的动画,但是无法解析 APNG 动画,因为 APNG 并没有被官方所承认,只是一个非行业标准动画格式。
读写 PDF 文件
ImageMagick 使用 Ghostscript 库来读取PDF文件,不过因为授权协议不同,所以 Ghostscript 的库文件需要你自行下载。https://ghostscript.com/releases/gsdnld.html
根据自己的目标平台,选择下载 x86 或 x64 的 Ghostscript,将其中的
gsdll32.dll/gsdl64.dll/gswin32c.exe/gswin64c.exe
释放到主程序目录下(路径可以自定义),然后在程序初始化时设定 Ghostscript 运行库的路径。' 在程序初始化时执行,请尽早执行
MagickNET.SetGhostscriptDirectory("x:\xxx\Ghostscript\")
' 直接通过 MagickImageCollection 加载PDF文件
Dim Pdf As New MagickImageCollection("x:\path\xxx.pdf", New MagickReadSettings() With {.Density = New Density(300)})
' 获取PDF文件内每一页的图像实例
For Each page In Pdf
' do someting
page.ToBitmapWithDensity()
Next
' 你还可以在PDF文件中添加、插入新的图片
Pdf.Add(MagickImage) '在末尾添加图片
Pdf.Insert(0, MagickImage) '在指定位置添加图片
Pdf.RemoveAt(0) '删除指定图片
Pdf.Write("x:\path\new xxx.pdf") '保存修改
暂时先写这么多吧,我仅列出了 Magick.NET 最基础的用法,ImageMagick 的神奇功能还有许多,比如压缩、水印、虚化、添加各种特效等等,还请自行检阅
MagickImage
的内部方法。本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。