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 來取得正確的供應商名稱、版本資訊等。
在 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.
Q:我該如何使用它?
a:最簡單的方法是使用 Maven、Gradle 或其他具有依賴管理功能的建置工具來建立自己的項目,然後將依賴項新增到您需要的特定插件中。如果您不使用這樣的建置工具,請確保類別路徑中具有所有必要的 JAR。請參閱上面的安裝部分。
Q:為了使用插件,我必須對程式碼進行哪些更改?
答:簡短的回答是:沒有。對於基本用法,例如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 上安裝。提供了TIFFImageReader
。使用成對排序不會刪除這些實作中的任何功能,但在大多數情況下,您最終將使用 TwelveMonkeys 外掛程式。
Q:為什麼不支援 GIF 或 PNG 等常見格式?
答:簡單來說,ImageIO 對這些格式的內建支援被認為是足夠好的。如果您正在尋找 Java 7 和 8 上更好的 PNG 寫入效能,請參閱 JDK9 PNG Writer Backport。
Q:下一個版本什麼時候發布?目前的發佈時間表是怎樣的?
a:目標是每月發布一次,包含錯誤修復和次要新功能。每季發布更多「主要」功能。
Q:我喜歡這個項目!我能提供什麼幫助嗎?
a:查看未解決的問題,看看是否有任何您可以協助修復的問題,或提供範例檔案或建立測試案例。您或您的組織也可以透過 GitHub Sponsors 成為贊助商。提供資金將使我們能夠花更多的時間來修復錯誤並實現新功能。
Q:JAI 怎麼樣? JAI 已經支援其中幾種格式。
a:雖然 JAI(尤其是 jai-imageio)支援某些相同的格式,但 JAI 存在一些重大問題。最明顯的是:
Q:JMagick 或 IM4Java 怎麼樣?你不能只使用已經可用的東西嗎?
a:雖然支援多種格式的優秀函式庫,但與 ImageIO 相比,基於 ImageMagick 的函式庫有一些缺點。
我們做到了