TwelveMonkeys ImageIO 通过javax.imageio.*
包的插件为 Java 平台提供扩展图像文件格式支持。
该项目的主要目标是为 JDK 未涵盖的文件格式提供支持。对这些格式的支持非常重要,以便能够读取“野外”发现的数据,以及保持对旧格式数据的访问。由于存在大量遗留数据,我们认为需要针对流行格式开放阅读器实现。
插件 | 格式 | 描述 | 右 | 瓦 | 元数据 | 笔记 |
---|---|---|---|---|---|---|
蜡染 | 静止无功发生器 | 可缩放矢量图形 | ✔ | - | - | 需要蜡染 |
WMF | MS Windows 图元文件 | ✔ | - | - | 需要蜡染 | |
骨形态发生蛋白 | 骨形态发生蛋白 | MS Windows 和 IBM OS/2 设备独立位图 | ✔ | ✔ | 原生、标准 | |
尿素 | MS Windows 光标格式 | ✔ | - | - | ||
首次代币发行 | MS Windows 图标格式 | ✔ | ✔ | - | ||
直达DS | 直达DS | MS Direct Draw 表面格式 | ✔ | - | 标准 | |
高动态范围 | 高动态范围 | Radiance 高动态范围 RGBE 格式 | ✔ | - | 标准 | |
集成电路神经网络 | 集成电路神经网络 | 苹果图标图像 | ✔ | ✔ | - | |
IFF | IFF | Commodore Amiga/电子艺术交换文件格式 | ✔ | ✔ | 标准 | |
JPEG | JPEG | 联合摄影师专家组 | ✔ | ✔ | 原生、标准 | |
JPEG 无损 | ✔ | - | 原生、标准 | |||
电脑X | 电脑X | ZSoft 画笔格式 | ✔ | - | 标准 | |
DCX | 多页 PCX 传真文档 | ✔ | - | 标准 | ||
图像通信技术 | 图像通信技术 | Apple QuickTime 图片格式 | ✔ | ✔ | 标准 | |
PNTG | Apple MacPaint 图片格式 | ✔ | - | 标准 | ||
PNM | 聚丙烯酰胺 | NetPBM 便携式任何地图 | ✔ | ✔ | 标准 | |
药物管理 | NetPBM 便携式位图 | ✔ | - | 标准 | ||
铂族金属 | NetPBM 便携式灰图 | ✔ | - | 标准 | ||
生产计划管理 | NetPBM 便携式像素地图 | ✔ | ✔ | 标准 | ||
PFM | 便携式浮动地图 | ✔ | - | 标准 | ||
PSD | PSD | Adobe Photoshop 文档 | ✔ | (✔) | 原生、标准 | |
公共安全局 | Adobe Photoshop 大文档 | ✔ | - | 原生、标准 | ||
SGI | SGI | 硅图形图像格式 | ✔ | - | 标准 | |
热重分析 | 热重分析 | Truevision TGA 图像格式 | ✔ | ✔ | 标准 | |
拇指数据库 | 拇指数据库 | MS Windows Thumbs DB | ✔ | - | - | 仅基于 OLE2 复合文档格式 |
TIFF | TIFF | Aldus/Adobe 标记图像文件格式 | ✔ | ✔ | 原生、标准 | |
大TIFF | ✔ | ✔ | 原生、标准 | |||
网络P | 网络P | 谷歌 WebP 格式 | ✔ | - | 标准 | |
西华达 | 西华达 | X11 窗口转储格式 | ✔ | - | 标准 |
有关使用 Batik 的重要说明:请阅读 Apache™ XML 图形项目 - 安全性,并确保您使用更新且安全的版本。
请注意,使用 JDK 标准插件,ImageIO API 已支持 GIF、PNG 和 WBMP 格式。对于 BMP、JPEG 和 TIFF 格式,TwelveMonkeys 插件提供扩展格式支持和附加功能。
大多数时候,您所需要做的只是将插件包含在项目中并编写:
BufferedImage image = ImageIO . read ( file );
这会将文件的第一个图像完全加载到内存中。
基本且最简单的写作形式是:
if (! ImageIO . write ( image , format , file )) {
// Handle image not written case
}
这将使用给定格式的默认设置将整个图像写入单个文件。
这些插件会在运行时自动发现。有关此机制如何工作的更多信息,请参阅常见问题解答。
如果您需要更多地控制读取参数和读取过程,常见的读取习惯是这样的:
// Create input stream (in try-with-resource block to avoid leaks)
try ( ImageInputStream input = ImageIO . createImageInputStream ( file )) {
// Get the reader
Iterator < ImageReader > readers = ImageIO . getImageReaders ( input );
if (! readers . hasNext ()) {
throw new IllegalArgumentException ( "No reader for: " + file );
}
ImageReader reader = readers . next ();
try {
reader . setInput ( input );
// Optionally, listen for read warnings, progress, etc.
reader . addIIOReadWarningListener (...);
reader . addIIOReadProgressListener (...);
ImageReadParam param = reader . getDefaultReadParam ();
// Optionally, control read settings like sub sampling, source region or destination etc.
param . setSourceSubsampling (...);
param . setSourceRegion (...);
param . setDestination (...);
// ...
// Finally read the image, using settings from param
BufferedImage image = reader . read ( 0 , param );
// Optionally, read thumbnails, meta data, etc...
int numThumbs = reader . getNumThumbnails ( 0 );
// ...
}
finally {
// Dispose reader in finally block to avoid memory leaks
reader . dispose ();
}
}
使用reader.getWidth(n)
和reader.getHeight(n)
查询阅读器的源图像尺寸,而无需先将整个图像读入内存。
还可以使用reader.getNumImages()
循环从同一文件中读取多个图像。
如果您需要更多地控制写入参数和写入过程,常见的写入习惯是这样的:
// Get the writer
Iterator < ImageWriter > writers = ImageIO . getImageWritersByFormatName ( format );
if (! writers . hasNext ()) {
throw new IllegalArgumentException ( "No writer for: " + format );
}
ImageWriter writer = writers . next ();
try {
// Create output stream (in try-with-resource block to avoid leaks)
try ( ImageOutputStream output = ImageIO . createImageOutputStream ( file )) {
writer . setOutput ( output );
// Optionally, listen to progress, warnings, etc.
ImageWriteParam param = writer . getDefaultWriteParam ();
// Optionally, control format specific settings of param (requires casting), or
// control generic write settings like sub sampling, source region, output type etc.
// Optionally, provide thumbnails and image/stream metadata
writer . write (..., new IIOImage (..., image , ...), param );
}
}
finally {
// Dispose writer in finally block to avoid memory leaks
writer . dispose ();
}
有关更高级的用法以及如何使用 ImageIO API 的信息,我建议您阅读 Oracle 的 Java Image I/O API 指南。
import com . twelvemonkeys . imageio . path . Paths ;
...
try ( ImageInputStream stream = ImageIO . createImageInputStream ( new File ( "image_with_path.jpg" )) {
BufferedImage image = Paths . readClipped ( stream );
// Do something with the clipped image...
}
有关更多详细信息和示例代码,请参阅 Wiki 上的 Adobe Clipping Path 支持。
该库附带了重采样(图像调整大小)操作,其中包含许多不同的算法,可以以合理的速度提供出色的结果。
import com . twelvemonkeys . image . ResampleOp ;
...
BufferedImage input = ...; // Image to resample
int width , height = ...; // new width/height
BufferedImageOp resampler = new ResampleOp ( width , height , ResampleOp . FILTER_LANCZOS ); // A good default filter, see class documentation for more info
BufferedImage output = resampler . filter ( input , null );
该库附带了抖动操作,可用于使用 Floyd-Steinberg 误差扩散抖动将BufferedImage
转换为IndexColorModel
。
import com . twelvemonkeys . image . DiffusionDither ;
...
BufferedImage input = ...; // Image to dither
BufferedImageOp ditherer = new DiffusionDither ();
BufferedImage output = ditherer . filter ( input , null );
当使用正常模式加载图像时,尝试加载损坏的图像将导致抛出IOException
。
BufferedImage image = null ;
try {
image = ImageIO . read ( file );
} catch ( IOException exception ) {
// Handle, log a warning/error etc
}
在这种情况下,如果图像损坏,并且ImageIO.read
引发异常, image
仍然为null
- 函数不可能既返回值又引发异常。
然而,在某些情况下,可以从损坏的图像中获取可用的图像数据。执行此操作的方法是使用ImageReadParam
将BufferedImage
设置为目标。
int width = reader . getWidth ( 0 );
int height = reader . getHeight ( 0 );
ImageTypeSpecifier imageType = reader . getRawImageType ( 0 );
BufferedImage image = imageType . createBufferedImage ( width , height );
ImageReadParam param = reader . getDefaultReadParam ();
param . setDestination ( image );
try {
reader . read ( 0 , param );
}
catch ( IOException e ) {
// Handle, log a warning/error etc
}
理论上,这应该适用于所有插件,但结果很大程度上取决于插件/实现。对于某些格式和某些形式的损坏文件,您可能会得到最有用的图像。但是,您应该做好准备,这可能只会给出空白或空图像。
下载项目(使用 Git):
$ git clone [email protected]:haraldk/TwelveMonkeys.git
这应该在您的当前目录中创建一个名为TwelveMonkeys
的文件夹。将目录更改为TwelveMonkeys
文件夹,然后发出以下命令进行构建。
构建项目(使用 Maven):
$ mvn package
目前,推荐用于构建的 JDK 是 Oracle JDK 8.x。
可以使用 OpenJDK 进行构建,但由于所使用的颜色管理系统之间存在一些细微差异,某些测试可能会失败。您需要禁用相关测试,或者完全不进行测试进行构建。
由于单元测试需要相当多的内存才能运行,因此您可能必须设置环境变量MAVEN_OPTS
来为运行 Maven 的 Java 进程提供更多内存。我建议类似-Xmx512m -XX:MaxPermSize=256m
。
或者,您可以使用以下命令将项目安装在本地 Maven 存储库中:
$ mvn install
要安装插件,请使用 Maven 并向项目添加必要的依赖项,或者在类路径中手动添加所需的 JAR 以及所需的依赖项。
ImageIO 注册表和服务查找机制将确保插件可供使用。
要验证 JPEG 插件是否已安装并在运行时使用,您可以使用以下代码:
Iterator < ImageReader > readers = ImageIO . getImageReadersByFormatName ( "JPEG" );
while ( readers . hasNext ()) {
System . out . println ( "reader: " + readers . next ());
}
第一行应该打印:
reader: com.twelvemonkeys.imageio.plugins.jpeg.JPEGImageReader@somehash
要使用 Maven 依赖 JPEG 和 TIFF 插件,请将以下内容添加到 POM 中:
...
< dependencies >
...
< dependency >
< groupId >com.twelvemonkeys.imageio</ groupId >
< artifactId >imageio-jpeg</ artifactId >
< version >3.12.0</ version >
</ dependency >
< dependency >
< groupId >com.twelvemonkeys.imageio</ groupId >
< artifactId >imageio-tiff</ artifactId >
< version >3.12.0</ version >
</ dependency >
<!--
Optional dependency. Needed only if you deploy ImageIO plugins as part of a web app.
Make sure you add the IIOProviderContextListener to your web.xml, see above.
-->
< dependency >
< groupId >com.twelvemonkeys.servlet</ groupId >
< artifactId >servlet</ artifactId >
< version >3.12.0</ version >
</ dependency >
<!--
Or Jakarta version, for Servlet API 5.0
-->
< dependency >
< groupId >com.twelvemonkeys.servlet</ groupId >
< artifactId >servlet</ artifactId >
< version >3.12.0</ version >
< classifier >jakarta</ classifier >
</ dependency >
</ dependencies >
要依赖 IDE 或程序中的 JPEG 和 TIFF 插件,请将以下所有 JAR 添加到类路径中:
twelvemonkeys-common-lang-3.12.0.jar
twelvemonkeys-common-io-3.12.0.jar
twelvemonkeys-common-image-3.12.0.jar
twelvemonkeys-imageio-core-3.12.0.jar
twelvemonkeys-imageio-metadata-3.12.0.jar
twelvemonkeys-imageio-jpeg-3.12.0.jar
twelvemonkeys-imageio-tiff-3.12.0.jar
由于ImageIO
插件注册表( IIORegistry
)是“VM 全局”的,因此它不能很好地与 servlet 上下文一起工作。如果您从WEB-INF/lib
或classes
文件夹加载插件,这一点尤其明显。除非您在代码中的某个位置添加ImageIO.scanForPlugins()
,否则这些插件可能永远不可用。
此外,Servlet 上下文动态加载和卸载类(每个上下文使用新的类加载器)。如果重新启动应用程序,默认情况下旧类将永远保留在内存中(因为下次调用scanForPlugins
时,将由另一个ClassLoader
扫描/加载类,因此它们将成为注册表中的新实例)。如果尝试使用剩余的“旧”读取器之一进行读取,则可能会发生奇怪的异常(例如访问static final
初始化字段时的NullPointerException
或未初始化的内部类的NoClassDefFoundError
)。
为了解决发现问题和资源泄漏问题,强烈建议使用IIOProviderContextListener
来实现 Web 应用程序的 ImageIO 插件的动态加载和卸载。
< web-app ...>
...
< listener >
< display-name >ImageIO service provider loader/unloader</ display-name >
< listener-class >com.twelvemonkeys.servlet.image.IIOProviderContextListener</ listener-class >
</ listener >
...
</ web-app >
不支持在未安装上下文侦听器的情况下从WEB-INF/lib
加载插件,并且无法正常工作。
上下文侦听器不依赖于 TwelveMonkeys ImageIO 插件,并且也可以与 JAI ImageIO 或其他 ImageIO 插件一起使用。
另一个安全的选择是将 JAR 文件放置在应用程序服务器的共享或公共 lib 文件夹中。
对于那些从旧的javax.servlet
包过渡到新的jakarta.servlet
包的人,有一个单独的依赖项可用。它包含与上面提到的完全相同的 servlet 类,但是是针对新的 Jakarta EE 包构建的。该依赖项具有与以前相同的组名称和标识符,但附加了jakarta
分类器,以将其与非 Jakarta 包区分开。
请参阅 Maven 依赖项示例,了解如何使用 Maven 启用它。 Gradle 或其他构建工具也会有类似的选项。
使用插件的推荐方法是通过 Maven 依赖项或类似方式将 JAR 按原样包含在您的项目中。重新打包不是使用该库所必需的,也不建议这样做。
但是,如果您想要创建“胖”JAR,或者出于某种原因想要重新打包 JAR,请务必记住 ImageIO 对插件的自动发现取决于服务提供商接口 (SPI) 机制。简而言之,每个 JAR 都包含一个名为META-INF/services
特殊文件夹,其中包含一个或多个文件,通常是javax.imageio.spi.ImageReaderSpi
和javax.imageio.spi.ImageWriterSpi
。这些文件在每个 JAR 中都以相同的名称存在,因此如果您只是将所有内容解压到单个文件夹或创建一个 JAR,文件将被覆盖并且行为未指定(很可能您最终会安装一个插件)。
解决方案是确保所有具有相同名称的文件合并为一个文件,其中包含每种类型的所有 SPI 信息。如果使用 Maven Shade 插件,您应该使用 ServicesResourceTransformer 来正确合并这些文件。您可能还想使用 ManifestResourceTransforme 来获取正确的供应商名称、版本信息等。其他“胖”JAR 捆绑程序可能具有类似的机制来合并具有相同名称的条目。
在 Java 7 上运行的最新版本是 3.9.4。更高版本将需要 Java 8 或更高版本。
常见的依赖关系
图像IO依赖项
图像IO插件
需要 3rd 方库的 ImageIO 插件
Photoshop 对 ImageIO 的路径支持
Servlet 支持
该项目是根据 OSI 批准的 BSD 许可证提供的:
Copyright (c) 2008-2022, Harald Kuhr
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
o Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
o Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
o Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
问:我该如何使用它?
a:最简单的方法是使用 Maven、Gradle 或其他具有依赖管理功能的构建工具构建自己的项目,然后将依赖项添加到您需要的特定插件中。如果您不使用这样的构建工具,请确保类路径中具有所有必需的 JAR。请参阅上面的安装部分。
问:为了使用插件,我必须对代码进行哪些更改?
答:简短的回答是:没有。对于基本用法,例如ImageIO.read(...)
或ImageIO.getImageReaders(...)
,无需更改代码。大多数功能都可以通过标准 ImageIO API 获得,并且非常小心地避免在不必要的地方引入额外的 API。
如果您想使用某些格式的非常具体/高级的功能,您可能必须使用特定的 API,例如为由多个文件组成的 SVG 图像设置基本 URL,或控制 TIFF 文件的输出压缩。
问:它是如何工作的?
a:TwelveMonkeys ImageIO 项目包含 ImageIO 插件。 ImageIO 使用服务查找机制,在运行时发现插件。
您所要做的就是确保您的类路径中有 TwelveMonkeys ImageIO JAR。
您可以在 IIORegistry API 文档中阅读有关注册表和查找机制的更多信息。
细则:JPEG、BMP 和 TIFF 的 TwelveMonkeys 服务提供程序重写了 onRegistration 方法,并利用IIOServiceRegistry
的成对部分排序机制来确保它在 Sun/Oracle 提供的JPEGImageReader
、 BMPImageReader
TIFFImageReader
和 Apple 之前安装。分别在 OS X 上提供了TIFFImageReader
。使用成对排序不会删除这些实现中的任何功能,但在大多数情况下,您最终将使用 TwelveMonkeys 插件。
问:为什么不支持 GIF 或 PNG 等常见格式?
答:简单来说,ImageIO 对这些格式的内置支持被认为是足够好的。如果您正在寻找 Java 7 和 8 上更好的 PNG 写入性能,请参阅 JDK9 PNG Writer Backport。
问:下一个版本什么时候发布?目前的发布时间表是怎样的?
a:目标是每月发布一次,包含错误修复和次要新功能。每季度发布更多“主要”功能。
问:我喜欢这个项目!我能提供什么帮助吗?
a:查看未解决的问题,看看是否有任何您可以帮助修复的问题,或者提供示例文件或创建测试用例。您或您的组织也可以通过 GitHub Sponsors 成为赞助商。提供资金将使我们能够花更多的时间来修复错误和实现新功能。
问:JAI 怎么样? JAI 已经支持其中几种格式。
a:虽然 JAI(尤其是 jai-imageio)支持某些相同的格式,但 JAI 存在一些重大问题。最明显的是:
问:JMagick 或 IM4Java 怎么样?你不能只使用已经可用的东西吗?
a:虽然支持多种格式的优秀库,但与 ImageIO 相比,基于 ImageMagick 的库有一些缺点。
我们做到了