TwelveMonkeys ImageIO fornece suporte estendido ao formato de arquivo de imagem para a plataforma Java, por meio de plug-ins para o pacote javax.imageio.*
.
O principal objetivo deste projeto é fornecer suporte para formatos de arquivo não cobertos pelo JDK. O suporte para esses formatos é importante, para poder ler dados encontrados “em estado selvagem”, bem como para manter o acesso aos dados em formatos legados. Como existem muitos dados legados por aí, vemos a necessidade de implementações abertas de leitores para formatos populares.
Plug-in | Formatar | Descrição | R | C | Metadados | Notas |
---|---|---|---|---|---|---|
Batik | SVG | Gráficos vetoriais escaláveis | ✔ | - | - | Requer Batik |
WMF | Metarquivo MS Windows | ✔ | - | - | Requer Batik | |
Veículo de combate de infantaria | Veículo de combate de infantaria | Bitmap independente de dispositivo MS Windows e IBM OS/2 | ✔ | ✔ | Nativo, Padrão | |
CUR | Formato do cursor do MS Windows | ✔ | - | - | ||
OIC | Formato de ícone do MS Windows | ✔ | ✔ | - | ||
DDS | DDS | Formato de superfície de desenho direto MS | ✔ | - | Padrão | |
HDR | HDR | Formato RGBE de alta faixa dinâmica Radiance | ✔ | - | Padrão | |
ICNS | ICNS | Imagem do ícone da Apple | ✔ | ✔ | - | |
IFF | IFF | Formato de arquivo de intercâmbio Commodore Amiga/Electronic Arts | ✔ | ✔ | Padrão | |
JPEG | JPEG | Grupo conjunto de especialistas em fotógrafos | ✔ | ✔ | Nativo, Padrão | |
JPEG sem perdas | ✔ | - | Nativo, Padrão | |||
PCX | PCX | Formato pincel ZSoft | ✔ | - | Padrão | |
DCX | Documento de fax PCX de várias páginas | ✔ | - | Padrão | ||
IMAGEM | IMAGEM | Formato de imagem Apple QuickTime | ✔ | ✔ | Padrão | |
PNG | Formato de imagem Apple MacPaint | ✔ | - | Padrão | ||
PNM | PAM | NetPBM portátil qualquer mapa | ✔ | ✔ | Padrão | |
PBM | Mapa de bits portátil NetPBM | ✔ | - | Padrão | ||
PGM | Mapa cinza portátil NetPBM | ✔ | - | Padrão | ||
PPM | Mapa Pix Portátil NetPBM | ✔ | ✔ | Padrão | ||
GFP | Mapa flutuante portátil | ✔ | - | Padrão | ||
PSD | PSD | Documento Adobe Photoshop | ✔ | (✔) | Nativo, Padrão | |
PSB | Documento grande do Adobe Photoshop | ✔ | - | Nativo, Padrão | ||
SGI | SGI | Formato de imagem gráfica de silício | ✔ | - | Padrão | |
TGA | TGA | Formato de imagem Truevision TGA | ✔ | ✔ | Padrão | |
ThumbsDB | Thumbs.db | Banco de dados de polegares do MS Windows | ✔ | - | - | Somente formato baseado em documento composto OLE2 |
TIFF | TIFF | Formato de arquivo de imagem marcado Aldus/Adobe | ✔ | ✔ | Nativo, Padrão | |
GrandeTIFF | ✔ | ✔ | Nativo, Padrão | |||
WebP | WebP | Formato WebP do Google | ✔ | - | Padrão | |
XWD | XWD | Formato de despejo de janela X11 | ✔ | - | Padrão |
Observação importante sobre o uso do Batik: Leia The Apache™ XML Graphics Project - Segurança e certifique-se de usar uma versão atualizada e segura.
Observe que os formatos GIF, PNG e WBMP já são suportados pela API ImageIO, usando os plugins padrão JDK. Para os formatos BMP, JPEG e TIFF, os plug-ins TwelveMonkeys oferecem suporte estendido a formatos e recursos adicionais.
Na maioria das vezes, tudo que você precisa fazer é simplesmente incluir os plugins em seu projeto e escrever:
BufferedImage image = ImageIO . read ( file );
Isso carregará a primeira imagem do arquivo inteiramente na memória.
A forma básica e mais simples de escrita é:
if (! ImageIO . write ( image , format , file )) {
// Handle image not written case
}
Isso gravará a imagem inteira em um único arquivo, usando as configurações padrão do formato fornecido.
Os plugins são descobertos automaticamente em tempo de execução. Consulte o FAQ para obter mais informações sobre como esse mecanismo funciona.
Se você precisar de mais controle dos parâmetros de leitura e do processo de leitura, o idioma comum para leitura é algo como:
// 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 ();
}
}
Consulte o leitor sobre as dimensões da imagem de origem usando reader.getWidth(n)
e reader.getHeight(n)
sem primeiro ler a imagem inteira na memória.
Também é possível ler várias imagens do mesmo arquivo em loop, usando reader.getNumImages()
.
Se você precisar de mais controle dos parâmetros de gravação e do processo de escrita, a linguagem comum para escrita é algo como:
// 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 ();
}
Para uso mais avançado e informações sobre como usar a API ImageIO, sugiro que você leia o Java Image I/O API Guide da Oracle.
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...
}
Consulte o suporte do Adobe Clipping Path no Wiki para obter mais detalhes e exemplos de código.
A biblioteca vem com uma operação de reamostragem (redimensionamento de imagem), que contém diversos algoritmos diferentes para fornecer resultados excelentes em velocidade razoável.
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 );
A biblioteca vem com uma operação de pontilhamento, que pode ser usada para converter BufferedImage
s em IndexColorModel
usando pontilhamento de difusão de erro Floyd-Steinberg.
import com . twelvemonkeys . image . DiffusionDither ;
...
BufferedImage input = ...; // Image to dither
BufferedImageOp ditherer = new DiffusionDither ();
BufferedImage output = ditherer . filter ( input , null );
Ao usar os padrões normais para carregar imagens, tentar carregar uma imagem danificada resultará no lançamento de uma IOException
.
BufferedImage image = null ;
try {
image = ImageIO . read ( file );
} catch ( IOException exception ) {
// Handle, log a warning/error etc
}
Nesse cenário, se a imagem estiver danificada e ImageIO.read
lançar uma exceção, image
ainda será null
- não é possível que uma função retorne um valor e lance uma exceção.
No entanto, em alguns casos, pode ser possível obter dados de imagem utilizáveis a partir de uma imagem danificada. A maneira de fazer isso é usar um ImageReadParam
para definir um BufferedImage
como destino.
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
}
Em teoria, isso deveria funcionar para todos os plug-ins, mas o resultado é muito específico do plug-in/implementação. Com alguns formatos e algumas formas de arquivo danificado, você pode obter uma imagem que é muito útil. No entanto, você deve estar preparado para a possibilidade de isso gerar apenas uma imagem em branco ou vazia.
Baixe o projeto (usando Git):
$ git clone [email protected]:haraldk/TwelveMonkeys.git
Isso deve criar uma pasta chamada TwelveMonkeys
em seu diretório atual. Mude o diretório para a pasta TwelveMonkeys
e emita o comando abaixo para construir.
Construa o projeto (usando Maven):
$ mvn package
Atualmente, o JDK recomendado para fazer um build é o Oracle JDK 8.x.
É possível construir usando OpenJDK, mas alguns testes podem falhar devido a algumas pequenas diferenças entre os sistemas de gerenciamento de cores usados. Você precisará desabilitar os testes em questão ou compilar sem testes.
Como os testes de unidade precisam de bastante memória para serem executados, talvez seja necessário definir a variável de ambiente MAVEN_OPTS
para fornecer mais memória ao processo Java que executa o Maven. Sugiro algo como -Xmx512m -XX:MaxPermSize=256m
.
Opcionalmente, você pode instalar o projeto em seu repositório Maven local usando:
$ mvn install
Para instalar os plug-ins, use o Maven e adicione as dependências necessárias ao seu projeto ou adicione manualmente os JARs necessários junto com as dependências necessárias no caminho de classe.
O registro do ImageIO e o mecanismo de pesquisa de serviço garantirão que os plug-ins estejam disponíveis para uso.
Para verificar se o plugin JPEG está instalado e usado em tempo de execução, você pode usar o seguinte código:
Iterator < ImageReader > readers = ImageIO . getImageReadersByFormatName ( "JPEG" );
while ( readers . hasNext ()) {
System . out . println ( "reader: " + readers . next ());
}
A primeira linha deve imprimir:
reader: com.twelvemonkeys.imageio.plugins.jpeg.JPEGImageReader@somehash
Para depender do plugin JPEG e TIFF usando Maven, adicione o seguinte ao seu 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 >
Para depender do plug-in JPEG e TIFF em seu IDE ou programa, adicione todos os JARs a seguir ao seu caminho de classe:
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
Como o registro do plugin ImageIO
(o IIORegistry
) é "VM global", ele não funciona bem com contextos de servlet como estão. Isto é especialmente evidente se você carregar plugins da pasta WEB-INF/lib
ou classes
. A menos que você adicione ImageIO.scanForPlugins()
em algum lugar do seu código, os plug-ins podem nunca estar disponíveis.
Além disso, os contextos de servlet carregam e descarregam classes dinamicamente (usando um novo carregador de classes por contexto). Se você reiniciar seu aplicativo, as classes antigas permanecerão, por padrão, na memória para sempre (porque na próxima vez que scanForPlugins
for chamado, será outro ClassLoader
que varrerá/carregará as classes e, portanto, elas serão novas instâncias no registro). Se uma leitura for tentada usando um dos leitores "antigos" restantes, exceções estranhas (como NullPointerException
s ao acessar campos static final
inicializados ou NoClassDefFoundError
s para classes internas não inicializadas) poderão ocorrer.
Para contornar o problema de descoberta e o vazamento de recursos, é altamente recomendável usar o IIOProviderContextListener
que implementa carregamento e descarregamento dinâmico de plug-ins ImageIO para aplicativos da web.
< web-app ...>
...
< listener >
< display-name >ImageIO service provider loader/unloader</ display-name >
< listener-class >com.twelvemonkeys.servlet.image.IIOProviderContextListener</ listener-class >
</ listener >
...
</ web-app >
Carregar plug-ins de WEB-INF/lib
sem o ouvinte de contexto instalado não é suportado e não funcionará corretamente.
O ouvinte de contexto não tem dependências dos plug-ins TwelveMonkeys ImageIO e também pode ser usado com JAI ImageIO ou outros plug-ins ImageIO.
Outra opção segura é colocar os arquivos JAR na pasta lib compartilhada ou comum do servidor de aplicativos.
Para aqueles que estão fazendo a transição do antigo javax.servlet
para o novo pacote jakarta.servlet
, há uma dependência separada disponível. Ele contém exatamente as mesmas classes de servlet mencionadas acima, mas construídas com base nos novos pacotes Jakarta EE. A dependência tem o mesmo nome de grupo e identificador de antes, mas um classificador jakarta
anexado, para distingui-la do pacote não-Jakarta.
Veja o exemplo de dependência do Maven para saber como habilitá-lo com o Maven. Gradle ou outras ferramentas de construção terão opções semelhantes.
A forma recomendada de usar os plugins é apenas incluir os JARs como estão em seu projeto, através de uma dependência do Maven ou similar. A reembalagem não é necessária para usar a biblioteca e não é recomendada.
No entanto, se você deseja criar um JAR "gordo" ou, por algum motivo, deseja reempacotar os JARs, é importante lembrar que a descoberta automática dos plug-ins pelo ImageIO depende do mecanismo Service Provider Interface (SPI). Resumindo, cada JAR contém uma pasta especial, chamada META-INF/services
contendo um ou mais arquivos, normalmente javax.imageio.spi.ImageReaderSpi
e javax.imageio.spi.ImageWriterSpi
. Esses arquivos existem com o mesmo nome em todos os JAR , portanto, se você simplesmente descompactar tudo em uma única pasta ou criar um JAR, os arquivos serão sobrescritos e o comportamento não será especificado (provavelmente você acabará com um único plugin sendo instalado).
A solução é garantir que todos os arquivos com o mesmo nome sejam mesclados em um único arquivo, contendo todas as informações SPI de cada tipo. Se estiver usando o plugin Maven Shade, você deve usar o ServicesResourceTransformer para mesclar esses arquivos corretamente. Você também pode querer usar o ManifestResourceTransforme para obter o nome correto do fornecedor, informações de versão, etc. Outros empacotadores JAR "gordos" provavelmente terão mecanismos semelhantes para mesclar entradas com o mesmo nome.
A versão mais recente que será executada no Java 7 é a 3.9.4. Versões posteriores exigirão Java 8 ou posterior.
Dependências comuns
Dependências do ImageIO
Plug-ins ImageIO
Plug-ins ImageIO que exigem bibliotecas de terceiros
Suporte do Photoshop Path para ImageIO
Suporte a servlets
Este projeto é fornecido sob a licença BSD aprovada pela OSI:
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.
p: Como faço para usá-lo?
a: A maneira mais fácil é construir seu próprio projeto usando Maven, Gradle ou outra ferramenta de construção com gerenciamento de dependências e apenas adicionar dependências aos plug-ins específicos que você precisa. Se você não usar essa ferramenta de construção, certifique-se de ter todos os JARs necessários no caminho de classe. Consulte a seção Instalação acima.
p: Que alterações devo fazer em meu código para poder usar os plug-ins?
a: A resposta curta é: Nenhuma. Para uso básico, como ImageIO.read(...)
ou ImageIO.getImageReaders(...)
, não há necessidade de alterar seu código. A maior parte da funcionalidade está disponível por meio de APIs ImageIO padrão, e muito cuidado foi tomado para não introduzir API extra onde nenhuma for necessária.
Se você quiser usar recursos muito específicos/avançados de alguns dos formatos, talvez seja necessário usar APIs específicas, como definir URL base para uma imagem SVG que consiste em vários arquivos ou controlar a compactação de saída de um arquivo TIFF.
p: Como funciona?
a: O projeto TwelveMonkeys ImageIO contém plug-ins para ImageIO. ImageIO usa um mecanismo de pesquisa de serviço para descobrir plug-ins em tempo de execução.
Tudo o que você precisa fazer é certificar-se de ter os JARs TwelveMonkeys ImageIO em seu caminho de classe.
Você pode ler mais sobre o registro e o mecanismo de pesquisa no documento da API IIORegistry.
As letras miúdas: os provedores de serviços TwelveMonkeys para JPEG, BMP e TIFF, substituem o método onRegistration e utilizam o mecanismo de ordenação parcial em pares do IIOServiceRegistry
para garantir que ele seja instalado antes do JPEGImageReader
, BMPImageReader
TIFFImageReader
fornecido pela Sun/Oracle e o Apple forneceu TIFFImageReader
no OS X, respectivamente. Usar a ordem em pares não removerá nenhuma funcionalidade dessas implementações, mas na maioria dos casos você acabará usando os plug-ins TwelveMonkeys.
q: Por que não há suporte para formatos comuns como GIF ou PNG?
a: A resposta curta é simplesmente que o suporte integrado no ImageIO para esses formatos é considerado bom o suficiente no estado em que se encontra. Se você está procurando melhor desempenho de gravação de PNG em Java 7 e 8, consulte JDK9 PNG Writer Backport.
q: Quando é o próximo lançamento? Qual é o cronograma de lançamento atual?
a: O objetivo é fazer lançamentos mensais, contendo correções de bugs e pequenas novidades. E lançamentos trimestrais com mais recursos “principais”.
q: Eu amo esse projeto! Como posso ajudar?
a: Dê uma olhada nos problemas em aberto e veja se há algum problema que você possa ajudar a corrigir ou forneça um arquivo de amostra ou crie casos de teste. Também é possível que você ou sua organização se tornem patrocinadores, por meio do GitHub Sponsors. Fornecer financiamento nos permitirá gastar mais tempo corrigindo bugs e implementando novos recursos.
q: E quanto ao JAI? Vários dos formatos já são suportados pelo JAI.
a: Embora o JAI (e o jai-imageio em particular) tenham suporte para alguns dos mesmos formatos, o JAI tem alguns problemas importantes. O mais óbvio é:
p: E quanto ao JMagick ou IM4Java? Você não pode simplesmente usar o que já está disponível?
a: Embora grandes bibliotecas com uma ampla variedade de formatos sejam suportadas, as bibliotecas baseadas em ImageMagick têm algumas desvantagens em comparação com ImageIO.
Nós fizemos isso