mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2026-05-19 00:00:03 -04:00
Compare commits
202 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c61e47b3e7 | |||
| 1d47d2ef90 | |||
| 94554b0660 | |||
| a39bca4d2f | |||
| fdbbcc54a8 | |||
| 00cd9471dd | |||
| eb6a3bde39 | |||
| d3dd4c29d5 | |||
| 3355acfa03 | |||
| b70e52561a | |||
| 49ddbfa85e | |||
| 215db21569 | |||
| 0ac8011053 | |||
| abbbca9be3 | |||
| bc5473aec6 | |||
| aca8b1256a | |||
| 18c1edf0e5 | |||
| 6a01466ebb | |||
| b19df1640b | |||
| e8e3e0f1d6 | |||
| c249a21c8c | |||
| 182b2fdfa9 | |||
| df068e350d | |||
| b55c623b87 | |||
| 09573b52ac | |||
| ff7edbd223 | |||
| d8f0cd97a2 | |||
| f6971a9bce | |||
| 821965df0d | |||
| ed46305d31 | |||
| ca3adb7c45 | |||
| 59f76209bc | |||
| 2076235313 | |||
| 88bebf31f8 | |||
| 37beb21c29 | |||
| 88c0d27516 | |||
| dcd4ffccf4 | |||
| f35f5c6b24 | |||
| 9dcf53d985 | |||
| 44a2066b79 | |||
| 382c51db7c | |||
| 660f3f7e86 | |||
| b4c30872c4 | |||
| 402d4d466d | |||
| 7860bf7e17 | |||
| d52522fb80 | |||
| 8a8c6b1931 | |||
| 2930708a54 | |||
| 5661e7459c | |||
| 4d45ea4966 | |||
| 34852f7be5 | |||
| abc929a531 | |||
| 247a09ca61 | |||
| c2880fe793 | |||
| c9e522475b | |||
| 1da16e4d7a | |||
| ab7b08dfa9 | |||
| e0d6fa0d84 | |||
| 51bdd370da | |||
| ee2be3f88f | |||
| c5511833cc | |||
| 6ac8a5d8b4 | |||
| 3f7cb24407 | |||
| 8bf9f7a8f0 | |||
| 03ab9558a0 | |||
| 715bde8358 | |||
| 0151efb5f6 | |||
| bd796429c5 | |||
| b4ef5823f3 | |||
| 9adf0f4da3 | |||
| 01a4e55185 | |||
| 2e2ab11091 | |||
| 419ffc9373 | |||
| b67975eef7 | |||
| b32a861b2d | |||
| 6930168c93 | |||
| fac9f1a927 | |||
| 913a03608c | |||
| 46ce99e10f | |||
| 3e4460ac41 | |||
| 5b7fc25520 | |||
| 42196e8513 | |||
| bc07524e7a | |||
| 0011b9a480 | |||
| 7b09ec8919 | |||
| 9c8977062d | |||
| b01b820ec8 | |||
| b61f2c179c | |||
| 967f8e6984 | |||
| bb650e5280 | |||
| 3b34d6e7ce | |||
| db782cfe9e | |||
| 96223f9f9f | |||
| da45c5783d | |||
| 5b6c819ac4 | |||
| 6d41f2db86 | |||
| ba0bb7b903 | |||
| d03dc28764 | |||
| 20a785ea5e | |||
| 0286fa4268 | |||
| 85fb9e6af3 | |||
| 97a8806bfb | |||
| 970f4f3a7e | |||
| 6d192968d1 | |||
| f5959af2e1 | |||
| ea74ac2714 | |||
| 80c595cea8 | |||
| fbc738f2d4 | |||
| 3e3acf3332 | |||
| 0a77520d67 | |||
| 72cd3aade3 | |||
| 88bd9cd2ba | |||
| 5ee8678a29 | |||
| fb1937ae63 | |||
| de02e3d7e0 | |||
| ebaa69713f | |||
| 8a1a90dafd | |||
| 6f6e65be12 | |||
| 253f04066b | |||
| 74902b3fb4 | |||
| af1a6492d4 | |||
| 0da007ec8c | |||
| 9053fb3816 | |||
| c1d4e474f0 | |||
| 6bac13eb84 | |||
| 0e48ddd306 | |||
| 8682decbbc | |||
| bb615b90bf | |||
| cb0c320b45 | |||
| 73044bea58 | |||
| 3bb312e9e1 | |||
| c7d2f422b8 | |||
| 4dedf76ebc | |||
| 2376d16ffd | |||
| 1fe0bdd41f | |||
| 1b4d25342f | |||
| bc391550fb | |||
| b563f573de | |||
| 25150b421c | |||
| 94031a2913 | |||
| 64fb421b38 | |||
| 78af95d747 | |||
| 1d4f681b8f | |||
| eda2cd76db | |||
| 4adc60a6c6 | |||
| 0d5577a9a4 | |||
| 918f92aba7 | |||
| 7a24d55be7 | |||
| a84cc1c060 | |||
| 31cb79d2b9 | |||
| d995e7baa0 | |||
| e7fe6d5c22 | |||
| 918b698e50 | |||
| 2427b2323f | |||
| 0a8222fea3 | |||
| 60a00b89ae | |||
| 4c88efa19d | |||
| 17d65a1f6f | |||
| fcd03eb903 | |||
| 4e69efce28 | |||
| 16caec4a22 | |||
| 08282ea09d | |||
| c04fed1aff | |||
| 97e788883a | |||
| a16fce0749 | |||
| 26e2fa0168 | |||
| 120deb3ad4 | |||
| 0a9e2df5de | |||
| 6ffcb88872 | |||
| 960e764c7b | |||
| d88f27b251 | |||
| e5b3e9755e | |||
| 6c34fb211f | |||
| 9fdbc3b1fc | |||
| 622c6f40d4 | |||
| 107da17ca9 | |||
| f9871b73a3 | |||
| 7605b646fe | |||
| 19c62ac7da | |||
| a5e4412d1a | |||
| 651246566a | |||
| fe8f854b17 | |||
| a4d20a4af4 | |||
| 0643d5910a | |||
| c78a456985 | |||
| 27017576d3 | |||
| f1810be10a | |||
| 021aba1a98 | |||
| a0b68adff3 | |||
| fa4586663c | |||
| 623d13a517 | |||
| a7ebc1b52f | |||
| f54f4370c0 | |||
| 5040e9fe8a | |||
| fc72cd34a1 | |||
| 6d71a3d306 | |||
| 86f8cf52a5 | |||
| bda6544a5f | |||
| 49c7cd1979 | |||
| 9dae58d5a6 | |||
| ed14b97199 | |||
| b94135a91c |
@@ -0,0 +1 @@
|
||||
github: haraldk
|
||||
+10
-8
@@ -1,14 +1,16 @@
|
||||
dist: trusty
|
||||
language: java
|
||||
jdk:
|
||||
- oraclejdk8
|
||||
# Oracle JDK 7 no longer supported, we use env matrix to test various CMM providers
|
||||
# - oraclejdk7
|
||||
# Some JPEGImageReader tests fail on OpenJDK, need to investigate/fix before enabling
|
||||
# - openjdk7
|
||||
env:
|
||||
- MAVEN_OPTS=-Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider
|
||||
- MAVEN_OPTS=""
|
||||
- oraclejdk8 # LTS until Mar 2022
|
||||
- oraclejdk11 # LTS until Sep 2023
|
||||
- oraclejdk15 # out of support
|
||||
- oraclejdk16 # out of support
|
||||
- oraclejdk17 # LTS until Sep 2026
|
||||
jobs:
|
||||
include:
|
||||
# Extra job, testing legacy CMM option
|
||||
- jdk: oraclejdk8
|
||||
env: MAVEN_OPTS=-Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.m2
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
[](https://travis-ci.org/haraldk/TwelveMonkeys)
|
||||
[](https://maven-badges.herokuapp.com/maven-central/com.twelvemonkeys.imageio/imageio)
|
||||
[](https://stackoverflow.com/questions/tagged/twelvemonkeys)
|
||||
[](https://travis-ci.com/github/haraldk/TwelveMonkeys)
|
||||
[](https://maven-badges.herokuapp.com/maven-central/com.twelvemonkeys.imageio/imageio)
|
||||
[](https://stackoverflow.com/questions/tagged/twelvemonkeys)
|
||||
[](https://paypal.me/haraldk76/100)
|
||||
|
||||
## About
|
||||
|
||||
TwelveMonkeys ImageIO is a collection of plugins and extensions for Java's ImageIO.
|
||||
TwelveMonkeys ImageIO provides extended image file format support for the Java platform, through plugins for the `javax.imageio.*` package.
|
||||
|
||||
These plugins extend the number of image file formats supported in Java, using the `javax.imageio.*` package.
|
||||
The main purpose of this project is to provide support for formats not covered by the JRE itself.
|
||||
|
||||
Support for formats is important, both to be able to read data found
|
||||
The main goal of this project is to provide support for formats not covered by the JRE itself.
|
||||
Support for these formats is important, to be able to read data found
|
||||
"in the wild", as well as to maintain access to data in legacy formats.
|
||||
Because there is lots of legacy data out there, we see the need for open implementations of readers for popular formats.
|
||||
The goal is to create a set of efficient and robust ImageIO plug-ins, that can be distributed independently.
|
||||
As there is lots of legacy data out there, we see the need for open implementations of readers for popular formats.
|
||||
|
||||
----
|
||||
|
||||
@@ -23,34 +20,40 @@ The goal is to create a set of efficient and robust ImageIO plug-ins, that can b
|
||||
| ------ | -------- | ----------- |:----:|:-----:| -------- | ----- |
|
||||
| Batik | **SVG** | Scalable Vector Graphics | âś” | - | - | Requires [Batik](https://xmlgraphics.apache.org/batik/)
|
||||
| | WMF | MS Windows Metafile | âś” | - | - | Requires [Batik](https://xmlgraphics.apache.org/batik/)
|
||||
| [BMP](https://github.com/haraldk/TwelveMonkeys/wiki/BMP-Plugin) | **BMP** | MS Windows and IBM OS/2 Device Independent Bitmap | âś” | âś” | Native & Standard |
|
||||
| [BMP](https://github.com/haraldk/TwelveMonkeys/wiki/BMP-Plugin) | **BMP** | MS Windows and IBM OS/2 Device Independent Bitmap | âś” | âś” | [Native](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/bmp_metadata.html) & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| | CUR | MS Windows Cursor Format | âś” | - | - |
|
||||
| | ICO | MS Windows Icon Format | âś” | âś” | - |
|
||||
| [HDR](https://github.com/haraldk/TwelveMonkeys/wiki/HDR-Plugin) | HDR | Radiance High Dynamic Range RGBE Format | âś” | - | Standard |
|
||||
| [HDR](https://github.com/haraldk/TwelveMonkeys/wiki/HDR-Plugin) | HDR | Radiance High Dynamic Range RGBE Format | âś” | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| [ICNS](https://github.com/haraldk/TwelveMonkeys/wiki/ICNS-Plugin) | ICNS | Apple Icon Image | âś” | âś” | - |
|
||||
| [IFF](https://github.com/haraldk/TwelveMonkeys/wiki/IFF-Plugin) | IFF | Commodore Amiga/Electronic Arts Interchange File Format | âś” | âś” | - |
|
||||
| [JPEG](https://github.com/haraldk/TwelveMonkeys/wiki/JPEG-Plugin) | **JPEG** | Joint Photographers Expert Group | âś” | âś” | Native & Standard |
|
||||
| [PCX](https://github.com/haraldk/TwelveMonkeys/wiki/PCX-Plugin) | PCX | ZSoft Paintbrush Format | âś” | - | Standard |
|
||||
| | DCX | Multi-page PCX fax document | âś” | - | Standard |
|
||||
| [PICT](https://github.com/haraldk/TwelveMonkeys/wiki/PICT-Plugin) | PICT | Apple Mac Paint Picture Format | âś” | - | - |
|
||||
| [PNM](https://github.com/haraldk/TwelveMonkeys/wiki/PNM-Plugin) | PAM | NetPBM Portable Any Map | âś” | âś” | Standard |
|
||||
| | PBM | NetPBM Portable Bit Map | âś” | - | Standard |
|
||||
| | PGM | NetPBM Portable Grey Map | âś” | - | Standard |
|
||||
| | PPM | NetPBM Portable Pix Map | âś” | âś” | Standard |
|
||||
| | PFM | Portable Float Map | âś” | - | Standard |
|
||||
| [PSD](https://github.com/haraldk/TwelveMonkeys/wiki/PSD-Plugin) | **PSD** | Adobe Photoshop Document | âś” | - | Native & Standard |
|
||||
| | PSB | Adobe Photoshop Large Document | âś” | - | Native & Standard |
|
||||
| [SGI](https://github.com/haraldk/TwelveMonkeys/wiki/SGI-Plugin) | SGI | Silicon Graphics Image Format | âś” | - | Standard |
|
||||
| [TGA](https://github.com/haraldk/TwelveMonkeys/wiki/TGA-Plugin) | TGA | Truevision TGA Image Format | âś” | âś” | Standard |
|
||||
| [IFF](https://github.com/haraldk/TwelveMonkeys/wiki/IFF-Plugin) | IFF | Commodore Amiga/Electronic Arts Interchange File Format | âś” | âś” | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| [JPEG](https://github.com/haraldk/TwelveMonkeys/wiki/JPEG-Plugin) | **JPEG** | Joint Photographers Expert Group | âś” | âś” | [Native](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/jpeg_metadata.html#image) & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| | JPEG Lossless | | âś” | - | [Native](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/jpeg_metadata.html#image) & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| [PCX](https://github.com/haraldk/TwelveMonkeys/wiki/PCX-Plugin) | PCX | ZSoft Paintbrush Format | âś” | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| | DCX | Multi-page PCX fax document | âś” | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| [PICT](https://github.com/haraldk/TwelveMonkeys/wiki/PICT-Plugin) | PICT | Apple QuickTime Picture Format | âś” | âś” | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| | PNTG | Apple MacPaint Picture Format | âś” | | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| [PNM](https://github.com/haraldk/TwelveMonkeys/wiki/PNM-Plugin) | PAM | NetPBM Portable Any Map | âś” | âś” | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| | PBM | NetPBM Portable Bit Map | âś” | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| | PGM | NetPBM Portable Grey Map | âś” | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| | PPM | NetPBM Portable Pix Map | âś” | âś” | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| | PFM | Portable Float Map | âś” | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| [PSD](https://github.com/haraldk/TwelveMonkeys/wiki/PSD-Plugin) | **PSD** | Adobe Photoshop Document | âś” | - | Native & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| | PSB | Adobe Photoshop Large Document | âś” | - | Native & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| [SGI](https://github.com/haraldk/TwelveMonkeys/wiki/SGI-Plugin) | SGI | Silicon Graphics Image Format | âś” | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| [TGA](https://github.com/haraldk/TwelveMonkeys/wiki/TGA-Plugin) | TGA | Truevision TGA Image Format | âś” | âś” | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
|ThumbsDB| Thumbs.db| MS Windows Thumbs DB | âś” | - | - | OLE2 Compound Document based format only
|
||||
| [TIFF](https://github.com/haraldk/TwelveMonkeys/wiki/TIFF-Plugin) | **TIFF** | Aldus/Adobe Tagged Image File Format | âś” | âś” | Native & Standard |
|
||||
| | BigTIFF | | âś” | - | Native & Standard |
|
||||
| [WebP](https://github.com/haraldk/TwelveMonkeys/wiki/WebP-Plugin) | **WebP** | Google WebP Format | âś” | - | Standard | In progress
|
||||
| XWD | XWD | X11 Window Dump Format | âś” | - | Standard |
|
||||
| [TIFF](https://github.com/haraldk/TwelveMonkeys/wiki/TIFF-Plugin) | **TIFF** | Aldus/Adobe Tagged Image File Format | âś” | âś” | [Native](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/tiff_metadata.html#ImageMetadata) & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| | BigTIFF | | âś” | - | [Native](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/tiff_metadata.html#ImageMetadata) & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| [WebP](https://github.com/haraldk/TwelveMonkeys/wiki/WebP-Plugin) | **WebP** | Google WebP Format | âś” | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| XWD | XWD | X11 Window Dump Format | âś” | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
|
||||
|
||||
**Important note on using Batik:** *Please read [The Apache™ XML Graphics Project - Security](http://xmlgraphics.apache.org/security.html),
|
||||
and make sure you use either version 1.6.1, 1.7.1, 1.8+ or later.*
|
||||
**Important note on using Batik:** *Please read [The Apache™ XML Graphics Project - Security](https://xmlgraphics.apache.org/security.html),
|
||||
and make sure you use version 1.14 or later.*
|
||||
|
||||
Note that GIF, PNG and WBMP formats are already supported through the ImageIO API, using the
|
||||
[JDK standard plugins](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/package-summary.html).
|
||||
For BMP, JPEG, and TIFF formats the TwelveMonkeys plugins provides extended format support and additional features.
|
||||
|
||||
## Basic usage
|
||||
|
||||
@@ -79,10 +82,8 @@ The plugins are discovered automatically at run time. See the [FAQ](#faq) for mo
|
||||
If you need more control of read parameters and the reading process, the common idiom for reading is something like:
|
||||
|
||||
```java
|
||||
// Create input stream
|
||||
ImageInputStream input = ImageIO.createImageInputStream(file);
|
||||
|
||||
try {
|
||||
// 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);
|
||||
|
||||
@@ -119,10 +120,6 @@ try {
|
||||
reader.dispose();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
// Close stream in finally block to avoid resource leaks
|
||||
input.close();
|
||||
}
|
||||
```
|
||||
|
||||
Query the reader for source image dimensions using `reader.getWidth(n)` and `reader.getHeight(n)` without reading the
|
||||
@@ -144,10 +141,8 @@ if (!writers.hasNext()) {
|
||||
ImageWriter writer = writers.next();
|
||||
|
||||
try {
|
||||
// Create output stream
|
||||
ImageOutputStream output = ImageIO.createImageOutputStream(file);
|
||||
|
||||
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.
|
||||
@@ -160,10 +155,6 @@ try {
|
||||
// Optionally, provide thumbnails and image/stream metadata
|
||||
writer.write(..., new IIOImage(..., image, ...), param);
|
||||
}
|
||||
finally {
|
||||
// Close stream in finally block to avoid resource leaks
|
||||
output.close();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
// Dispose writer in finally block to avoid memory leaks
|
||||
@@ -172,7 +163,7 @@ finally {
|
||||
```
|
||||
|
||||
For more advanced usage, and information on how to use the ImageIO API, I suggest you read the
|
||||
[Java Image I/O API Guide](http://docs.oracle.com/javase/7/docs/technotes/guides/imageio/spec/imageio_guideTOC.fm.html)
|
||||
[Java Image I/O API Guide](https://docs.oracle.com/javase/7/docs/technotes/guides/imageio/spec/imageio_guideTOC.fm.html)
|
||||
from Oracle.
|
||||
|
||||
#### Adobe Clipping Path support
|
||||
@@ -226,14 +217,14 @@ BufferedImage output = ditherer.filter(input, null);
|
||||
|
||||
## Building
|
||||
|
||||
Download the project (using [Git](http://git-scm.com/downloads)):
|
||||
Download the project (using [Git](https://git-scm.com/downloads)):
|
||||
|
||||
$ git clone git@github.com:haraldk/TwelveMonkeys.git
|
||||
|
||||
This should create a folder named `TwelveMonkeys` in your current directory. Change directory to the `TwelveMonkeys`
|
||||
folder, and issue the command below to build.
|
||||
|
||||
Build the project (using [Maven](http://maven.apache.org/download.cgi)):
|
||||
Build the project (using [Maven](https://maven.apache.org/download.cgi)):
|
||||
|
||||
$ mvn package
|
||||
|
||||
@@ -280,12 +271,12 @@ To depend on the JPEG and TIFF plugin using Maven, add the following to your POM
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-jpeg</artifactId>
|
||||
<version>3.6.2</version>
|
||||
<version>3.7.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-tiff</artifactId>
|
||||
<version>3.6.2</version>
|
||||
<version>3.7.0</version>
|
||||
</dependency>
|
||||
|
||||
<!--
|
||||
@@ -295,7 +286,7 @@ To depend on the JPEG and TIFF plugin using Maven, add the following to your POM
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.servlet</groupId>
|
||||
<artifactId>servlet</artifactId>
|
||||
<version>3.6.2</version>
|
||||
<version>3.7.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
```
|
||||
@@ -304,13 +295,13 @@ To depend on the JPEG and TIFF plugin using Maven, add the following to your POM
|
||||
|
||||
To depend on the JPEG and TIFF plugin in your IDE or program, add all of the following JARs to your class path:
|
||||
|
||||
twelvemonkeys-common-lang-3.6.2.jar
|
||||
twelvemonkeys-common-io-3.6.2.jar
|
||||
twelvemonkeys-common-image-3.6.2.jar
|
||||
twelvemonkeys-imageio-core-3.6.2.jar
|
||||
twelvemonkeys-imageio-metadata-3.6.2.jar
|
||||
twelvemonkeys-imageio-jpeg-3.6.2.jar
|
||||
twelvemonkeys-imageio-tiff-3.6.2.jar
|
||||
twelvemonkeys-common-lang-3.7.0.jar
|
||||
twelvemonkeys-common-io-3.7.0.jar
|
||||
twelvemonkeys-common-image-3.7.0.jar
|
||||
twelvemonkeys-imageio-core-3.7.0.jar
|
||||
twelvemonkeys-imageio-metadata-3.7.0.jar
|
||||
twelvemonkeys-imageio-jpeg-3.7.0.jar
|
||||
twelvemonkeys-imageio-tiff-3.7.0.jar
|
||||
|
||||
#### Deploying the plugins in a web app
|
||||
|
||||
@@ -376,77 +367,79 @@ Other "fat" JAR bundlers will probably have similar mechanisms to merge entries
|
||||
|
||||
### Links to prebuilt binaries
|
||||
|
||||
##### Latest version (3.6.2)
|
||||
##### Latest version (3.7.0)
|
||||
|
||||
Requires Java 7 or later.
|
||||
|
||||
Common dependencies
|
||||
* [common-lang-3.6.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-lang/3.6.2/common-lang-3.6.2.jar)
|
||||
* [common-io-3.6.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-io/3.6.2/common-io-3.6.2.jar)
|
||||
* [common-image-3.6.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-image/3.6.2/common-image-3.6.2.jar)
|
||||
* [common-lang-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-lang/3.7.0/common-lang-3.7.0.jar)
|
||||
* [common-io-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-io/3.7.0/common-io-3.7.0.jar)
|
||||
* [common-image-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-image/3.7.0/common-image-3.7.0.jar)
|
||||
|
||||
ImageIO dependencies
|
||||
* [imageio-core-3.6.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-core/3.6.2/imageio-core-3.6.2.jar)
|
||||
* [imageio-metadata-3.6.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-metadata/3.6.2/imageio-metadata-3.6.2.jar)
|
||||
* [imageio-core-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-core/3.7.0/imageio-core-3.7.0.jar)
|
||||
* [imageio-metadata-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-metadata/3.7.0/imageio-metadata-3.7.0.jar)
|
||||
|
||||
ImageIO plugins
|
||||
* [imageio-bmp-3.6.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-bmp/3.6.2/imageio-bmp-3.6.2.jar)
|
||||
* [imageio-hdr-3.6.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-hdr/3.6.2/imageio-hdr-3.6.2.jar)
|
||||
* [imageio-icns-3.6.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-icns/3.6.2/imageio-icns-3.6.2.jar)
|
||||
* [imageio-iff-3.6.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-iff/3.6.2/imageio-iff-3.6.2.jar)
|
||||
* [imageio-jpeg-3.6.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jpeg/3.6.2/imageio-jpeg-3.6.2.jar)
|
||||
* [imageio-pcx-3.6.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pcx/3.6.2/imageio-pcx-3.6.2.jar)
|
||||
* [imageio-pict-3.6.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pict/3.6.2/imageio-pict-3.6.2.jar)
|
||||
* [imageio-pnm-3.6.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pnm/3.6.2/imageio-pnm-3.6.2.jar)
|
||||
* [imageio-psd-3.6.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-psd/3.6.2/imageio-psd-3.6.2.jar)
|
||||
* [imageio-sgi-3.6.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-sgi/3.6.2/imageio-sgi-3.6.2.jar)
|
||||
* [imageio-tga-3.6.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tga/3.6.2/imageio-tga-3.6.2.jar)
|
||||
* [imageio-thumbsdb-3.6.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-thumbsdb/3.6.2/imageio-thumbsdb-3.6.2.jar)
|
||||
* [imageio-tiff-3.6.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tiff/3.6.2/imageio-tiff-3.6.2.jar)
|
||||
* [imageio-bmp-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-bmp/3.7.0/imageio-bmp-3.7.0.jar)
|
||||
* [imageio-hdr-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-hdr/3.7.0/imageio-hdr-3.7.0.jar)
|
||||
* [imageio-icns-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-icns/3.7.0/imageio-icns-3.7.0.jar)
|
||||
* [imageio-iff-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-iff/3.7.0/imageio-iff-3.7.0.jar)
|
||||
* [imageio-jpeg-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jpeg/3.7.0/imageio-jpeg-3.7.0.jar)
|
||||
* [imageio-pcx-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pcx/3.7.0/imageio-pcx-3.7.0.jar)
|
||||
* [imageio-pict-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pict/3.7.0/imageio-pict-3.7.0.jar)
|
||||
* [imageio-pnm-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pnm/3.7.0/imageio-pnm-3.7.0.jar)
|
||||
* [imageio-psd-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-psd/3.7.0/imageio-psd-3.7.0.jar)
|
||||
* [imageio-sgi-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-sgi/3.7.0/imageio-sgi-3.7.0.jar)
|
||||
* [imageio-tga-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tga/3.7.0/imageio-tga-3.7.0.jar)
|
||||
* [imageio-thumbsdb-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-thumbsdb/3.7.0/imageio-thumbsdb-3.7.0.jar)
|
||||
* [imageio-tiff-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tiff/3.7.0/imageio-tiff-3.7.0.jar)
|
||||
* [imageio-webp-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-webp/3.7.0/imageio-webp-3.7.0.jar)
|
||||
* [imageio-xwd-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-xwd/3.7.0/imageio-xwd-3.7.0.jar)
|
||||
|
||||
ImageIO plugins requiring 3rd party libs
|
||||
* [imageio-batik-3.6.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-batik/3.6.2/imageio-batik-3.6.2.jar)
|
||||
* [imageio-batik-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-batik/3.7.0/imageio-batik-3.7.0.jar)
|
||||
|
||||
Photoshop Path support for ImageIO
|
||||
* [imageio-clippath-3.6.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-clippath/3.6.2/imageio-clippath-3.6.2.jar)
|
||||
* [imageio-clippath-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-clippath/3.7.0/imageio-clippath-3.7.0.jar)
|
||||
|
||||
Servlet support
|
||||
* [servlet-3.6.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/servlet/servlet/3.6.2/servlet-3.6.2.jar)
|
||||
* [servlet-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/servlet/servlet/3.7.0/servlet-3.7.0.jar)
|
||||
|
||||
##### Old version (3.0.x)
|
||||
|
||||
Use this version for projects that requires Java 6 or need the JMagick support. *Does not support Java 8 or later*.
|
||||
|
||||
Common dependencies
|
||||
* [common-lang-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-lang/3.0.2/common-lang-3.0.2.jar)
|
||||
* [common-io-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-io/3.0.2/common-io-3.0.2.jar)
|
||||
* [common-image-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-image/3.0.2/common-image-3.0.2.jar)
|
||||
* [common-lang-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-lang/3.0.2/common-lang-3.0.2.jar)
|
||||
* [common-io-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-io/3.0.2/common-io-3.0.2.jar)
|
||||
* [common-image-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-image/3.0.2/common-image-3.0.2.jar)
|
||||
|
||||
ImageIO dependencies
|
||||
* [imageio-core-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-core/3.0.2/imageio-core-3.0.2.jar)
|
||||
* [imageio-metadata-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-metadata/3.0.2/imageio-metadata-3.0.2.jar)
|
||||
* [imageio-core-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-core/3.0.2/imageio-core-3.0.2.jar)
|
||||
* [imageio-metadata-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-metadata/3.0.2/imageio-metadata-3.0.2.jar)
|
||||
|
||||
ImageIO plugins
|
||||
* [imageio-jpeg-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jpeg/3.0.2/imageio-jpeg-3.0.2.jar)
|
||||
* [imageio-tiff-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tiff/3.0.2/imageio-tiff-3.0.2.jar)
|
||||
* [imageio-psd-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-psd/3.0.2/imageio-psd-3.0.2.jar)
|
||||
* [imageio-pict-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pict/3.0.2/imageio-pict-3.0.2.jar)
|
||||
* [imageio-iff-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-iff/3.0.2/imageio-iff-3.0.2.jar)
|
||||
* [imageio-icns-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-icns/3.0.2/imageio-icns-3.0.2.jar)
|
||||
* [imageio-ico-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-ico/3.0.2/imageio-ico-3.0.2.jar)
|
||||
* [imageio-thumbsdb-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-thumbsdb/3.0.2/imageio-thumbsdb-3.0.2.jar)
|
||||
* [imageio-jpeg-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jpeg/3.0.2/imageio-jpeg-3.0.2.jar)
|
||||
* [imageio-tiff-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tiff/3.0.2/imageio-tiff-3.0.2.jar)
|
||||
* [imageio-psd-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-psd/3.0.2/imageio-psd-3.0.2.jar)
|
||||
* [imageio-pict-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pict/3.0.2/imageio-pict-3.0.2.jar)
|
||||
* [imageio-iff-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-iff/3.0.2/imageio-iff-3.0.2.jar)
|
||||
* [imageio-icns-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-icns/3.0.2/imageio-icns-3.0.2.jar)
|
||||
* [imageio-ico-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-ico/3.0.2/imageio-ico-3.0.2.jar)
|
||||
* [imageio-thumbsdb-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-thumbsdb/3.0.2/imageio-thumbsdb-3.0.2.jar)
|
||||
|
||||
ImageIO plugins requiring 3rd party libs
|
||||
* [imageio-batik-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-batik/3.0.2/imageio-batik-3.0.2.jar)
|
||||
* [imageio-jmagick-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jmagick/3.0.2/imageio-jmagick-3.0.2.jar)
|
||||
* [imageio-batik-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-batik/3.0.2/imageio-batik-3.0.2.jar)
|
||||
* [imageio-jmagick-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jmagick/3.0.2/imageio-jmagick-3.0.2.jar)
|
||||
|
||||
Servlet support
|
||||
* [servlet-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/servlet/servlet/3.0.2/servlet-3.0.2.jar)
|
||||
* [servlet-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/servlet/servlet/3.0.2/servlet-3.0.2.jar)
|
||||
|
||||
|
||||
## License
|
||||
|
||||
The project is distributed under the OSI approved [BSD license](http://opensource.org/licenses/BSD-3-Clause):
|
||||
This project is provided under the OSI approved [BSD license](https://opensource.org/licenses/BSD-3-Clause):
|
||||
|
||||
Copyright (c) 2008-2020, Harald Kuhr
|
||||
All rights reserved.
|
||||
@@ -501,7 +494,7 @@ a: The TwelveMonkeys ImageIO project contains plug-ins for ImageIO. ImageIO uses
|
||||
|
||||
All you have have to do, is to make sure you have the TwelveMonkeys JARs in your classpath.
|
||||
|
||||
You can read more about the registry and the lookup mechanism in the [IIORegistry API doc](http://docs.oracle.com/javase/7/docs/api/javax/imageio/spi/IIORegistry.html).
|
||||
You can read more about the registry and the lookup mechanism in the [IIORegistry API doc](https://docs.oracle.com/javase/7/docs/api/javax/imageio/spi/IIORegistry.html).
|
||||
|
||||
The fine print: The TwelveMonkeys service providers for JPEG, BMP and TIFF, overrides the onRegistration method, and
|
||||
utilizes the pairwise partial ordering mechanism of the `IIOServiceRegistry` to make sure it is installed before
|
||||
@@ -509,10 +502,15 @@ the Sun/Oracle provided `JPEGImageReader` and `BMPImageReader`, and the Apple pr
|
||||
respectively. Using the pairwise ordering will not remove any functionality form these implementations, but in most
|
||||
cases you'll end up using the TwelveMonkeys plug-ins instead.
|
||||
|
||||
q: Why is there no support for common formats like GIF or PNG?
|
||||
|
||||
a: The short answer is simply that the built-in support in ImageIO for these formats are good enough as-is.
|
||||
If you are looking for better PNG write performance on Java 7 and 8, see [JDK9 PNG Writer Backport](https://github.com/gredler/jdk9-png-writer-backport).
|
||||
|
||||
|
||||
q: What about JAI? Several of the formats are already supported by JAI.
|
||||
|
||||
a: While JAI (and jai-imageio in particular) have support for some of the formats, JAI has some major issues.
|
||||
a: While JAI (and jai-imageio in particular) have support for some of the same formats, JAI has some major issues.
|
||||
The most obvious being:
|
||||
- It's not actively developed. No issues has been fixed for years.
|
||||
- To get full format support, you need native libs.
|
||||
|
||||
+6
-1
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys</groupId>
|
||||
<artifactId>twelvemonkeys</artifactId>
|
||||
<version>3.6.3</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>com.twelvemonkeys.bom</groupId>
|
||||
@@ -123,6 +123,11 @@
|
||||
<artifactId>imageio-tiff</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-webp</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-xwd</artifactId>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>3.6.3</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>common-image</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
@@ -13,6 +13,10 @@
|
||||
The TwelveMonkeys Common Image support
|
||||
</description>
|
||||
|
||||
<properties>
|
||||
<project.jpms.module.name>com.twelvemonkeys.common.image</project.jpms.module.name>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
|
||||
@@ -34,7 +34,13 @@ import java.awt.*;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.image.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.BufferedImageOp;
|
||||
import java.awt.image.ColorModel;
|
||||
import java.awt.image.ImagingOpException;
|
||||
import java.awt.image.Raster;
|
||||
import java.awt.image.RasterOp;
|
||||
import java.awt.image.WritableRaster;
|
||||
|
||||
/**
|
||||
* This is a drop-in replacement for {@link java.awt.image.AffineTransformOp}.
|
||||
@@ -70,6 +76,7 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp {
|
||||
delegate = new java.awt.image.AffineTransformOp(xform, interpolationType);
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public BufferedImage filter(final BufferedImage src, BufferedImage dst) {
|
||||
try {
|
||||
@@ -80,10 +87,9 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp {
|
||||
dst = createCompatibleDestImage(src, src.getColorModel());
|
||||
}
|
||||
|
||||
Graphics2D g2d = null;
|
||||
Graphics2D g2d = dst.createGraphics();
|
||||
|
||||
try {
|
||||
g2d = dst.createGraphics();
|
||||
int interpolationType = delegate.getInterpolationType();
|
||||
|
||||
if (interpolationType > 0) {
|
||||
@@ -109,9 +115,7 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp {
|
||||
return dst;
|
||||
}
|
||||
finally {
|
||||
if (g2d != null) {
|
||||
g2d.dispose();
|
||||
}
|
||||
g2d.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,8 +45,8 @@ import java.awt.image.BufferedImage;
|
||||
*/
|
||||
public class BufferedImageIcon implements Icon {
|
||||
private final BufferedImage image;
|
||||
private int width;
|
||||
private int height;
|
||||
private final int width;
|
||||
private final int height;
|
||||
private final boolean fast;
|
||||
|
||||
public BufferedImageIcon(BufferedImage pImage) {
|
||||
@@ -81,11 +81,10 @@ public class BufferedImageIcon implements Icon {
|
||||
else {
|
||||
//System.out.println("Scaling using interpolation");
|
||||
Graphics2D g2 = (Graphics2D) g;
|
||||
AffineTransform xform = AffineTransform.getTranslateInstance(x, y);
|
||||
xform.scale(width / (double) image.getWidth(), height / (double) image.getHeight());
|
||||
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
|
||||
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
||||
g2.drawImage(image, xform, null);
|
||||
AffineTransform transform = AffineTransform.getTranslateInstance(x, y);
|
||||
transform.scale(width / (double) image.getWidth(), height / (double) image.getHeight());
|
||||
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
||||
g2.drawImage(image, transform, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -587,6 +587,7 @@ class IndexImage {
|
||||
* @deprecated Use {@link #getIndexColorModel(Image,int,int)} instead!
|
||||
* This version will be removed in a later version of the API.
|
||||
*/
|
||||
@Deprecated
|
||||
public static IndexColorModel getIndexColorModel(Image pImage, int pNumberOfColors, boolean pFast) {
|
||||
return getIndexColorModel(pImage, pNumberOfColors, pFast ? COLOR_SELECTION_FAST : COLOR_SELECTION_QUALITY);
|
||||
}
|
||||
|
||||
+22
-16
@@ -30,17 +30,26 @@
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
import org.junit.Test;
|
||||
import static java.lang.Math.min;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.image.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.BufferedImageOp;
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.awt.image.ImagingOpException;
|
||||
import java.awt.image.Raster;
|
||||
import java.awt.image.RasterOp;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* AffineTransformOpTest.
|
||||
@@ -101,6 +110,7 @@ public class AffineTransformOpTest {
|
||||
|
||||
private final int width = 30;
|
||||
private final int height = 20;
|
||||
private final double anchor = min(width, height) / 2.0;
|
||||
|
||||
@Test
|
||||
public void testGetPoint2D() {
|
||||
@@ -128,8 +138,8 @@ public class AffineTransformOpTest {
|
||||
|
||||
@Test
|
||||
public void testFilterRotateBIStandard() {
|
||||
BufferedImageOp jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, Math.min(width, height) / 2, Math.min(width, height) / 2), null);
|
||||
BufferedImageOp tmOp = new com.twelvemonkeys.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, Math.min(width, height) / 2, Math.min(width, height) / 2), null);
|
||||
BufferedImageOp jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), null);
|
||||
BufferedImageOp tmOp = new com.twelvemonkeys.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), null);
|
||||
|
||||
for (Integer type : TYPES) {
|
||||
BufferedImage image = new BufferedImage(width, height, type);
|
||||
@@ -147,8 +157,8 @@ public class AffineTransformOpTest {
|
||||
|
||||
@Test
|
||||
public void testFilterRotateBICustom() {
|
||||
BufferedImageOp jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, Math.min(width, height) / 2, Math.min(width, height) / 2), null);
|
||||
BufferedImageOp tmOp = new com.twelvemonkeys.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, Math.min(width, height) / 2, Math.min(width, height) / 2), null);
|
||||
BufferedImageOp jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), null);
|
||||
BufferedImageOp tmOp = new com.twelvemonkeys.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), null);
|
||||
|
||||
for (ImageTypeSpecifier spec : SPECS) {
|
||||
BufferedImage image = spec.createBufferedImage(width, height);
|
||||
@@ -197,8 +207,8 @@ public class AffineTransformOpTest {
|
||||
|
||||
@Test
|
||||
public void testFilterRotateRasterStandard() {
|
||||
RasterOp jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, Math.min(width, height) / 2, Math.min(width, height) / 2), null);
|
||||
RasterOp tmOp = new com.twelvemonkeys.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, Math.min(width, height) / 2, Math.min(width, height) / 2), null);
|
||||
RasterOp jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), null);
|
||||
RasterOp tmOp = new com.twelvemonkeys.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), null);
|
||||
|
||||
for (Integer type : TYPES) {
|
||||
Raster raster = new BufferedImage(width, height, type).getRaster();
|
||||
@@ -221,8 +231,6 @@ public class AffineTransformOpTest {
|
||||
fail("No result!");
|
||||
}
|
||||
else {
|
||||
System.err.println("AffineTransformOpTest.testFilterRotateRasterStandard");
|
||||
System.err.println("type: " + type);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -240,8 +248,8 @@ public class AffineTransformOpTest {
|
||||
|
||||
@Test
|
||||
public void testFilterRotateRasterCustom() {
|
||||
RasterOp jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, Math.min(width, height) / 2, Math.min(width, height) / 2), null);
|
||||
RasterOp tmOp = new com.twelvemonkeys.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, Math.min(width, height) / 2, Math.min(width, height) / 2), null);
|
||||
RasterOp jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), null);
|
||||
RasterOp tmOp = new com.twelvemonkeys.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), null);
|
||||
|
||||
for (ImageTypeSpecifier spec : SPECS) {
|
||||
Raster raster = spec.createBufferedImage(width, height).getRaster();
|
||||
@@ -264,8 +272,6 @@ public class AffineTransformOpTest {
|
||||
fail("No result!");
|
||||
}
|
||||
else {
|
||||
System.err.println("AffineTransformOpTest.testFilterRotateRasterCustom");
|
||||
System.err.println("spec: " + spec);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>3.6.3</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>common-io</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
@@ -13,6 +13,10 @@
|
||||
The TwelveMonkeys Common IO support
|
||||
</description>
|
||||
|
||||
<properties>
|
||||
<project.jpms.module.name>com.twelvemonkeys.common.io</project.jpms.module.name>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
|
||||
@@ -346,7 +346,7 @@ public final class FileUtil {
|
||||
|
||||
/**
|
||||
* Gets the file (type) extension of the given file.
|
||||
* A file extension is the part of the filename, after the last occurence
|
||||
* A file extension is the part of the filename, after the last occurrence
|
||||
* of a period {@code '.'}.
|
||||
* If the filename contains no period, {@code null} is returned.
|
||||
*
|
||||
|
||||
@@ -65,6 +65,7 @@ import java.io.FilenameFilter;
|
||||
* @see WildcardStringParser
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated
|
||||
public class FilenameMaskFilter implements FilenameFilter {
|
||||
|
||||
// TODO: Rewrite to use regexp, or create new class
|
||||
|
||||
@@ -442,6 +442,7 @@ public class LittleEndianDataInputStream extends FilterInputStream implements Da
|
||||
* @see java.io.BufferedReader#readLine()
|
||||
* @see java.io.DataInputStream#readLine()
|
||||
*/
|
||||
@Deprecated
|
||||
public String readLine() throws IOException {
|
||||
DataInputStream ds = new DataInputStream(in);
|
||||
return ds.readLine();
|
||||
|
||||
@@ -29,6 +29,9 @@
|
||||
|
||||
package com.twelvemonkeys.xml;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.io.Writer;
|
||||
|
||||
import org.w3c.dom.DOMConfiguration;
|
||||
import org.w3c.dom.DOMImplementationList;
|
||||
import org.w3c.dom.Document;
|
||||
@@ -38,9 +41,6 @@ import org.w3c.dom.ls.DOMImplementationLS;
|
||||
import org.w3c.dom.ls.LSOutput;
|
||||
import org.w3c.dom.ls.LSSerializer;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.io.Writer;
|
||||
|
||||
/**
|
||||
* {@code DOMImplementationLS} backed implementation.
|
||||
*
|
||||
@@ -88,17 +88,6 @@ public final class DOMSerializer {
|
||||
output.setCharacterStream(pStream);
|
||||
}
|
||||
|
||||
/*
|
||||
// TODO: Is it useful?
|
||||
public void setNewLine(final String pNewLine) {
|
||||
serializer.setNewLine(pNewLine);
|
||||
}
|
||||
|
||||
public String getNewLine() {
|
||||
return serializer.getNewLine();
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Specifies wether the serializer should use indentation and optimize for
|
||||
* readability.
|
||||
@@ -169,13 +158,7 @@ public final class DOMSerializer {
|
||||
try {
|
||||
return DOMImplementationRegistry.newInstance();
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
catch (InstantiationException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
catch (IllegalAccessException e) {
|
||||
catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,16 +30,23 @@
|
||||
|
||||
package com.twelvemonkeys.xml;
|
||||
|
||||
import com.twelvemonkeys.lang.StringUtil;
|
||||
import org.w3c.dom.*;
|
||||
import org.xml.sax.SAXException;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import java.io.*;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Date;
|
||||
|
||||
import org.w3c.dom.*;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import com.twelvemonkeys.lang.StringUtil;
|
||||
|
||||
/**
|
||||
* XMLSerializer
|
||||
@@ -290,7 +297,7 @@ public class XMLSerializer {
|
||||
}
|
||||
|
||||
private static int appendAndEscape(final String pString, int pStart, final int pEnd, final StringBuilder pBuilder, final String pEntity) {
|
||||
pBuilder.append(pString.substring(pStart, pEnd));
|
||||
pBuilder.append(pString, pStart, pEnd);
|
||||
pBuilder.append(pEntity);
|
||||
return pEnd + 1;
|
||||
}
|
||||
@@ -527,8 +534,7 @@ public class XMLSerializer {
|
||||
builder = factory.newDocumentBuilder();
|
||||
}
|
||||
catch (ParserConfigurationException e) {
|
||||
//noinspection ThrowableInstanceNeverThrown BOGUS
|
||||
throw (IOException) new IOException(e.getMessage()).initCause(e);
|
||||
throw new IOException(e);
|
||||
}
|
||||
|
||||
DOMImplementation dom = builder.getDOMImplementation();
|
||||
|
||||
@@ -32,13 +32,13 @@ package com.twelvemonkeys.io.enc;
|
||||
|
||||
import com.twelvemonkeys.io.FileUtil;
|
||||
import com.twelvemonkeys.lang.ObjectAbstractTest;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
@@ -73,7 +73,7 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] createData(final int pLength) throws Exception {
|
||||
private byte[] createData(final int pLength) {
|
||||
byte[] bytes = new byte[pLength];
|
||||
RANDOM.nextBytes(bytes);
|
||||
return bytes;
|
||||
@@ -82,9 +82,8 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
|
||||
private void runStreamTest(final int pLength) throws Exception {
|
||||
byte[] data = createData(pLength);
|
||||
ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
|
||||
OutputStream out = new EncoderStream(outBytes, createEncoder(), true);
|
||||
|
||||
try {
|
||||
try (OutputStream out = new EncoderStream(outBytes, createEncoder(), true)) {
|
||||
// Provoke failure for encoders that doesn't take array offset properly into account
|
||||
int off = (data.length + 1) / 2;
|
||||
out.write(data, 0, off);
|
||||
@@ -92,9 +91,6 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
|
||||
out.write(data, off, data.length - off);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
out.close();
|
||||
}
|
||||
|
||||
byte[] encoded = outBytes.toByteArray();
|
||||
|
||||
@@ -102,7 +98,7 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
|
||||
// System.err.println("encoded: " + Arrays.toString(encoded));
|
||||
|
||||
byte[] decoded = FileUtil.read(new DecoderStream(new ByteArrayInputStream(encoded), createCompatibleDecoder()));
|
||||
assertTrue(Arrays.equals(data, decoded));
|
||||
assertArrayEquals(data, decoded);
|
||||
|
||||
InputStream in = new DecoderStream(new ByteArrayInputStream(encoded), createCompatibleDecoder());
|
||||
outBytes = new ByteArrayOutputStream();
|
||||
@@ -116,7 +112,7 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
|
||||
}
|
||||
|
||||
decoded = outBytes.toByteArray();
|
||||
assertTrue(Arrays.equals(data, decoded));
|
||||
assertArrayEquals(data, decoded);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -129,10 +125,6 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
|
||||
e.printStackTrace();
|
||||
fail(e.getMessage() + ": " + i);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
fail(e.getMessage() + ": " + i);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 100; i < 2000; i += 250) {
|
||||
@@ -143,10 +135,6 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
|
||||
e.printStackTrace();
|
||||
fail(e.getMessage() + ": " + i);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
fail(e.getMessage() + ": " + i);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 2000; i < 80000; i += 1000) {
|
||||
@@ -157,14 +145,8 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
|
||||
e.printStackTrace();
|
||||
fail(e.getMessage() + ": " + i);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
fail(e.getMessage() + ": " + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Test that the transition from byte[]Â to ByteBuffer didn't introduce bugs when writing to a wrapped array with offset.
|
||||
|
||||
|
||||
// TODO: Test that the transition from byte[] to ByteBuffer didn't introduce bugs when writing to a wrapped array with offset.
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>3.6.3</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>common-lang</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
@@ -13,4 +13,8 @@
|
||||
The TwelveMonkeys Common Language support
|
||||
</description>
|
||||
|
||||
<properties>
|
||||
<project.jpms.module.name>com.twelvemonkeys.common.lang</project.jpms.module.name>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -770,6 +770,7 @@ public final class StringUtil {
|
||||
*/
|
||||
|
||||
/*public*/
|
||||
@Deprecated
|
||||
static String formatNumber(long pNum, int pLen) throws IllegalArgumentException {
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
@@ -1464,6 +1465,7 @@ public final class StringUtil {
|
||||
*/
|
||||
|
||||
/*public*/
|
||||
@Deprecated
|
||||
static String removeSubstring(final String pSource, final char pBeginBoundaryChar, final char pEndBoundaryChar, final int pOffset) {
|
||||
StringBuilder filteredString = new StringBuilder();
|
||||
boolean insideDemarcatedArea = false;
|
||||
|
||||
@@ -330,7 +330,7 @@ abstract class AbstractDecoratedMap<K, V> extends AbstractMap<K, V> implements M
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple Map.Entry implementaton.
|
||||
* A simple Map.Entry implementation.
|
||||
*/
|
||||
static class BasicEntry<K, V> implements Entry<K, V>, Serializable {
|
||||
K mKey;
|
||||
|
||||
@@ -157,6 +157,7 @@ public class Time {
|
||||
* @see #parseTime(String)
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated
|
||||
public String toString(String pFormatStr) {
|
||||
TimeFormat tf = new TimeFormat(pFormatStr);
|
||||
|
||||
@@ -174,6 +175,7 @@ public class Time {
|
||||
* @see #toString(String)
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated
|
||||
public static Time parseTime(String pStr) {
|
||||
TimeFormat tf = TimeFormat.getInstance();
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ import java.io.Serializable;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* A {@code Map} implementation that removes (exipres) its elements after
|
||||
* A {@code Map} implementation that removes (expires) its elements after
|
||||
* a given period. The map is by default backed by a {@link java.util.HashMap},
|
||||
* or can be instantiated with any given {@code Map} as backing.
|
||||
* <p>
|
||||
@@ -67,7 +67,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
|
||||
protected long expiryTime = 60000L; // 1 minute
|
||||
|
||||
//////////////////////
|
||||
private volatile long nextExpiryTime;
|
||||
private volatile long nextExpiryTime = Long.MAX_VALUE;
|
||||
//////////////////////
|
||||
|
||||
/**
|
||||
@@ -178,7 +178,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
|
||||
* @return {@code true} if this map contains no key-value mappings.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return (size() <= 0);
|
||||
return size() <= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -208,7 +208,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
|
||||
* @see #containsKey(java.lang.Object)
|
||||
*/
|
||||
public V get(Object pKey) {
|
||||
TimedEntry<K, V> entry = (TimedEntry<K, V>) entries.get(pKey);
|
||||
TimedEntry entry = (TimedEntry) entries.get(pKey);
|
||||
|
||||
if (entry == null) {
|
||||
return null;
|
||||
@@ -236,7 +236,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
|
||||
* {@code null} values.
|
||||
*/
|
||||
public V put(K pKey, V pValue) {
|
||||
TimedEntry<K, V> entry = (TimedEntry<K, V>) entries.get(pKey);
|
||||
TimedEntry entry = (TimedEntry) entries.get(pKey);
|
||||
V oldValue;
|
||||
|
||||
if (entry == null) {
|
||||
@@ -272,7 +272,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
|
||||
* {@code null} values.
|
||||
*/
|
||||
public V remove(Object pKey) {
|
||||
TimedEntry<K, V> entry = (TimedEntry<K, V>) entries.remove(pKey);
|
||||
TimedEntry entry = (TimedEntry) entries.remove(pKey);
|
||||
return (entry != null) ? entry.getValue() : null;
|
||||
}
|
||||
|
||||
@@ -284,13 +284,12 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
|
||||
init();
|
||||
}
|
||||
|
||||
/*protected*/ TimedEntry<K, V> createEntry(K pKey, V pValue) {
|
||||
return new TimedEntry<K, V>(pKey, pValue);
|
||||
/*protected*/ TimedEntry createEntry(K pKey, V pValue) {
|
||||
return new TimedEntry(pKey, pValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes any expired mappings.
|
||||
*
|
||||
*/
|
||||
protected void removeExpiredEntries() {
|
||||
// Remove any expired elements
|
||||
@@ -312,7 +311,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
|
||||
long next = Long.MAX_VALUE;
|
||||
nextExpiryTime = next; // Avoid multiple runs...
|
||||
for (Iterator<Entry<K, V>> iterator = new EntryIterator(); iterator.hasNext();) {
|
||||
TimedEntry<K, V> entry = (TimedEntry<K, V>) iterator.next();
|
||||
TimedEntry entry = (TimedEntry) iterator.next();
|
||||
////
|
||||
long expires = entry.expires();
|
||||
if (expires < next) {
|
||||
@@ -376,7 +375,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
|
||||
|
||||
while (mNext == null && mIterator.hasNext()) {
|
||||
Entry<K, Entry<K, V>> entry = mIterator.next();
|
||||
TimedEntry<K, V> timed = (TimedEntry<K, V>) entry.getValue();
|
||||
TimedEntry timed = (TimedEntry) entry.getValue();
|
||||
|
||||
if (timed.isExpiredBy(mNow)) {
|
||||
// Remove from map, and continue
|
||||
@@ -425,19 +424,28 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
|
||||
/**
|
||||
* Keeps track of timed objects
|
||||
*/
|
||||
private class TimedEntry<K, V> extends BasicEntry<K, V> {
|
||||
private class TimedEntry extends BasicEntry<K, V> {
|
||||
private long mTimestamp;
|
||||
|
||||
TimedEntry(K pKey, V pValue) {
|
||||
super(pKey, pValue);
|
||||
mTimestamp = System.currentTimeMillis();
|
||||
updateTimestamp();
|
||||
}
|
||||
|
||||
public V setValue(V pValue) {
|
||||
mTimestamp = System.currentTimeMillis();
|
||||
updateTimestamp();
|
||||
return super.setValue(pValue);
|
||||
}
|
||||
|
||||
private void updateTimestamp() {
|
||||
mTimestamp = System.currentTimeMillis();
|
||||
|
||||
long expires = expires();
|
||||
if (expires < nextExpiryTime) {
|
||||
nextExpiryTime = expires;
|
||||
}
|
||||
}
|
||||
|
||||
final boolean isExpired() {
|
||||
return isExpiredBy(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
+1
@@ -111,6 +111,7 @@ import java.io.PrintStream;
|
||||
* @author <a href="mailto:eirik.torske@iconmedialab.no">Eirik Torske</a>
|
||||
* @deprecated Will probably be removed in the near future
|
||||
*/
|
||||
@Deprecated
|
||||
public class WildcardStringParser {
|
||||
// TODO: Get rid of this class
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
package com.twelvemonkeys.lang;
|
||||
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.awt.*;
|
||||
import java.sql.Timestamp;
|
||||
@@ -41,7 +41,7 @@ import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.Locale;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* StringUtilTestCase
|
||||
@@ -76,24 +76,24 @@ public class StringUtilTest {
|
||||
assertNull(StringUtil.valueOf(null));
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Test
|
||||
public void testToUpperCase() {
|
||||
String str = StringUtil.toUpperCase(TEST_STRING);
|
||||
assertNotNull(str);
|
||||
assertEquals(TEST_STRING.toUpperCase(), str);
|
||||
|
||||
str = StringUtil.toUpperCase(null);
|
||||
assertNull(str);
|
||||
assertNull(StringUtil.toUpperCase(null));
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Test
|
||||
public void testToLowerCase() {
|
||||
String str = StringUtil.toLowerCase(TEST_STRING);
|
||||
assertNotNull(str);
|
||||
assertEquals(TEST_STRING.toLowerCase(), str);
|
||||
|
||||
str = StringUtil.toLowerCase(null);
|
||||
assertNull(str);
|
||||
assertNull(StringUtil.toLowerCase(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -113,6 +113,7 @@ public class StringUtilTest {
|
||||
assertFalse(StringUtil.isEmpty(new String[]{WHITESPACE_STRING, TEST_STRING}));
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Test
|
||||
public void testContains() {
|
||||
assertTrue(StringUtil.contains(TEST_STRING, TEST_STRING));
|
||||
@@ -145,6 +146,7 @@ public class StringUtilTest {
|
||||
assertFalse(StringUtil.containsIgnoreCase(null, null));
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Test
|
||||
public void testContainsChar() {
|
||||
for (int i = 0; i < TEST_STRING.length(); i++) {
|
||||
@@ -466,7 +468,7 @@ public class StringUtilTest {
|
||||
assertEquals(TEST_STRING, StringUtil.ltrim(TEST_STRING));
|
||||
assertEquals(TEST_STRING, StringUtil.ltrim(" " + TEST_STRING));
|
||||
assertEquals(TEST_STRING, StringUtil.ltrim(WHITESPACE_STRING + TEST_STRING));
|
||||
assertFalse(TEST_STRING.equals(StringUtil.ltrim(TEST_STRING + WHITESPACE_STRING)));
|
||||
assertNotEquals(TEST_STRING, StringUtil.ltrim(TEST_STRING + WHITESPACE_STRING));
|
||||
// TODO: Test is not complete
|
||||
}
|
||||
|
||||
@@ -475,7 +477,7 @@ public class StringUtilTest {
|
||||
assertEquals(TEST_STRING, StringUtil.rtrim(TEST_STRING));
|
||||
assertEquals(TEST_STRING, StringUtil.rtrim(TEST_STRING + " "));
|
||||
assertEquals(TEST_STRING, StringUtil.rtrim(TEST_STRING + WHITESPACE_STRING));
|
||||
assertFalse(TEST_STRING.equals(StringUtil.rtrim(WHITESPACE_STRING + TEST_STRING)));
|
||||
assertNotEquals(TEST_STRING, StringUtil.rtrim(WHITESPACE_STRING + TEST_STRING));
|
||||
// TODO: Test is not complete
|
||||
}
|
||||
|
||||
@@ -516,7 +518,7 @@ public class StringUtilTest {
|
||||
public void testCaptialize() {
|
||||
assertNull(StringUtil.capitalize(null));
|
||||
assertEquals(TEST_STRING.toUpperCase(), StringUtil.capitalize(TEST_STRING.toUpperCase()));
|
||||
assertTrue(StringUtil.capitalize("abc").charAt(0) == 'A');
|
||||
assertEquals('A', StringUtil.capitalize("abc").charAt(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -552,13 +554,13 @@ public class StringUtilTest {
|
||||
public void testToDateWithFormatString() {
|
||||
Calendar cal = new GregorianCalendar();
|
||||
cal.clear();
|
||||
cal.set(1976, 2, 16); // Month is 0-based
|
||||
cal.set(1976, Calendar.MARCH, 16); // Month is 0-based
|
||||
Date date = StringUtil.toDate("16.03.1976", "dd.MM.yyyy");
|
||||
assertNotNull(date);
|
||||
assertEquals(cal.getTime(), date);
|
||||
|
||||
cal.clear();
|
||||
cal.set(2004, 4, 13, 23, 51, 3);
|
||||
cal.set(2004, Calendar.MAY, 13, 23, 51, 3);
|
||||
date = StringUtil.toDate("2004-5-13 23:51 (03)", "yyyy-MM-dd hh:mm (ss)");
|
||||
assertNotNull(date);
|
||||
assertEquals(cal.getTime(), date);
|
||||
@@ -576,15 +578,15 @@ public class StringUtilTest {
|
||||
public void testToDateWithFormat() {
|
||||
Calendar cal = new GregorianCalendar();
|
||||
cal.clear();
|
||||
cal.set(1976, 2, 16); // Month is 0-based
|
||||
cal.set(1976, Calendar.MARCH, 16); // Month is 0-based
|
||||
Date date = StringUtil.toDate("16.03.1976", new SimpleDateFormat("dd.MM.yyyy"));
|
||||
assertNotNull(date);
|
||||
assertEquals(cal.getTime(), date);
|
||||
|
||||
cal.clear();
|
||||
cal.set(2004, 4, 13, 23, 51);
|
||||
date = StringUtil.toDate("13.5.04 23:51",
|
||||
DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, new Locale("no", "NO")));
|
||||
cal.set(2004, Calendar.MAY, 13, 23, 51);
|
||||
DateFormat format = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, new Locale("no", "NO"));
|
||||
date = StringUtil.toDate(format.format(cal.getTime()), format);
|
||||
assertNotNull(date);
|
||||
assertEquals(cal.getTime(), date);
|
||||
|
||||
@@ -601,10 +603,9 @@ public class StringUtilTest {
|
||||
public void testToTimestamp() {
|
||||
Calendar cal = new GregorianCalendar();
|
||||
cal.clear();
|
||||
cal.set(1976, 2, 16, 21, 28, 4); // Month is 0-based
|
||||
Date date = StringUtil.toTimestamp("1976-03-16 21:28:04");
|
||||
cal.set(1976, Calendar.MARCH, 16, 21, 28, 4); // Month is 0-based
|
||||
Timestamp date = StringUtil.toTimestamp("1976-03-16 21:28:04");
|
||||
assertNotNull(date);
|
||||
assertTrue(date instanceof Timestamp);
|
||||
assertEquals(cal.getTime(), date);
|
||||
}
|
||||
|
||||
@@ -821,7 +822,7 @@ public class StringUtilTest {
|
||||
assertTrue(StringUtil.isNumber("12345"));
|
||||
assertTrue(StringUtil.isNumber(TEST_INTEGER.toString()));
|
||||
assertTrue(StringUtil.isNumber("1234567890123456789012345678901234567890"));
|
||||
assertTrue(StringUtil.isNumber(String.valueOf(Long.MAX_VALUE) + String.valueOf(Long.MAX_VALUE)));
|
||||
assertTrue(StringUtil.isNumber(String.valueOf(Long.MAX_VALUE) + Long.MAX_VALUE));
|
||||
assertFalse(StringUtil.isNumber("abc"));
|
||||
assertFalse(StringUtil.isNumber(TEST_STRING));
|
||||
}
|
||||
@@ -831,7 +832,7 @@ public class StringUtilTest {
|
||||
assertTrue(StringUtil.isNumber("-12345"));
|
||||
assertTrue(StringUtil.isNumber('-' + TEST_INTEGER.toString()));
|
||||
assertTrue(StringUtil.isNumber("-1234567890123456789012345678901234567890"));
|
||||
assertTrue(StringUtil.isNumber('-' + String.valueOf(Long.MAX_VALUE) + String.valueOf(Long.MAX_VALUE)));
|
||||
assertTrue(StringUtil.isNumber('-' + String.valueOf(Long.MAX_VALUE) + Long.MAX_VALUE));
|
||||
assertFalse(StringUtil.isNumber("-abc"));
|
||||
assertFalse(StringUtil.isNumber('-' + TEST_STRING));
|
||||
}
|
||||
|
||||
@@ -557,7 +557,7 @@ public class TimeoutMapTest extends MapAbstractTest {
|
||||
// NOTE: Only wait fist time, to avoid slooow tests
|
||||
synchronized (this) {
|
||||
try {
|
||||
wait(60l);
|
||||
wait(60L);
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
}
|
||||
@@ -591,7 +591,7 @@ public class TimeoutMapTest extends MapAbstractTest {
|
||||
try {
|
||||
wait(60l);
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
catch (InterruptedException ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -651,5 +651,24 @@ public class TimeoutMapTest extends MapAbstractTest {
|
||||
assertTrue("Wrong entry removed, keySet().iterator() is broken.", !map.containsKey(removedKey));
|
||||
assertTrue("Wrong entry removed, keySet().iterator() is broken.", map.containsKey(otherKey));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testContainsKeyOnEmptyMap() {
|
||||
// See #600
|
||||
Map<String, String> timeoutMap = new TimeoutMap<>(30);
|
||||
assertFalse(timeoutMap.containsKey("xyz"));
|
||||
timeoutMap.put("xyz", "xyz");
|
||||
assertTrue(timeoutMap.containsKey("xyz"));
|
||||
|
||||
try {
|
||||
Thread.sleep(50); // Let the item expire
|
||||
}
|
||||
catch (InterruptedException ignore) {
|
||||
}
|
||||
|
||||
assertFalse(timeoutMap.containsKey("xyz"));
|
||||
assertNull(timeoutMap.get("xyz"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys</groupId>
|
||||
<artifactId>twelvemonkeys</artifactId>
|
||||
<version>3.6.3</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys</groupId>
|
||||
<artifactId>twelvemonkeys</artifactId>
|
||||
<version>3.6.3</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>com.twelvemonkeys.contrib</groupId>
|
||||
<artifactId>contrib</artifactId>
|
||||
|
||||
@@ -31,8 +31,9 @@
|
||||
package com.twelvemonkeys.contrib.tiff;
|
||||
|
||||
import com.twelvemonkeys.contrib.tiff.TIFFUtilities.TIFFExtension;
|
||||
import com.twelvemonkeys.imageio.plugins.tiff.TIFFMedataFormat;
|
||||
import com.twelvemonkeys.imageio.plugins.tiff.TIFFImageMetadataFormat;
|
||||
import com.twelvemonkeys.io.FileUtil;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.w3c.dom.Node;
|
||||
@@ -154,7 +155,7 @@ public class TIFFUtilitiesTest {
|
||||
reader.setInput(checkTest1);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Node metaData = reader.getImageMetadata(i)
|
||||
.getAsTree(TIFFMedataFormat.SUN_NATIVE_IMAGE_METADATA_FORMAT_NAME);
|
||||
.getAsTree(TIFFImageMetadataFormat.SUN_NATIVE_IMAGE_METADATA_FORMAT_NAME);
|
||||
short orientation = ((Number) expression.evaluate(metaData, XPathConstants.NUMBER)).shortValue();
|
||||
Assert.assertEquals(orientation, TIFFExtension.ORIENTATION_RIGHTTOP);
|
||||
}
|
||||
@@ -171,7 +172,7 @@ public class TIFFUtilitiesTest {
|
||||
reader.setInput(checkTest2);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Node metaData = reader.getImageMetadata(i)
|
||||
.getAsTree(TIFFMedataFormat.SUN_NATIVE_IMAGE_METADATA_FORMAT_NAME);
|
||||
.getAsTree(TIFFImageMetadataFormat.SUN_NATIVE_IMAGE_METADATA_FORMAT_NAME);
|
||||
short orientation = ((Number) expression.evaluate(metaData, XPathConstants.NUMBER)).shortValue();
|
||||
Assert.assertEquals(orientation, i == 1
|
||||
? TIFFExtension.ORIENTATION_BOTRIGHT
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.6.3</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-batik</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: Batik Plugin</name>
|
||||
@@ -14,6 +14,12 @@
|
||||
See the <a href="http://xmlgraphics.apache.org/batik/">Batik Home page</a>
|
||||
for more information.]]>
|
||||
</description>
|
||||
|
||||
<properties>
|
||||
<project.jpms.module.name>com.twelvemonkeys.imageio.batik</project.jpms.module.name>
|
||||
<batik.version>1.14</batik.version>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
@@ -62,13 +68,6 @@
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.xmlgraphics</groupId>
|
||||
<artifactId>xmlgraphics-commons</artifactId>
|
||||
<version>2.2</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.xmlgraphics</groupId>
|
||||
<artifactId>batik-anim</artifactId>
|
||||
@@ -92,7 +91,7 @@
|
||||
<!--
|
||||
There seems to be some weirdness in the
|
||||
Batik/FOP poms (Batik depends on FOP 0.20-5) that screws things up,
|
||||
making everything end up depending on Batik 1.5, not 1.6
|
||||
making everything end up depending on Batik 1.5, not the specified version
|
||||
-->
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
@@ -102,8 +101,4 @@
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<batik.version>1.14</batik.version>
|
||||
</properties>
|
||||
</project>
|
||||
|
||||
+37
-35
@@ -30,37 +30,8 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.svg;
|
||||
|
||||
import com.twelvemonkeys.image.ImageUtil;
|
||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||
import com.twelvemonkeys.lang.StringUtil;
|
||||
import org.apache.batik.anim.dom.SVGDOMImplementation;
|
||||
import org.apache.batik.anim.dom.SVGOMDocument;
|
||||
import org.apache.batik.bridge.*;
|
||||
import org.apache.batik.css.parser.CSSLexicalUnit;
|
||||
import org.apache.batik.dom.util.DOMUtilities;
|
||||
import org.apache.batik.ext.awt.image.GraphicsUtil;
|
||||
import org.apache.batik.gvt.CanvasGraphicsNode;
|
||||
import org.apache.batik.gvt.GraphicsNode;
|
||||
import org.apache.batik.gvt.renderer.ConcreteImageRendererFactory;
|
||||
import org.apache.batik.gvt.renderer.ImageRenderer;
|
||||
import org.apache.batik.gvt.renderer.ImageRendererFactory;
|
||||
import org.apache.batik.transcoder.*;
|
||||
import org.apache.batik.transcoder.image.ImageTranscoder;
|
||||
import org.apache.batik.util.ParsedURL;
|
||||
import org.apache.batik.util.SVGConstants;
|
||||
import org.apache.batik.xml.LexicalUnits;
|
||||
import org.w3c.dom.DOMImplementation;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.svg.SVGSVGElement;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import java.awt.*;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Dimension2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
@@ -70,6 +41,38 @@ import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
|
||||
import org.apache.batik.anim.dom.SVGDOMImplementation;
|
||||
import org.apache.batik.anim.dom.SVGOMDocument;
|
||||
import org.apache.batik.bridge.*;
|
||||
import org.apache.batik.dom.util.DOMUtilities;
|
||||
import org.apache.batik.ext.awt.image.GraphicsUtil;
|
||||
import org.apache.batik.gvt.CanvasGraphicsNode;
|
||||
import org.apache.batik.gvt.GraphicsNode;
|
||||
import org.apache.batik.gvt.renderer.ConcreteImageRendererFactory;
|
||||
import org.apache.batik.gvt.renderer.ImageRenderer;
|
||||
import org.apache.batik.gvt.renderer.ImageRendererFactory;
|
||||
import org.apache.batik.transcoder.SVGAbstractTranscoder;
|
||||
import org.apache.batik.transcoder.TranscoderException;
|
||||
import org.apache.batik.transcoder.TranscoderInput;
|
||||
import org.apache.batik.transcoder.TranscoderOutput;
|
||||
import org.apache.batik.transcoder.TranscodingHints;
|
||||
import org.apache.batik.transcoder.image.ImageTranscoder;
|
||||
import org.apache.batik.util.ParsedURL;
|
||||
import org.apache.batik.util.SVGConstants;
|
||||
import org.w3c.dom.DOMImplementation;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.svg.SVGSVGElement;
|
||||
|
||||
import com.twelvemonkeys.image.ImageUtil;
|
||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||
import com.twelvemonkeys.lang.StringUtil;
|
||||
|
||||
/**
|
||||
* Image reader for SVG document fragments.
|
||||
*
|
||||
@@ -132,6 +135,7 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
// Set ImageReadParams as hints
|
||||
// Note: The cast to Map invokes a different method that preserves
|
||||
// unset defaults, DO NOT REMOVE!
|
||||
//noinspection rawtypes
|
||||
rasterizer.setTranscodingHints((Map) paramsToHints(svgParam));
|
||||
}
|
||||
|
||||
@@ -260,7 +264,7 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IOException {
|
||||
public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) {
|
||||
return Collections.singleton(ImageTypeSpecifier.createFromRenderedImage(rasterizer.createImage(1, 1))).iterator();
|
||||
}
|
||||
|
||||
@@ -289,7 +293,7 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
}
|
||||
|
||||
// This is cheating... We don't fully transcode after all
|
||||
protected void transcode(Document document, final String uri, final TranscoderOutput output) throws TranscoderException {
|
||||
protected void transcode(Document document, final String uri, final TranscoderOutput output) {
|
||||
// Sets up root, curTxf & curAoi
|
||||
// ----
|
||||
if (document != null) {
|
||||
@@ -584,9 +588,7 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
return dest;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
TranscoderException exception = new TranscoderException(ex.getMessage());
|
||||
exception.initCause(ex);
|
||||
throw exception;
|
||||
throw new TranscoderException(ex.getMessage(), ex);
|
||||
}
|
||||
finally {
|
||||
if (context != null) {
|
||||
@@ -655,7 +657,7 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
if (allowExternalResources) {
|
||||
return super.getExternalResourceSecurity(resourceURL, docURL);
|
||||
}
|
||||
return new NoLoadExternalResourceSecurity();
|
||||
return new EmbededExternalResourceSecurity(resourceURL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+21
-4
@@ -54,8 +54,6 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
@@ -225,7 +223,7 @@ public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader>
|
||||
assertEquals(500, image.getHeight());
|
||||
|
||||
// CSS and embedded resources all go!
|
||||
verifyZeroInteractions(listener);
|
||||
verifyNoInteractions(listener);
|
||||
}
|
||||
finally {
|
||||
reader.dispose();
|
||||
@@ -266,7 +264,7 @@ public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader>
|
||||
assertEquals(500, image.getHeight());
|
||||
|
||||
// No more warnings now that the base URI is set
|
||||
verifyZeroInteractions(listener);
|
||||
verifyNoInteractions(listener);
|
||||
}
|
||||
finally {
|
||||
reader.dispose();
|
||||
@@ -297,6 +295,25 @@ public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader>
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadEmbeddedWithDisallowExternalResources() throws IOException{
|
||||
// File using "data:" URLs for embedded resources
|
||||
URL resource = getClassLoaderResource("/svg/embedded-data-resource.svg");
|
||||
SVGImageReader reader = createReader();
|
||||
|
||||
TestData data = new TestData(resource, (Dimension) null);
|
||||
try (ImageInputStream stream = data.getInputStream()) {
|
||||
reader.setInput(stream);
|
||||
|
||||
SVGReadParam param = reader.getDefaultReadParam();
|
||||
param.setAllowExternalResources(false);
|
||||
reader.read(0, param);
|
||||
}
|
||||
finally {
|
||||
reader.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = SecurityException.class)
|
||||
public void testDisallowedExternalResources() throws URISyntaxException, IOException {
|
||||
// system-property set to true in surefire-plugin-settings in the pom
|
||||
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 92 KiB |
@@ -4,12 +4,16 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.6.3</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-bmp</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: BMP plugin</name>
|
||||
<description>ImageIO plugin for Microsoft Device Independent Bitmap (BMP/DIB) format.</description>
|
||||
|
||||
<properties>
|
||||
<project.jpms.module.name>com.twelvemonkeys.imageio.bmp</project.jpms.module.name>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
|
||||
+46
-55
@@ -30,28 +30,6 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.*;
|
||||
import java.io.DataInput;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.event.IIOReadUpdateListener;
|
||||
import javax.imageio.event.IIOReadWarningListener;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.metadata.IIOMetadataFormatImpl;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
|
||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||
import com.twelvemonkeys.imageio.stream.SubImageInputStream;
|
||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||
@@ -61,6 +39,23 @@ import com.twelvemonkeys.io.LittleEndianDataInputStream;
|
||||
import com.twelvemonkeys.io.enc.DecoderStream;
|
||||
import com.twelvemonkeys.xml.XMLSerializer;
|
||||
|
||||
import javax.imageio.*;
|
||||
import javax.imageio.event.IIOReadUpdateListener;
|
||||
import javax.imageio.event.IIOReadWarningListener;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.metadata.IIOMetadataFormatImpl;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.*;
|
||||
import java.io.DataInput;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* ImageReader for Microsoft Windows Bitmap (BMP) format.
|
||||
*
|
||||
@@ -205,7 +200,7 @@ public final class BMPImageReader extends ImageReaderBase {
|
||||
checkBounds(pImageIndex);
|
||||
|
||||
// TODO: Better implementation, include INT_RGB types for 3BYTE_BGR and 4BYTE_ABGR for INT_ARGB
|
||||
return Arrays.asList(getRawImageType(pImageIndex)).iterator();
|
||||
return Collections.singletonList(getRawImageType(pImageIndex)).iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -410,6 +405,13 @@ public final class BMPImageReader extends ImageReaderBase {
|
||||
|
||||
private ImageReader initReaderDelegate(int compression) throws IOException {
|
||||
ImageReader reader = getImageReaderDelegate(compression);
|
||||
reader.reset();
|
||||
|
||||
// Install listener
|
||||
ListenerDelegator listenerDelegator = new ListenerDelegator();
|
||||
reader.addIIOReadWarningListener(listenerDelegator);
|
||||
reader.addIIOReadProgressListener(listenerDelegator);
|
||||
reader.addIIOReadUpdateListener(listenerDelegator);
|
||||
|
||||
imageInput.seek(pixelOffset);
|
||||
reader.setInput(new SubImageInputStream(imageInput, header.getImageSize()));
|
||||
@@ -450,12 +452,6 @@ public final class BMPImageReader extends ImageReaderBase {
|
||||
|
||||
ImageReader reader = readers.next();
|
||||
|
||||
// Install listener
|
||||
ListenerDelegator listenerDelegator = new ListenerDelegator();
|
||||
reader.addIIOReadWarningListener(listenerDelegator);
|
||||
reader.addIIOReadProgressListener(listenerDelegator);
|
||||
reader.addIIOReadUpdateListener(listenerDelegator);
|
||||
|
||||
// Cache for later use
|
||||
switch (compression) {
|
||||
case DIB.COMPRESSION_JPEG:
|
||||
@@ -481,8 +477,12 @@ public final class BMPImageReader extends ImageReaderBase {
|
||||
|
||||
private void readRowByte(final DataInput input, final int height, final Rectangle srcRegion, final int xSub, final int ySub,
|
||||
final byte[] rowDataByte, final WritableRaster destChannel, final Raster srcChannel, final int y) throws IOException {
|
||||
// Flip into position?
|
||||
int srcY = !header.topDown ? height - 1 - y : y;
|
||||
int dstY = (srcY - srcRegion.y) / ySub;
|
||||
|
||||
// If subsampled or outside source region, skip entire row
|
||||
if (y % ySub != 0 || height - 1 - y < srcRegion.y || height - 1 - y >= srcRegion.y + srcRegion.height) {
|
||||
if (srcY % ySub != 0 || srcY < srcRegion.y || srcY >= srcRegion.y + srcRegion.height) {
|
||||
input.skipBytes(rowDataByte.length);
|
||||
|
||||
return;
|
||||
@@ -497,19 +497,17 @@ public final class BMPImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
if (header.topDown) {
|
||||
destChannel.setDataElements(0, y, srcChannel);
|
||||
} else {
|
||||
// Flip into position
|
||||
int dstY = (height - 1 - y - srcRegion.y) / ySub;
|
||||
destChannel.setDataElements(0, dstY, srcChannel);
|
||||
}
|
||||
destChannel.setDataElements(0, dstY, srcChannel);
|
||||
}
|
||||
|
||||
private void readRowUShort(final DataInput input, final int height, final Rectangle srcRegion, final int xSub, final int ySub,
|
||||
final short[] rowDataUShort, final WritableRaster destChannel, final Raster srcChannel, final int y) throws IOException {
|
||||
// Flip into position?
|
||||
int srcY = !header.topDown ? height - 1 - y : y;
|
||||
int dstY = (srcY - srcRegion.y) / ySub;
|
||||
|
||||
// If subsampled or outside source region, skip entire row
|
||||
if (y % ySub != 0 || height - 1 - y < srcRegion.y || height - 1 - y >= srcRegion.y + srcRegion.height) {
|
||||
if (srcY % ySub != 0 || srcY < srcRegion.y || srcY >= srcRegion.y + srcRegion.height) {
|
||||
input.skipBytes(rowDataUShort.length * 2 + (rowDataUShort.length % 2) * 2);
|
||||
|
||||
return;
|
||||
@@ -529,19 +527,17 @@ public final class BMPImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
if (header.topDown) {
|
||||
destChannel.setDataElements(0, y, srcChannel);
|
||||
} else {
|
||||
// Flip into position
|
||||
int dstY = (height - 1 - y - srcRegion.y) / ySub;
|
||||
destChannel.setDataElements(0, dstY, srcChannel);
|
||||
}
|
||||
destChannel.setDataElements(0, dstY, srcChannel);
|
||||
}
|
||||
|
||||
private void readRowInt(final DataInput input, final int height, final Rectangle srcRegion, final int xSub, final int ySub,
|
||||
final int[] rowDataInt, final WritableRaster destChannel, final Raster srcChannel, final int y) throws IOException {
|
||||
// Flip into position?
|
||||
int srcY = !header.topDown ? height - 1 - y : y;
|
||||
int dstY = (srcY - srcRegion.y) / ySub;
|
||||
|
||||
// If subsampled or outside source region, skip entire row
|
||||
if (y % ySub != 0 || height - 1 - y < srcRegion.y || height - 1 - y >= srcRegion.y + srcRegion.height) {
|
||||
if (srcY % ySub != 0 || srcY < srcRegion.y || srcY >= srcRegion.y + srcRegion.height) {
|
||||
input.skipBytes(rowDataInt.length * 4);
|
||||
|
||||
return;
|
||||
@@ -556,13 +552,7 @@ public final class BMPImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
if (header.topDown) {
|
||||
destChannel.setDataElements(0, y, srcChannel);
|
||||
} else {
|
||||
// Flip into position
|
||||
int dstY = (height - 1 - y - srcRegion.y) / ySub;
|
||||
destChannel.setDataElements(0, dstY, srcChannel);
|
||||
}
|
||||
destChannel.setDataElements(0, dstY, srcChannel);
|
||||
}
|
||||
|
||||
// TODO: Candidate util method
|
||||
@@ -633,7 +623,8 @@ public final class BMPImageReader extends ImageReaderBase {
|
||||
return new BMPMetadata(header, colors);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public static void main(String[] args) {
|
||||
BMPImageReaderSpi provider = new BMPImageReaderSpi();
|
||||
BMPImageReader reader = new BMPImageReader(provider);
|
||||
|
||||
@@ -686,7 +677,7 @@ public final class BMPImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "UnusedDeclaration"})
|
||||
@SuppressWarnings({ "unchecked", "UnusedDeclaration", "SameParameterValue" })
|
||||
static <T extends Throwable> void throwAs(final Class<T> pType, final Throwable pThrowable) throws T {
|
||||
throw (T) pThrowable;
|
||||
}
|
||||
|
||||
+5
-1
@@ -364,7 +364,11 @@ abstract class DIBHeader {
|
||||
|
||||
public String getBMPVersion() {
|
||||
// This is to be compatible with the native metadata of the original com.sun....BMPMetadata
|
||||
return compression == DIB.COMPRESSION_BITFIELDS ? "BMP v. 3.x NT" : "BMP v. 3.x";
|
||||
return size > DIB.BITMAP_INFO_HEADER_SIZE
|
||||
? "BMP V2/V3 INFO"
|
||||
: compression == DIB.COMPRESSION_BITFIELDS || compression == DIB.COMPRESSION_ALPHA_BITFIELDS
|
||||
? "BMP v. 3.x NT"
|
||||
: "BMP v. 3.x";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+8
-4
@@ -32,8 +32,8 @@ package com.twelvemonkeys.imageio.plugins.bmp;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assume.assumeNoException;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.anyFloat;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@@ -295,7 +295,7 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
|
||||
// At least imageStarted and imageComplete, plus any number of imageProgress
|
||||
InOrder ordered = inOrder(listener);
|
||||
ordered.verify(listener).imageStarted(reader, 0);
|
||||
ordered.verify(listener, atLeastOnce()).imageProgress(eq(reader), anyInt());
|
||||
ordered.verify(listener, atLeastOnce()).imageProgress(eq(reader), anyFloat());
|
||||
ordered.verify(listener).imageComplete(reader);
|
||||
}
|
||||
|
||||
@@ -318,7 +318,7 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
|
||||
// At least imageStarted and imageComplete, plus any number of imageProgress
|
||||
InOrder ordered = inOrder(listener);
|
||||
ordered.verify(listener).imageStarted(reader, 0);
|
||||
ordered.verify(listener, atLeastOnce()).imageProgress(eq(reader), anyInt());
|
||||
ordered.verify(listener, atLeastOnce()).imageProgress(eq(reader), anyFloat());
|
||||
ordered.verify(listener).imageComplete(reader);
|
||||
}
|
||||
|
||||
@@ -342,6 +342,7 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
|
||||
|
||||
for (TestData data : getTestData()) {
|
||||
if (data.getInput().toString().contains("pal8offs")) {
|
||||
// Skip: Contains extra bogus PaletteEntry nodes
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -358,6 +359,7 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
|
||||
System.err.println("WARNING: Reading " + data + " caused exception: " + e.getMessage());
|
||||
continue;
|
||||
}
|
||||
|
||||
IIOMetadata jreMetadata = jreReader.getImageMetadata(0);
|
||||
|
||||
assertTrue(metadata.isStandardMetadataFormatSupported());
|
||||
@@ -370,6 +372,7 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
|
||||
String absolutePath = data.toString();
|
||||
String localPath = absolutePath.substring(absolutePath.lastIndexOf("test-classes") + 12);
|
||||
|
||||
// TODO: blauesglas_16_bitmask444 fails BMP Version for 11+
|
||||
Node expectedTree = jreMetadata.getAsTree(format);
|
||||
Node actualTree = metadata.getAsTree(format);
|
||||
|
||||
@@ -428,6 +431,7 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("RedundantIfStatement")
|
||||
private boolean excludeEqualValueTest(final Node expected) {
|
||||
if (expected.getLocalName().equals("ImageSize")) {
|
||||
// JRE metadata returns 0, even if known in reader...
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.6.3</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-clippath</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: Photoshop Path Support</name>
|
||||
@@ -12,6 +12,10 @@
|
||||
Photoshop Clipping Path Support.
|
||||
</description>
|
||||
|
||||
<properties>
|
||||
<project.jpms.module.name>com.twelvemonkeys.imageio.clippath</project.jpms.module.name>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
|
||||
+1
@@ -39,6 +39,7 @@ import java.io.IOException;
|
||||
*
|
||||
* @deprecated Use {@link AdobePathReader} instead. This class will be removed in a future release.
|
||||
*/
|
||||
@Deprecated
|
||||
public final class AdobePathBuilder {
|
||||
|
||||
private final AdobePathReader delegate;
|
||||
|
||||
@@ -4,11 +4,15 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.6.3</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: Core</name>
|
||||
|
||||
<properties>
|
||||
<project.jpms.module.name>com.twelvemonkeys.imageio.core</project.jpms.module.name>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
|
||||
@@ -265,8 +265,9 @@ public abstract class ImageReaderBase extends ImageReader {
|
||||
// - transferType is ok
|
||||
// - bands are ok
|
||||
// TODO: Test if color model is ok?
|
||||
if (specifier.getSampleModel().getTransferType() == dest.getSampleModel().getTransferType() &&
|
||||
specifier.getNumBands() <= dest.getSampleModel().getNumBands()) {
|
||||
if (specifier.getSampleModel().getTransferType() == dest.getSampleModel().getTransferType()
|
||||
&& Arrays.equals(specifier.getSampleModel().getSampleSize(), dest.getSampleModel().getSampleSize())
|
||||
&& specifier.getNumBands() <= dest.getSampleModel().getNumBands()) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
+174
-20
@@ -39,6 +39,7 @@ import com.twelvemonkeys.util.LRUHashMap;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.color.ICC_ColorSpace;
|
||||
import java.awt.color.ICC_Profile;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.ref.WeakReference;
|
||||
@@ -96,6 +97,8 @@ public final class ColorSpaces {
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public static final int CS_GENERIC_CMYK = 5001;
|
||||
|
||||
static final int ICC_PROFILE_HEADER_SIZE = 128;
|
||||
|
||||
// Weak references to hold the color spaces while cached
|
||||
private static WeakReference<ICC_Profile> adobeRGB1998 = new WeakReference<>(null);
|
||||
private static WeakReference<ICC_Profile> genericCMYK = new WeakReference<>(null);
|
||||
@@ -104,16 +107,8 @@ public final class ColorSpaces {
|
||||
private static final Map<Key, ICC_ColorSpace> cache = new LRUHashMap<>(10);
|
||||
|
||||
static {
|
||||
try {
|
||||
// Force invocation of ProfileDeferralMgr.activateProfiles() to avoid JDK-6986863
|
||||
ICC_Profile.getInstance(ColorSpace.CS_sRGB).getData();
|
||||
}
|
||||
catch (Throwable disasters) {
|
||||
System.err.println("ICC Color Profile not properly activated due to the exception below.");
|
||||
System.err.println("Expect to see JDK-6986863 in action, and consider filing a bug report to your JRE provider.");
|
||||
|
||||
disasters.printStackTrace();
|
||||
}
|
||||
// In case we didn't activate through SPI already
|
||||
ProfileDeferralActivator.activateProfiles();
|
||||
}
|
||||
|
||||
private ColorSpaces() {}
|
||||
@@ -148,9 +143,11 @@ public final class ColorSpaces {
|
||||
|
||||
private static byte[] getProfileHeaderWithProfileId(final ICC_Profile profile) {
|
||||
// Get *entire profile data*... :-/
|
||||
byte[] data = profile.getData();
|
||||
return getProfileHeaderWithProfileId(profile.getData());
|
||||
}
|
||||
|
||||
// Clear out preferred CMM, platform & creator, as these does not affect the profile in any way
|
||||
private static byte[] getProfileHeaderWithProfileId(byte[] data) {
|
||||
// Clear out preferred CMM, platform & creator, as these don't affect the profile in any way
|
||||
// - LCMS updates CMM + creator to "lcms" and platform to current platform
|
||||
// - KCMS keeps the values in the file...
|
||||
Arrays.fill(data, ICC_Profile.icHdrCmmId, ICC_Profile.icHdrCmmId + 4, (byte) 0);
|
||||
@@ -167,7 +164,7 @@ public final class ColorSpaces {
|
||||
System.arraycopy(md5, 0, data, ICC_Profile.icHdrProfileID, md5.length);
|
||||
|
||||
// ICC profile header is the first 128 bytes
|
||||
return Arrays.copyOf(data, 128);
|
||||
return Arrays.copyOf(data, ICC_PROFILE_HEADER_SIZE);
|
||||
}
|
||||
|
||||
private static byte[] computeMD5(byte[] data) {
|
||||
@@ -203,25 +200,35 @@ public final class ColorSpaces {
|
||||
Key key = new Key(profileHeader);
|
||||
|
||||
synchronized (cache) {
|
||||
ICC_ColorSpace cs = cache.get(key);
|
||||
ICC_ColorSpace cs = getCachedCS(key);
|
||||
|
||||
if (cs == null) {
|
||||
cs = new ICC_ColorSpace(profile);
|
||||
|
||||
validateColorSpace(cs);
|
||||
|
||||
// On LCMS, validation *alters* the profile header, need to re-generate key
|
||||
key = profileCleaner.validationAltersProfileHeader()
|
||||
? new Key(getProfileHeaderWithProfileId(cs.getProfile()))
|
||||
: key;
|
||||
|
||||
cache.put(key, cs);
|
||||
|
||||
// On LCMS, validation *alters* the profile header, need to re-generate key
|
||||
if (profileCleaner.validationAltersProfileHeader()) {
|
||||
cache.put(new Key(getProfileHeaderWithProfileId(cs.getProfile())), cs);
|
||||
}
|
||||
}
|
||||
|
||||
return cs;
|
||||
}
|
||||
}
|
||||
|
||||
private static ICC_ColorSpace getCachedCS(Key profileKey) {
|
||||
synchronized (cache) {
|
||||
return cache.get(profileKey);
|
||||
}
|
||||
}
|
||||
|
||||
private static ICC_ColorSpace getCachedCS(final byte[] profileHeader) {
|
||||
return getCachedCS(new Key(profileHeader));
|
||||
}
|
||||
|
||||
private static void validateColorSpace(final ICC_ColorSpace cs) {
|
||||
// Validate the color space, to avoid caching bad profiles/color spaces
|
||||
// Will throw IllegalArgumentException or CMMException if the profile is bad
|
||||
@@ -239,6 +246,7 @@ public final class ColorSpaces {
|
||||
* @return {@code true} if {@code profile} is equal to the default sRGB profile.
|
||||
* @throws IllegalArgumentException if {@code profile} is {@code null}
|
||||
*
|
||||
* @see java.awt.color.ColorSpace#CS_sRGB
|
||||
* @see java.awt.color.ColorSpace#isCS_sRGB()
|
||||
*/
|
||||
public static boolean isCS_sRGB(final ICC_Profile profile) {
|
||||
@@ -247,6 +255,21 @@ public final class ColorSpaces {
|
||||
return profile.getColorSpaceType() == ColorSpace.TYPE_RGB && Arrays.equals(getProfileHeaderWithProfileId(profile), sRGB.header);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether an ICC color profile is equal to the default GRAY profile.
|
||||
*
|
||||
* @param profile the ICC profile to test. May not be {@code null}.
|
||||
* @return {@code true} if {@code profile} is equal to the default GRAY profile.
|
||||
* @throws IllegalArgumentException if {@code profile} is {@code null}
|
||||
*
|
||||
* @see java.awt.color.ColorSpace#CS_GRAY
|
||||
*/
|
||||
public static boolean isCS_GRAY(final ICC_Profile profile) {
|
||||
Validate.notNull(profile, "profile");
|
||||
|
||||
return profile.getColorSpaceType() == ColorSpace.TYPE_GRAY && Arrays.equals(getProfileHeaderWithProfileId(profile), GRAY.header);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether an ICC color profile is known to cause problems for {@link java.awt.image.ColorConvertOp}.
|
||||
* <p>
|
||||
@@ -302,6 +325,137 @@ public final class ColorSpaces {
|
||||
return profile;
|
||||
}
|
||||
|
||||
public static ICC_Profile readProfileRaw(final InputStream input) throws IOException {
|
||||
return ICC_Profile.getInstance(input);
|
||||
}
|
||||
|
||||
public static ICC_Profile readProfile(final InputStream input) throws IOException {
|
||||
// TODO: Implement this smarter?
|
||||
// Could read the header 128 bytes, get size + magic, then read read rest into array and feed the byte[] method...
|
||||
ICC_Profile profile = ICC_Profile.getInstance(input);
|
||||
|
||||
if (profile == null) {
|
||||
throw new IllegalArgumentException("Invalid ICC Profile Data");
|
||||
}
|
||||
|
||||
return createProfile(profile.getData());
|
||||
}
|
||||
|
||||
public static ICC_Profile createProfileRaw(final byte[] input) {
|
||||
try {
|
||||
return readProfileRaw(new ByteArrayInputStream(input));
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new IllegalArgumentException("Invalid ICC Profile Data", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static ICC_Profile createProfile(final byte[] input) {
|
||||
Validate.notNull(input, "input");
|
||||
|
||||
if (input.length < ICC_PROFILE_HEADER_SIZE) { // Can't be less than size of ICC header
|
||||
throw new IllegalArgumentException("Truncated ICC profile, length < 128: " + input.length);
|
||||
}
|
||||
int size = intBigEndian(input, 0);
|
||||
if (size < 0 || size > input.length) {
|
||||
throw new IllegalArgumentException("Truncated ICC profile, length < " + size + ": " + input.length);
|
||||
}
|
||||
|
||||
if (input[36] != 'a' || input[37] != 'c' || input[38] != 's' || input[39] != 'p') {
|
||||
throw new IllegalArgumentException("Not an ICC profile, missing file signature");
|
||||
}
|
||||
|
||||
// Look up in cache before returning, these are already validated
|
||||
byte[] profileHeader = getProfileHeaderWithProfileId(input);
|
||||
int csType = getCsType(profileHeader);
|
||||
|
||||
ICC_ColorSpace internal = getInternalCS(csType, profileHeader);
|
||||
if (internal != null) {
|
||||
return internal.getProfile();
|
||||
}
|
||||
|
||||
ICC_ColorSpace cached = getCachedCS(profileHeader);
|
||||
if (cached != null) {
|
||||
return cached.getProfile();
|
||||
}
|
||||
|
||||
// WEIRDNESS: Unlike the InputStream version, the byte version
|
||||
// of ICC_Profile.getInstance() does not discard extra bytes at the end.
|
||||
// We'll chop them off here for convenience
|
||||
byte[] profileBytes = input.length == size ? input : Arrays.copyOf(input, size);
|
||||
ICC_Profile profile = ICC_Profile.getInstance(profileBytes);
|
||||
|
||||
// We'll validate & cache by creating a color space and returning its profile...
|
||||
// TODO: Rewrite with separate cache for profiles...
|
||||
return createColorSpace(profile).getProfile();
|
||||
}
|
||||
|
||||
private static int intBigEndian(byte[] data, int index) {
|
||||
return (data[index] & 0xff) << 24 | (data[index + 1] & 0xff) << 16 | (data[index + 2] & 0xff) << 8 | (data[index + 3] & 0xff);
|
||||
}
|
||||
|
||||
private static int getCsType(byte[] profileHeader) {
|
||||
int csSig = intBigEndian(profileHeader, ICC_Profile.icHdrColorSpace);
|
||||
|
||||
// TODO: Wonder why they didn't just use the sig as type, when there is obviously a 1:1 mapping...
|
||||
|
||||
switch (csSig) {
|
||||
case ICC_Profile.icSigXYZData:
|
||||
return ColorSpace.TYPE_XYZ;
|
||||
case ICC_Profile.icSigLabData:
|
||||
return ColorSpace.TYPE_Lab;
|
||||
case ICC_Profile.icSigLuvData:
|
||||
return ColorSpace.TYPE_Luv;
|
||||
case ICC_Profile.icSigYCbCrData:
|
||||
return ColorSpace.TYPE_YCbCr;
|
||||
case ICC_Profile.icSigYxyData:
|
||||
return ColorSpace.TYPE_Yxy;
|
||||
case ICC_Profile.icSigRgbData:
|
||||
return ColorSpace.TYPE_RGB;
|
||||
case ICC_Profile.icSigGrayData:
|
||||
return ColorSpace.TYPE_GRAY;
|
||||
case ICC_Profile.icSigHsvData:
|
||||
return ColorSpace.TYPE_HSV;
|
||||
case ICC_Profile.icSigHlsData:
|
||||
return ColorSpace.TYPE_HLS;
|
||||
case ICC_Profile.icSigCmykData:
|
||||
return ColorSpace.TYPE_CMYK;
|
||||
// Note: There is no TYPE_* 10...
|
||||
case ICC_Profile.icSigCmyData:
|
||||
return ColorSpace.TYPE_CMY;
|
||||
case ICC_Profile.icSigSpace2CLR:
|
||||
return ColorSpace.TYPE_2CLR;
|
||||
case ICC_Profile.icSigSpace3CLR:
|
||||
return ColorSpace.TYPE_3CLR;
|
||||
case ICC_Profile.icSigSpace4CLR:
|
||||
return ColorSpace.TYPE_4CLR;
|
||||
case ICC_Profile.icSigSpace5CLR:
|
||||
return ColorSpace.TYPE_5CLR;
|
||||
case ICC_Profile.icSigSpace6CLR:
|
||||
return ColorSpace.TYPE_6CLR;
|
||||
case ICC_Profile.icSigSpace7CLR:
|
||||
return ColorSpace.TYPE_7CLR;
|
||||
case ICC_Profile.icSigSpace8CLR:
|
||||
return ColorSpace.TYPE_8CLR;
|
||||
case ICC_Profile.icSigSpace9CLR:
|
||||
return ColorSpace.TYPE_9CLR;
|
||||
case ICC_Profile.icSigSpaceACLR:
|
||||
return ColorSpace.TYPE_ACLR;
|
||||
case ICC_Profile.icSigSpaceBCLR:
|
||||
return ColorSpace.TYPE_BCLR;
|
||||
case ICC_Profile.icSigSpaceCCLR:
|
||||
return ColorSpace.TYPE_CCLR;
|
||||
case ICC_Profile.icSigSpaceDCLR:
|
||||
return ColorSpace.TYPE_DCLR;
|
||||
case ICC_Profile.icSigSpaceECLR:
|
||||
return ColorSpace.TYPE_ECLR;
|
||||
case ICC_Profile.icSigSpaceFCLR:
|
||||
return ColorSpace.TYPE_FCLR;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid ICC color space signature: " + csSig); // TODO: fourCC?
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the color space specified by the given color space constant.
|
||||
* <p>
|
||||
@@ -449,7 +603,7 @@ public final class ColorSpaces {
|
||||
}
|
||||
}
|
||||
|
||||
// Cache header profile data to avoid excessive array creation/copying in static inner class for on-demand lazy init
|
||||
// Cache header profile data to avoid excessive array creation/copying. Use static inner class for on-demand lazy init
|
||||
private static class sRGB {
|
||||
private static final byte[] header = getProfileHeaderWithProfileId(ICC_Profile.getInstance(ColorSpace.CS_sRGB));
|
||||
}
|
||||
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
package com.twelvemonkeys.imageio.color;
|
||||
|
||||
import javax.imageio.spi.ImageInputStreamSpi;
|
||||
import javax.imageio.spi.ServiceRegistry;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.File;
|
||||
import java.util.Locale;
|
||||
|
||||
import static com.twelvemonkeys.imageio.util.IIOUtil.deregisterProvider;
|
||||
|
||||
/**
|
||||
* This class exists to force early invocation of {@code ProfileDeferralMgr.activateProfiles()},
|
||||
* in an attempt to avoid JDK-6986863 and related bugs in Java < 17.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @see <a href="https://bugs.openjdk.java.net/browse/JDK-6986863">JDK-6986863</a>
|
||||
*/
|
||||
final class ProfileDeferralActivator {
|
||||
|
||||
static {
|
||||
activateProfilesInternal();
|
||||
}
|
||||
|
||||
private static void activateProfilesInternal() {
|
||||
try {
|
||||
// Force invocation of ProfileDeferralMgr.activateProfiles() to avoid JDK-6986863 and friends.
|
||||
// Relies on static initializer in ColorConvertOp to actually invoke ProfileDeferralMgr.activateProfiles()
|
||||
Class.forName("java.awt.image.ColorConvertOp");
|
||||
}
|
||||
catch (Throwable disasters) {
|
||||
System.err.println("ProfileDeferralMgr.activateProfiles() failed. ICC Color Profiles may not work properly, see stack trace below.");
|
||||
System.err.println("For more information, see https://bugs.openjdk.java.net/browse/JDK-6986863");
|
||||
System.err.println("Please upgrade to Java 17 or later where this bug is fixed, or ask your JRE provider to backport the fix.");
|
||||
System.err.println();
|
||||
System.err.println("If you can't update to Java 17, a possible workaround is to add");
|
||||
System.err.println("\tClass.forName(\"java.awt.image.ColorConvertOp\");");
|
||||
System.err.println("*early* in your application startup code, to force profile activation before profiles are accessed.");
|
||||
System.err.println();
|
||||
|
||||
disasters.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
static void activateProfiles() {
|
||||
// This method exists for other classes in the package to
|
||||
// ensure this class' static initializer is run.
|
||||
}
|
||||
|
||||
/**
|
||||
* This is not a service provider, but exploits the SPI mechanism as a hook to force early profile activation.
|
||||
*/
|
||||
public static final class Spi extends ImageInputStreamSpi {
|
||||
@Override public void onRegistration(ServiceRegistry registry, Class<?> category) {
|
||||
activateProfiles();
|
||||
|
||||
deregisterProvider(registry, this, category);
|
||||
}
|
||||
|
||||
@Override public String getDescription(Locale locale) {
|
||||
return getClass().getName();
|
||||
}
|
||||
|
||||
@Override public ImageInputStream createInputStreamInstance(Object input, boolean useCache, File cacheDir) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -61,7 +61,7 @@ public final class UInt32ColorModel extends ComponentColorModel {
|
||||
// This class only supports DataBuffer.TYPE_INT, cast is safe
|
||||
int[] ipixel = (int[]) pixel;
|
||||
for (int c = 0, nc = normOffset; c < numComponents; c++, nc++) {
|
||||
normComponents[nc] = ((float) (ipixel[c] & 0xffffffffl)) / ((float) ((1l << getComponentSize(c)) - 1));
|
||||
normComponents[nc] = ((float) (ipixel[c] & 0xFFFFFFFFL)) / ((float) ((1L << getComponentSize(c)) - 1));
|
||||
}
|
||||
|
||||
int numColorComponents = getNumColorComponents();
|
||||
|
||||
+87
-31
@@ -45,36 +45,82 @@ public final class YCbCrConverter {
|
||||
private final static int CENTERJSAMPLE = 128;
|
||||
private final static int ONE_HALF = 1 << (SCALEBITS - 1);
|
||||
|
||||
private final static int[] Cr_R_LUT = new int[MAXJSAMPLE + 1];
|
||||
private final static int[] Cb_B_LUT = new int[MAXJSAMPLE + 1];
|
||||
private final static int[] Cr_G_LUT = new int[MAXJSAMPLE + 1];
|
||||
private final static int[] Cb_G_LUT = new int[MAXJSAMPLE + 1];
|
||||
private final static class JPEG {
|
||||
private final static int[] Cr_R_LUT = new int[MAXJSAMPLE + 1];
|
||||
private final static int[] Cb_B_LUT = new int[MAXJSAMPLE + 1];
|
||||
private final static int[] Cr_G_LUT = new int[MAXJSAMPLE + 1];
|
||||
private final static int[] Cb_G_LUT = new int[MAXJSAMPLE + 1];
|
||||
|
||||
/**
|
||||
* Initializes tables for YCC->RGB color space conversion.
|
||||
*/
|
||||
private static void buildYCCtoRGBtable() {
|
||||
if (ColorSpaces.DEBUG) {
|
||||
System.err.println("Building YCC conversion table");
|
||||
/**
|
||||
* Initializes tables for YCC->RGB color space conversion.
|
||||
*/
|
||||
private static void buildYCCtoRGBtable() {
|
||||
if (ColorSpaces.DEBUG) {
|
||||
System.err.println("Building JPEG YCbCr conversion table");
|
||||
}
|
||||
|
||||
for (int i = 0, x = -CENTERJSAMPLE; i <= MAXJSAMPLE; i++, x++) {
|
||||
// i is the actual input pixel value, in the range 0..MAXJSAMPLE
|
||||
// The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE
|
||||
// Cr=>R value is nearest int to 1.40200 * x
|
||||
Cr_R_LUT[i] = (int) ((1.40200 * (1 << SCALEBITS) + 0.5) * x + ONE_HALF) >> SCALEBITS;
|
||||
// Cb=>B value is nearest int to 1.77200 * x
|
||||
Cb_B_LUT[i] = (int) ((1.77200 * (1 << SCALEBITS) + 0.5) * x + ONE_HALF) >> SCALEBITS;
|
||||
// Cr=>G value is scaled-up -0.71414 * x
|
||||
Cr_G_LUT[i] = -(int) (0.71414 * (1 << SCALEBITS) + 0.5) * x;
|
||||
// Cb=>G value is scaled-up -0.34414 * x
|
||||
// We also add in ONE_HALF so that need not do it in inner loop
|
||||
Cb_G_LUT[i] = -(int) ((0.34414) * (1 << SCALEBITS) + 0.5) * x + ONE_HALF;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0, x = -CENTERJSAMPLE; i <= MAXJSAMPLE; i++, x++) {
|
||||
// i is the actual input pixel value, in the range 0..MAXJSAMPLE
|
||||
// The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE
|
||||
// Cr=>R value is nearest int to 1.40200 * x
|
||||
Cr_R_LUT[i] = (int) ((1.40200 * (1 << SCALEBITS) + 0.5) * x + ONE_HALF) >> SCALEBITS;
|
||||
// Cb=>B value is nearest int to 1.77200 * x
|
||||
Cb_B_LUT[i] = (int) ((1.77200 * (1 << SCALEBITS) + 0.5) * x + ONE_HALF) >> SCALEBITS;
|
||||
// Cr=>G value is scaled-up -0.71414 * x
|
||||
Cr_G_LUT[i] = -(int) (0.71414 * (1 << SCALEBITS) + 0.5) * x;
|
||||
// Cb=>G value is scaled-up -0.34414 * x
|
||||
// We also add in ONE_HALF so that need not do it in inner loop
|
||||
Cb_G_LUT[i] = -(int) ((0.34414) * (1 << SCALEBITS) + 0.5) * x + ONE_HALF;
|
||||
static {
|
||||
buildYCCtoRGBtable();
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
buildYCCtoRGBtable();
|
||||
private final static class ITU_R_601 {
|
||||
private final static int[] Cr_R_LUT = new int[MAXJSAMPLE + 1];
|
||||
private final static int[] Cb_B_LUT = new int[MAXJSAMPLE + 1];
|
||||
private final static int[] Cr_G_LUT = new int[MAXJSAMPLE + 1];
|
||||
private final static int[] Cb_G_LUT = new int[MAXJSAMPLE + 1];
|
||||
private final static int[] Y_LUT = new int[MAXJSAMPLE + 1];
|
||||
|
||||
/**
|
||||
* Initializes tables for YCC->RGB color space conversion.
|
||||
*/
|
||||
private static void buildYCCtoRGBtable() {
|
||||
if (ColorSpaces.DEBUG) {
|
||||
System.err.println("Building ITU-R REC.601 YCbCr conversion table");
|
||||
}
|
||||
|
||||
for (int i = 0, x = -CENTERJSAMPLE; i <= MAXJSAMPLE; i++, x++) {
|
||||
// i is the actual input pixel value, in the range 0..MAXJSAMPLE
|
||||
// The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE
|
||||
|
||||
// Y'CbCr to RGB conversion, using values from BT.601 specification:
|
||||
// R = 1.16438 * (Y'-16) + 1.59603 * (Cr-128)
|
||||
// G = 1.16438 * (Y'-16) - 0.39176 * (Cb-128) - 0.81297 * (Cr-128)
|
||||
// B = 1.16438 * (Y'-16) + 2.01723 * (Cb-128)
|
||||
|
||||
// Cr=>R value is nearest int to 1.59603 * x
|
||||
Cr_R_LUT[i] = ((int) (1.59603 * (1 << SCALEBITS) + 0.5) * x + ONE_HALF) >> SCALEBITS;
|
||||
// Cb=>B value is nearest int to 2.01723 * x
|
||||
Cb_B_LUT[i] = ((int) (2.01723 * (1 << SCALEBITS) + 0.5) * x + ONE_HALF) >> SCALEBITS;
|
||||
// Cr=>G value is scaled-up -0.81297 * x
|
||||
Cr_G_LUT[i] = -(int) (0.81297 * (1 << SCALEBITS) + 0.5) * x;
|
||||
// Cb=>G value is scaled-up -0.39176 * x
|
||||
// We also add in ONE_HALF so that need not do it in inner loop
|
||||
Cb_G_LUT[i] = -(int) ((0.39176) * (1 << SCALEBITS) + 0.5) * x + ONE_HALF;
|
||||
|
||||
// Y`=>RGB
|
||||
Y_LUT[i] = ((int) (1.16438 * (1 << SCALEBITS) + 0.5) * (i - 16) + ONE_HALF) >> SCALEBITS;
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
buildYCCtoRGBtable();
|
||||
}
|
||||
}
|
||||
|
||||
public static void convertYCbCr2RGB(final byte[] yCbCr, final byte[] rgb, final double[] coefficients, double[] referenceBW, final int offset) {
|
||||
@@ -108,17 +154,27 @@ public final class YCbCrConverter {
|
||||
rgb[offset + 1] = clamp(green);
|
||||
}
|
||||
|
||||
public static void convertYCbCr2RGB(final byte[] yCbCr, final byte[] rgb, final int offset) {
|
||||
int y = yCbCr[offset] & 0xff;
|
||||
int cr = yCbCr[offset + 2] & 0xff;
|
||||
public static void convertJPEGYCbCr2RGB(final byte[] yCbCr, final byte[] rgb, final int offset) {
|
||||
int y = yCbCr[offset ] & 0xff;
|
||||
int cb = yCbCr[offset + 1] & 0xff;
|
||||
int cr = yCbCr[offset + 2] & 0xff;
|
||||
|
||||
rgb[offset] = clamp(y + Cr_R_LUT[cr]);
|
||||
rgb[offset + 1] = clamp(y + (Cb_G_LUT[cb] + Cr_G_LUT[cr] >> SCALEBITS));
|
||||
rgb[offset + 2] = clamp(y + Cb_B_LUT[cb]);
|
||||
rgb[offset ] = clamp(y + JPEG.Cr_R_LUT[cr]);
|
||||
rgb[offset + 1] = clamp(y + (JPEG.Cb_G_LUT[cb] + JPEG.Cr_G_LUT[cr] >> SCALEBITS));
|
||||
rgb[offset + 2] = clamp(y + JPEG.Cb_B_LUT[cb]);
|
||||
}
|
||||
|
||||
private static byte clamp(int val) {
|
||||
public static void convertRec601YCbCr2RGB(final byte[] yCbCr, final byte[] rgb, final int offset) {
|
||||
int y = yCbCr[offset ] & 0xff;
|
||||
int cb = yCbCr[offset + 1] & 0xff;
|
||||
int cr = yCbCr[offset + 2] & 0xff;
|
||||
|
||||
rgb[offset ] = clamp(ITU_R_601.Y_LUT[y] + ITU_R_601.Cr_R_LUT[cr]);
|
||||
rgb[offset + 1] = clamp(ITU_R_601.Y_LUT[y] + (ITU_R_601.Cr_G_LUT[cr] + ITU_R_601.Cb_G_LUT[cb] >> SCALEBITS));
|
||||
rgb[offset + 2] = clamp(ITU_R_601.Y_LUT[y] + ITU_R_601.Cb_B_LUT[cb]);
|
||||
}
|
||||
|
||||
private static byte clamp(final int val) {
|
||||
return (byte) Math.max(0, Math.min(255, val));
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -49,10 +49,10 @@ public abstract class ReaderWriterProviderInfo extends ProviderInfo {
|
||||
private final String[] mimeTypes;
|
||||
private final String readerClassName;
|
||||
private final String[] readerSpiClassNames;
|
||||
private final Class[] inputTypes = new Class[] {ImageInputStream.class};
|
||||
private final Class<?>[] inputTypes = new Class<?>[] {ImageInputStream.class};
|
||||
private final String writerClassName;
|
||||
private final String[] writerSpiClassNames;
|
||||
private final Class[] outputTypes = new Class[] {ImageOutputStream.class};
|
||||
private final Class<?>[] outputTypes = new Class<?>[] {ImageOutputStream.class};
|
||||
private final boolean supportsStandardStreamMetadata;
|
||||
private final String nativeStreamMetadataFormatName;
|
||||
private final String nativeStreamMetadataFormatClassName;
|
||||
@@ -80,7 +80,7 @@ public abstract class ReaderWriterProviderInfo extends ProviderInfo {
|
||||
final String writerClassName,
|
||||
final String[] writerSpiClassNames,
|
||||
final boolean supportsStandardStreamMetadata,
|
||||
final String nativeStreameMetadataFormatName,
|
||||
final String nativeStreamMetadataFormatName,
|
||||
final String nativeStreamMetadataFormatClassName,
|
||||
final String[] extraStreamMetadataFormatNames,
|
||||
final String[] extraStreamMetadataFormatClassNames,
|
||||
@@ -99,7 +99,7 @@ public abstract class ReaderWriterProviderInfo extends ProviderInfo {
|
||||
this.writerClassName = writerClassName;
|
||||
this.writerSpiClassNames = writerSpiClassNames;
|
||||
this.supportsStandardStreamMetadata = supportsStandardStreamMetadata;
|
||||
this.nativeStreamMetadataFormatName = nativeStreameMetadataFormatName;
|
||||
this.nativeStreamMetadataFormatName = nativeStreamMetadataFormatName;
|
||||
this.nativeStreamMetadataFormatClassName = nativeStreamMetadataFormatClassName;
|
||||
this.extraStreamMetadataFormatNames = extraStreamMetadataFormatNames;
|
||||
this.extraStreamMetadataFormatClassNames = extraStreamMetadataFormatClassNames;
|
||||
|
||||
+259
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
* Copyright (c) 2021, 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:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
* * 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.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.stream;
|
||||
|
||||
import javax.imageio.stream.ImageInputStreamImpl;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import static com.twelvemonkeys.lang.Validate.notNull;
|
||||
import static java.lang.Math.max;
|
||||
|
||||
/**
|
||||
* A buffered replacement for {@link javax.imageio.stream.FileImageInputStream}
|
||||
* that provides greatly improved performance for shorter reads, like single
|
||||
* byte or bit reads.
|
||||
* As with {@code javax.imageio.stream.FileImageInputStream}, either
|
||||
* {@link File} or {@link RandomAccessFile} can be used as input.
|
||||
*
|
||||
* @see javax.imageio.stream.FileImageInputStream
|
||||
*/
|
||||
// TODO: Create a memory-mapped version?
|
||||
// Or not... From java.nio.channels.FileChannel.map:
|
||||
// For most operating systems, mapping a file into memory is more
|
||||
// expensive than reading or writing a few tens of kilobytes of data via
|
||||
// the usual {@link #read read} and {@link #write write} methods. From the
|
||||
// standpoint of performance it is generally only worth mapping relatively
|
||||
// large files into memory.
|
||||
public final class BufferedFileImageInputStream extends ImageInputStreamImpl {
|
||||
static final int DEFAULT_BUFFER_SIZE = 8192;
|
||||
|
||||
private byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
|
||||
private int bufferPos;
|
||||
private int bufferLimit;
|
||||
|
||||
private final ByteBuffer integralCache = ByteBuffer.allocate(8);
|
||||
private final byte[] integralCacheArray = integralCache.array();
|
||||
|
||||
private RandomAccessFile raf;
|
||||
|
||||
/**
|
||||
* Constructs a <code>BufferedFileImageInputStream</code> that will read from a given <code>File</code>.
|
||||
*
|
||||
* @param file a <code>File</code> to read from.
|
||||
* @throws IllegalArgumentException if <code>file</code> is <code>null</code>.
|
||||
* @throws FileNotFoundException if <code>file</code> is a directory or cannot be opened for reading
|
||||
* for any reason.
|
||||
*/
|
||||
public BufferedFileImageInputStream(final File file) throws FileNotFoundException {
|
||||
this(new RandomAccessFile(notNull(file, "file"), "r"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a <code>BufferedFileImageInputStream</code> that will read from a given <code>RandomAccessFile</code>.
|
||||
*
|
||||
* @param raf a <code>RandomAccessFile</code> to read from.
|
||||
* @throws IllegalArgumentException if <code>raf</code> is <code>null</code>.
|
||||
*/
|
||||
public BufferedFileImageInputStream(final RandomAccessFile raf) {
|
||||
this.raf = notNull(raf, "raf");
|
||||
}
|
||||
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
private boolean fillBuffer() throws IOException {
|
||||
int length = raf.read(buffer, 0, buffer.length);
|
||||
bufferPos = 0;
|
||||
bufferLimit = max(length, 0);
|
||||
|
||||
return bufferLimit > 0;
|
||||
}
|
||||
|
||||
private boolean bufferEmpty() {
|
||||
return bufferPos >= bufferLimit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setByteOrder(ByteOrder byteOrder) {
|
||||
super.setByteOrder(byteOrder);
|
||||
integralCache.order(byteOrder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
checkClosed();
|
||||
|
||||
if (bufferEmpty() && !fillBuffer()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
bitOffset = 0;
|
||||
streamPos++;
|
||||
|
||||
return buffer[bufferPos++] & 0xff;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(final byte[] bytes, final int offset, final int length) throws IOException {
|
||||
checkClosed();
|
||||
bitOffset = 0;
|
||||
|
||||
if (bufferEmpty()) {
|
||||
// Bypass buffer if buffer is empty for reads longer than buffer
|
||||
if (length >= buffer.length) {
|
||||
return readDirect(bytes, offset, length);
|
||||
}
|
||||
else if (!fillBuffer()) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int fromBuffer = readBuffered(bytes, offset, length);
|
||||
|
||||
if (length > fromBuffer) {
|
||||
// Due to known bugs in certain JDK-bundled ImageIO plugins expecting read to behave as readFully,
|
||||
// we'll read as much as possible from the buffer, and the rest directly after
|
||||
return fromBuffer + max(0, readDirect(bytes, offset + fromBuffer, length - fromBuffer));
|
||||
}
|
||||
|
||||
return fromBuffer;
|
||||
}
|
||||
|
||||
private int readDirect(final byte[] bytes, final int offset, final int length) throws IOException {
|
||||
// Invalidate the buffer, as its contents is no longer in sync with the stream's position.
|
||||
bufferLimit = 0;
|
||||
int read = raf.read(bytes, offset, length);
|
||||
|
||||
if (read > 0) {
|
||||
streamPos += read;
|
||||
}
|
||||
|
||||
return read;
|
||||
}
|
||||
|
||||
private int readBuffered(final byte[] bytes, final int offset, final int length) {
|
||||
// Read as much as possible from buffer
|
||||
int available = Math.min(bufferLimit - bufferPos, length);
|
||||
|
||||
if (available > 0) {
|
||||
System.arraycopy(buffer, bufferPos, bytes, offset, available);
|
||||
bufferPos += available;
|
||||
streamPos += available;
|
||||
}
|
||||
|
||||
return available;
|
||||
}
|
||||
|
||||
public long length() {
|
||||
// WTF?! This method is allowed to throw IOException in the interface...
|
||||
try {
|
||||
checkClosed();
|
||||
return raf.length();
|
||||
}
|
||||
catch (IOException ignore) {
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
super.close();
|
||||
|
||||
raf.close();
|
||||
|
||||
raf = null;
|
||||
buffer = null;
|
||||
}
|
||||
|
||||
// Need to override the readShort(), readInt() and readLong() methods,
|
||||
// because the implementations in ImageInputStreamImpl expects the
|
||||
// read(byte[], int, int) to always read the expected number of bytes,
|
||||
// causing uninitialized values, alignment issues and EOFExceptions at
|
||||
// random places...
|
||||
// Notes:
|
||||
// * readUnsignedXx() is covered by their signed counterparts
|
||||
// * readChar() is covered by readShort()
|
||||
// * readFloat() and readDouble() is covered by readInt() and readLong()
|
||||
// respectively.
|
||||
// * readLong() may be covered by two readInt()s, we'll override to be safe
|
||||
|
||||
@Override
|
||||
public short readShort() throws IOException {
|
||||
readFully(integralCacheArray, 0, 2);
|
||||
|
||||
return integralCache.getShort(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readInt() throws IOException {
|
||||
readFully(integralCacheArray, 0, 4);
|
||||
|
||||
return integralCache.getInt(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long readLong() throws IOException {
|
||||
readFully(integralCacheArray, 0, 8);
|
||||
|
||||
return integralCache.getLong(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seek(long position) throws IOException {
|
||||
checkClosed();
|
||||
|
||||
if (position < flushedPos) {
|
||||
throw new IndexOutOfBoundsException("position < flushedPos!");
|
||||
}
|
||||
|
||||
bitOffset = 0;
|
||||
|
||||
if (streamPos == position) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Optimized to not invalidate buffer if new position is within current buffer
|
||||
long newBufferPos = bufferPos + position - streamPos;
|
||||
if (newBufferPos >= 0 && newBufferPos < bufferLimit) {
|
||||
bufferPos = (int) newBufferPos;
|
||||
}
|
||||
else {
|
||||
// Will invalidate buffer
|
||||
bufferLimit = 0;
|
||||
raf.seek(position);
|
||||
}
|
||||
|
||||
streamPos = position;
|
||||
}
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (c) 2021, 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:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
* * 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.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.stream;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ProviderInfo;
|
||||
|
||||
import javax.imageio.spi.ImageInputStreamSpi;
|
||||
import javax.imageio.spi.ServiceRegistry;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* BufferedFileImageInputStreamSpi
|
||||
* Experimental
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: BufferedFileImageInputStreamSpi.java,v 1.0 May 15, 2008 2:14:59 PM haraldk Exp$
|
||||
*/
|
||||
public class BufferedFileImageInputStreamSpi extends ImageInputStreamSpi {
|
||||
public BufferedFileImageInputStreamSpi() {
|
||||
this(new StreamProviderInfo());
|
||||
}
|
||||
|
||||
private BufferedFileImageInputStreamSpi(ProviderInfo providerInfo) {
|
||||
super(providerInfo.getVendorName(), providerInfo.getVersion(), File.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRegistration(final ServiceRegistry registry, final Class<?> category) {
|
||||
Iterator<ImageInputStreamSpi> providers = registry.getServiceProviders(ImageInputStreamSpi.class, new FileInputFilter(), true);
|
||||
|
||||
while (providers.hasNext()) {
|
||||
ImageInputStreamSpi provider = providers.next();
|
||||
if (provider != this) {
|
||||
registry.setOrdering(ImageInputStreamSpi.class, this, provider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ImageInputStream createInputStreamInstance(final Object input, final boolean pUseCache, final File pCacheDir) {
|
||||
if (input instanceof File) {
|
||||
try {
|
||||
return new BufferedFileImageInputStream((File) input);
|
||||
}
|
||||
catch (FileNotFoundException e) {
|
||||
// For consistency with the JRE bundled SPIs, we'll return null here,
|
||||
// even though the spec does not say that's allowed.
|
||||
// The problem is that the SPIs can only declare that they support an input type like a File,
|
||||
// instead they should be allowed to inspect the instance, to see that the file does exist...
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Expected input of type File: " + input);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUseCacheFile() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getDescription(final Locale pLocale) {
|
||||
return "Service provider that instantiates an ImageInputStream from a File";
|
||||
}
|
||||
|
||||
private static class FileInputFilter implements ServiceRegistry.Filter {
|
||||
@Override
|
||||
public boolean filter(final Object provider) {
|
||||
return ((ImageInputStreamSpi) provider).getInputClass() == File.class;
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
-3
@@ -43,15 +43,19 @@ import static com.twelvemonkeys.lang.Validate.notNull;
|
||||
* A buffered {@code ImageInputStream}.
|
||||
* Experimental - seems to be effective for {@link javax.imageio.stream.FileImageInputStream}
|
||||
* and {@link javax.imageio.stream.FileCacheImageInputStream} when doing a lot of single-byte reads
|
||||
* (or short byte-array reads) on OS X at least.
|
||||
* (or short byte-array reads).
|
||||
* Code that uses the {@code readFully} methods are not affected by the issue.
|
||||
* <p>
|
||||
* NOTE: Invoking {@code close()} will <em>NOT</em> close the wrapped stream.
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: BufferedFileImageInputStream.java,v 1.0 May 15, 2008 4:36:49 PM haraldk Exp$
|
||||
*
|
||||
* @deprecated Use {@link BufferedFileImageInputStream} instead.
|
||||
*/
|
||||
// TODO: Create a provider for this (wrapping the FileIIS and FileCacheIIS classes), and disable the Sun built-in spis?
|
||||
// TODO: Test on other platforms, might be just an OS X issue
|
||||
@Deprecated
|
||||
public final class BufferedImageInputStream extends ImageInputStreamImpl implements ImageInputStream {
|
||||
static final int DEFAULT_BUFFER_SIZE = 8192;
|
||||
|
||||
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (c) 2021, 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:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
* * 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.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.stream;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ProviderInfo;
|
||||
|
||||
import javax.imageio.spi.ImageInputStreamSpi;
|
||||
import javax.imageio.spi.ServiceRegistry;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.File;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* BufferedRAFImageInputStreamSpi
|
||||
* Experimental
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: BufferedRAFImageInputStreamSpi.java,v 1.0 May 15, 2008 2:14:59 PM haraldk Exp$
|
||||
*/
|
||||
public class BufferedRAFImageInputStreamSpi extends ImageInputStreamSpi {
|
||||
public BufferedRAFImageInputStreamSpi() {
|
||||
this(new StreamProviderInfo());
|
||||
}
|
||||
|
||||
private BufferedRAFImageInputStreamSpi(ProviderInfo providerInfo) {
|
||||
super(providerInfo.getVendorName(), providerInfo.getVersion(), RandomAccessFile.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRegistration(final ServiceRegistry registry, final Class<?> category) {
|
||||
Iterator<ImageInputStreamSpi> providers = registry.getServiceProviders(ImageInputStreamSpi.class, new RAFInputFilter(), true);
|
||||
|
||||
while (providers.hasNext()) {
|
||||
ImageInputStreamSpi provider = providers.next();
|
||||
if (provider != this) {
|
||||
registry.setOrdering(ImageInputStreamSpi.class, this, provider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ImageInputStream createInputStreamInstance(final Object input, final boolean pUseCache, final File pCacheDir) {
|
||||
if (input instanceof RandomAccessFile) {
|
||||
return new BufferedFileImageInputStream((RandomAccessFile) input);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Expected input of type RandomAccessFile: " + input);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUseCacheFile() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getDescription(final Locale pLocale) {
|
||||
return "Service provider that instantiates an ImageInputStream from a RandomAccessFile";
|
||||
}
|
||||
|
||||
private static class RAFInputFilter implements ServiceRegistry.Filter {
|
||||
@Override
|
||||
public boolean filter(final Object provider) {
|
||||
return ((ImageInputStreamSpi) provider).getInputClass() == RandomAccessFile.class;
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
-9
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* Copyright (c) 2021, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -34,7 +34,6 @@ import com.twelvemonkeys.imageio.spi.ProviderInfo;
|
||||
|
||||
import javax.imageio.spi.ImageInputStreamSpi;
|
||||
import javax.imageio.stream.FileCacheImageInputStream;
|
||||
import javax.imageio.stream.FileImageInputStream;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import javax.imageio.stream.MemoryCacheImageInputStream;
|
||||
import java.io.File;
|
||||
@@ -72,7 +71,7 @@ public class URLImageInputStreamSpi extends ImageInputStreamSpi {
|
||||
// Special case for file protocol, a lot faster than FileCacheImageInputStream
|
||||
if ("file".equals(url.getProtocol())) {
|
||||
try {
|
||||
return new BufferedImageInputStream(new FileImageInputStream(new File(url.toURI())));
|
||||
return new BufferedFileImageInputStream(new File(url.toURI()));
|
||||
}
|
||||
catch (URISyntaxException ignore) {
|
||||
// This should never happen, but if it does, we'll fall back to using the stream
|
||||
@@ -81,29 +80,29 @@ public class URLImageInputStreamSpi extends ImageInputStreamSpi {
|
||||
}
|
||||
|
||||
// Otherwise revert to cached
|
||||
final InputStream stream = url.openStream();
|
||||
final InputStream urlStream = url.openStream();
|
||||
if (pUseCache) {
|
||||
return new BufferedImageInputStream(new FileCacheImageInputStream(stream, pCacheDir) {
|
||||
return new FileCacheImageInputStream(urlStream, pCacheDir) {
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
try {
|
||||
super.close();
|
||||
}
|
||||
finally {
|
||||
stream.close(); // NOTE: If this line throws IOE, it will shadow the original..
|
||||
urlStream.close(); // NOTE: If this line throws IOE, it will shadow the original..
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
else {
|
||||
return new MemoryCacheImageInputStream(stream) {
|
||||
return new MemoryCacheImageInputStream(urlStream) {
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
try {
|
||||
super.close();
|
||||
}
|
||||
finally {
|
||||
stream.close(); // NOTE: If this line throws IOE, it will shadow the original..
|
||||
urlStream.close(); // NOTE: If this line throws IOE, it will shadow the original..
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -158,19 +158,18 @@ public final class IIOUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* THIS METHOD WILL ME MOVED/RENAMED, DO NOT USE.
|
||||
* THIS METHOD WILL BE MOVED/RENAMED, DO NOT USE.
|
||||
*
|
||||
* @param registry the registry to unregister from.
|
||||
* @param provider the provider to unregister.
|
||||
* @param category the category to unregister from.
|
||||
*/
|
||||
public static <T> void deregisterProvider(final ServiceRegistry registry, final IIOServiceProvider provider, final Class<T> category) {
|
||||
// http://www.ibm.com/developerworks/java/library/j-jtp04298.html
|
||||
registry.deregisterServiceProvider(category.cast(provider), category);
|
||||
}
|
||||
|
||||
/**
|
||||
* THIS METHOD WILL ME MOVED/RENAMED, DO NOT USE.
|
||||
* THIS METHOD WILL BE MOVED/RENAMED, DO NOT USE.
|
||||
*
|
||||
* @param registry the registry to lookup from.
|
||||
* @param providerClassName name of the provider class.
|
||||
@@ -223,7 +222,12 @@ public final class IIOUtil {
|
||||
public static void subsampleRow(byte[] srcRow, int srcPos, int srcWidth,
|
||||
byte[] destRow, int destPos,
|
||||
int samplesPerPixel, int bitsPerSample, int samplePeriod) {
|
||||
Validate.isTrue(samplePeriod > 1, "samplePeriod must be > 1"); // Period == 1 could be a no-op...
|
||||
// Period == 1 is a no-op...
|
||||
if (samplePeriod == 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
Validate.isTrue(samplePeriod > 1, "samplePeriod must be > 1");
|
||||
Validate.isTrue(bitsPerSample > 0 && bitsPerSample <= 8 && (bitsPerSample == 1 || bitsPerSample % 2 == 0),
|
||||
"bitsPerSample must be > 0 and <= 8 and a power of 2");
|
||||
Validate.isTrue(samplesPerPixel > 0, "samplesPerPixel must be > 0");
|
||||
@@ -261,7 +265,12 @@ public final class IIOUtil {
|
||||
public static void subsampleRow(short[] srcRow, int srcPos, int srcWidth,
|
||||
short[] destRow, int destPos,
|
||||
int samplesPerPixel, int bitsPerSample, int samplePeriod) {
|
||||
Validate.isTrue(samplePeriod > 1, "samplePeriod must be > 1"); // Period == 1 could be a no-op...
|
||||
// Period == 1 is a no-op...
|
||||
if (samplePeriod == 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
Validate.isTrue(samplePeriod > 1, "samplePeriod must be > 1");
|
||||
Validate.isTrue(bitsPerSample > 0 && bitsPerSample <= 16 && (bitsPerSample == 1 || bitsPerSample % 2 == 0),
|
||||
"bitsPerSample must be > 0 and <= 16 and a power of 2");
|
||||
Validate.isTrue(samplesPerPixel > 0, "samplesPerPixel must be > 0");
|
||||
@@ -278,7 +287,12 @@ public final class IIOUtil {
|
||||
public static void subsampleRow(int[] srcRow, int srcPos, int srcWidth,
|
||||
int[] destRow, int destPos,
|
||||
int samplesPerPixel, int bitsPerSample, int samplePeriod) {
|
||||
Validate.isTrue(samplePeriod > 1, "samplePeriod must be > 1"); // Period == 1 could be a no-op...
|
||||
// Period == 1 is a no-op...
|
||||
if (samplePeriod == 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
Validate.isTrue(samplePeriod > 1, "samplePeriod must be > 1");
|
||||
Validate.isTrue(bitsPerSample > 0 && bitsPerSample <= 32 && (bitsPerSample == 1 || bitsPerSample % 2 == 0),
|
||||
"bitsPerSample must be > 0 and <= 32 and a power of 2");
|
||||
Validate.isTrue(samplesPerPixel > 0, "samplesPerPixel must be > 0");
|
||||
|
||||
+40
-18
@@ -30,15 +30,22 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.util;
|
||||
|
||||
import com.twelvemonkeys.imageio.color.DiscreteAlphaIndexColorModel;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.*;
|
||||
|
||||
import static com.twelvemonkeys.lang.Validate.isTrue;
|
||||
import static com.twelvemonkeys.lang.Validate.notNull;
|
||||
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ColorModel;
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.awt.image.DirectColorModel;
|
||||
import java.awt.image.IndexColorModel;
|
||||
import java.awt.image.MultiPixelPackedSampleModel;
|
||||
import java.awt.image.SampleModel;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
|
||||
import com.twelvemonkeys.imageio.color.DiscreteAlphaIndexColorModel;
|
||||
|
||||
/**
|
||||
* Factory class for creating {@code ImageTypeSpecifier}s.
|
||||
* Fixes some subtle bugs in {@code ImageTypeSpecifier}'s factory methods, but
|
||||
@@ -169,21 +176,36 @@ public final class ImageTypeSpecifiers {
|
||||
|
||||
int numEntries = 1 << bits;
|
||||
|
||||
byte[] r = new byte[numEntries];
|
||||
byte[] g = new byte[numEntries];
|
||||
byte[] b = new byte[numEntries];
|
||||
ColorModel colorModel;
|
||||
|
||||
// Scale array values according to color profile..
|
||||
for (int i = 0; i < numEntries; i++) {
|
||||
float[] gray = new float[]{i / (float) (numEntries - 1)};
|
||||
float[] rgb = colorSpace.toRGB(gray);
|
||||
if (ColorSpace.getInstance(ColorSpace.CS_GRAY).equals(colorSpace)) {
|
||||
// For default gray, use linear response
|
||||
byte[] gray = new byte[numEntries];
|
||||
|
||||
r[i] = (byte) (rgb[0] * 255);
|
||||
g[i] = (byte) (rgb[1] * 255);
|
||||
b[i] = (byte) (rgb[2] * 255);
|
||||
for (int i = 0; i < numEntries; i++) {
|
||||
gray[i] = (byte) ((i * 255) / (numEntries - 1));
|
||||
}
|
||||
|
||||
colorModel = new IndexColorModel(bits, numEntries, gray, gray, gray);
|
||||
}
|
||||
else {
|
||||
byte[] r = new byte[numEntries];
|
||||
byte[] g = new byte[numEntries];
|
||||
byte[] b = new byte[numEntries];
|
||||
|
||||
// Scale array values according to color profile..
|
||||
for (int i = 0; i < numEntries; i++) {
|
||||
float[] gray = new float[] { i / (float) (numEntries - 1) };
|
||||
float[] rgb = colorSpace.toRGB(gray);
|
||||
|
||||
r[i] = (byte) Math.round(rgb[0] * 255);
|
||||
g[i] = (byte) Math.round(rgb[1] * 255);
|
||||
b[i] = (byte) Math.round(rgb[2] * 255);
|
||||
}
|
||||
|
||||
colorModel = new IndexColorModel(bits, numEntries, r, g, b);
|
||||
}
|
||||
|
||||
ColorModel colorModel = new IndexColorModel(bits, numEntries, r, g, b);
|
||||
SampleModel sampleModel = new MultiPixelPackedSampleModel(dataType, 1, 1, bits);
|
||||
|
||||
return new ImageTypeSpecifier(colorModel, sampleModel);
|
||||
@@ -201,7 +223,7 @@ public final class ImageTypeSpecifiers {
|
||||
}
|
||||
|
||||
public static ImageTypeSpecifier createFromIndexColorModel(final IndexColorModel pColorModel) {
|
||||
return IndexedImageTypeSpecifier.createFromIndexColorModel(pColorModel);
|
||||
return new IndexedImageTypeSpecifier(pColorModel);
|
||||
}
|
||||
|
||||
public static ImageTypeSpecifier createDiscreteAlphaIndexedFromIndexColorModel(final IndexColorModel pColorModel) {
|
||||
|
||||
+24
-27
@@ -30,14 +30,14 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.util;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.IndexColorModel;
|
||||
import java.awt.image.WritableRaster;
|
||||
import java.util.Hashtable;
|
||||
|
||||
import static com.twelvemonkeys.lang.Validate.notNull;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ColorModel;
|
||||
import java.awt.image.WritableRaster;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
|
||||
/**
|
||||
* IndexedImageTypeSpecifier
|
||||
*
|
||||
@@ -45,27 +45,24 @@ import static com.twelvemonkeys.lang.Validate.notNull;
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: IndexedImageTypeSpecifier.java,v 1.0 May 19, 2008 11:04:28 AM haraldk Exp$
|
||||
*/
|
||||
final class IndexedImageTypeSpecifier {
|
||||
private IndexedImageTypeSpecifier() {}
|
||||
final class IndexedImageTypeSpecifier extends ImageTypeSpecifier {
|
||||
IndexedImageTypeSpecifier(final ColorModel colorModel) {
|
||||
// For some reason, we need a sample model, even though we won't use it
|
||||
super(notNull(colorModel, "colorModel"), colorModel.createCompatibleSampleModel(1, 1));
|
||||
}
|
||||
|
||||
static ImageTypeSpecifier createFromIndexColorModel(final IndexColorModel pColorModel) {
|
||||
// For some reason, we need a sample model
|
||||
return new ImageTypeSpecifier(notNull(pColorModel, "colorModel"), pColorModel.createCompatibleSampleModel(1, 1)) {
|
||||
|
||||
@Override
|
||||
public final BufferedImage createBufferedImage(final int pWidth, final int pHeight) {
|
||||
try {
|
||||
// This is a fix for the super-method, that first creates a sample model, and then
|
||||
// creates a raster from it, using Raster.createWritableRaster. The problem with
|
||||
// that approach, is that it always creates a TYPE_CUSTOM BufferedImage for indexed images.
|
||||
WritableRaster raster = colorModel.createCompatibleWritableRaster(pWidth, pHeight);
|
||||
return new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), new Hashtable());
|
||||
}
|
||||
catch (NegativeArraySizeException e) {
|
||||
// Exception most likely thrown from a DataBuffer constructor
|
||||
throw new IllegalArgumentException("Array size > Integer.MAX_VALUE!");
|
||||
}
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public final BufferedImage createBufferedImage(final int pWidth, final int pHeight) {
|
||||
try {
|
||||
// This is a fix for the super-method, that first creates a sample model, and then
|
||||
// creates a raster from it, using Raster.createWritableRaster. The problem with
|
||||
// that approach, is that it always creates a TYPE_CUSTOM BufferedImage for indexed images.
|
||||
WritableRaster raster = colorModel.createCompatibleWritableRaster(pWidth, pHeight);
|
||||
return new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);
|
||||
}
|
||||
catch (NegativeArraySizeException e) {
|
||||
// Exception most likely thrown from a DataBuffer constructor
|
||||
throw new IllegalArgumentException("Array size > Integer.MAX_VALUE!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright (c) 2021, 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:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
* * 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.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.util;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.*;
|
||||
|
||||
import static com.twelvemonkeys.lang.Validate.notNull;
|
||||
|
||||
/**
|
||||
* A class containing various raster utility methods.
|
||||
*/
|
||||
public final class RasterUtils {
|
||||
|
||||
private RasterUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a raster with {@code DataBuffer.TYPE_BYTE} transfer type.
|
||||
* Works for any raster from a {@code BufferedImage.TYPE_INT_*} image
|
||||
*
|
||||
* @param raster a {@code Raster} with either transfer type {@code DataBuffer.TYPE_BYTE}
|
||||
* or {@code DataBuffer.TYPE_INT} with `SinglePixelPackedSampleModel`, not {@code null}.
|
||||
* @return a raster with {@code DataBuffer.TYPE_BYTE} transfer type.
|
||||
* @throws IllegalArgumentException if {@code raster} does not have transfer type {@code DataBuffer.TYPE_BYTE}
|
||||
* or {@code DataBuffer.TYPE_INT} with `SinglePixelPackedSampleModel`
|
||||
* @throws NullPointerException if {@code raster} is {@code null}.
|
||||
*/
|
||||
public static Raster asByteRaster(final Raster raster) {
|
||||
return asByteRaster0(raster);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a writable raster with {@code DataBuffer.TYPE_BYTE} transfer type.
|
||||
* Works for any raster from a {@code BufferedImage.TYPE_INT_*} image.
|
||||
*
|
||||
* @param raster a {@code WritableRaster} with either transfer type {@code DataBuffer.TYPE_BYTE}
|
||||
* or {@code DataBuffer.TYPE_INT} with `SinglePixelPackedSampleModel`, not {@code null}.
|
||||
* @return a writable raster with {@code DataBuffer.TYPE_BYTE} transfer type.
|
||||
* @throws IllegalArgumentException if {@code raster} does not have transfer type {@code DataBuffer.TYPE_BYTE}
|
||||
* or {@code DataBuffer.TYPE_INT} with `SinglePixelPackedSampleModel`
|
||||
* @throws NullPointerException if {@code raster} is {@code null}.
|
||||
*/
|
||||
public static WritableRaster asByteRaster(final WritableRaster raster) {
|
||||
return (WritableRaster) asByteRaster0(raster);
|
||||
}
|
||||
|
||||
private static Raster asByteRaster0(final Raster raster) {
|
||||
switch (raster.getTransferType()) {
|
||||
case DataBuffer.TYPE_BYTE:
|
||||
return raster;
|
||||
case DataBuffer.TYPE_INT:
|
||||
SampleModel sampleModel = raster.getSampleModel();
|
||||
|
||||
if (!(sampleModel instanceof SinglePixelPackedSampleModel)) {
|
||||
throw new IllegalArgumentException(String.format("Requires SinglePixelPackedSampleModel, %s not supported", sampleModel.getClass().getSimpleName()));
|
||||
}
|
||||
|
||||
final int bands = 4;
|
||||
final DataBufferInt buffer = (DataBufferInt) raster.getDataBuffer();
|
||||
|
||||
int w = raster.getWidth();
|
||||
int h = raster.getHeight();
|
||||
int size = buffer.getSize();
|
||||
|
||||
return new WritableRaster(
|
||||
new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE, w, h, bands, w * bands, createBandOffsets((SinglePixelPackedSampleModel) sampleModel)),
|
||||
new DataBuffer(DataBuffer.TYPE_BYTE, size * bands) {
|
||||
final int[] MASKS = {
|
||||
0xffffff00,
|
||||
0xffff00ff,
|
||||
0xff00ffff,
|
||||
0x00ffffff,
|
||||
};
|
||||
|
||||
@Override
|
||||
public int getElem(int bank, int i) {
|
||||
int index = i / bands;
|
||||
int shift = (i % bands) * 8;
|
||||
|
||||
return (buffer.getElem(index) >>> shift) & 0xff;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setElem(int bank, int i, int val) {
|
||||
int index = i / bands;
|
||||
int element = i % bands;
|
||||
int shift = element * 8;
|
||||
|
||||
int value = (buffer.getElem(index) & MASKS[element]) | ((val & 0xff) << shift);
|
||||
buffer.setElem(index, value);
|
||||
}
|
||||
}, new Point()) {
|
||||
};
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException(String.format("Raster type %d not supported", raster.getTransferType()));
|
||||
}
|
||||
}
|
||||
|
||||
private static int[] createBandOffsets(final SinglePixelPackedSampleModel sampleModel) {
|
||||
notNull(sampleModel, "sampleModel");
|
||||
|
||||
int[] masks = sampleModel.getBitMasks();
|
||||
int[] offs = new int[masks.length];
|
||||
|
||||
for (int i = 0; i < masks.length; i++) {
|
||||
int mask = masks[i];
|
||||
int off = 0;
|
||||
|
||||
// TODO: FixMe! This only works for standard 8 bit masks (0xFF)
|
||||
if (mask != 0) {
|
||||
while ((mask & 0xFF) == 0) {
|
||||
mask >>>= 8;
|
||||
off++;
|
||||
}
|
||||
}
|
||||
|
||||
offs[i] = off;
|
||||
}
|
||||
|
||||
return offs;
|
||||
}
|
||||
}
|
||||
+18
-9
@@ -30,15 +30,16 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.util;
|
||||
|
||||
import com.twelvemonkeys.imageio.color.UInt32ColorModel;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.BandedSampleModel;
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.awt.image.PixelInterleavedSampleModel;
|
||||
import java.awt.image.SampleModel;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
|
||||
import com.twelvemonkeys.imageio.color.UInt32ColorModel;
|
||||
|
||||
/**
|
||||
* ImageTypeSpecifier for interleaved 32 bit unsigned integral samples.
|
||||
*
|
||||
@@ -47,11 +48,13 @@ import java.awt.image.SampleModel;
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: UInt32ImageTypeSpecifier.java,v 1.0 24.01.11 17.51 haraldk Exp$
|
||||
*/
|
||||
final class UInt32ImageTypeSpecifier {
|
||||
private UInt32ImageTypeSpecifier() {}
|
||||
final class UInt32ImageTypeSpecifier extends ImageTypeSpecifier {
|
||||
private UInt32ImageTypeSpecifier(final ColorSpace cs, final boolean hasAlpha, final boolean isAlphaPremultiplied, final SampleModel sampleModel) {
|
||||
super(new UInt32ColorModel(cs, hasAlpha, isAlphaPremultiplied), sampleModel);
|
||||
}
|
||||
|
||||
static ImageTypeSpecifier createInterleaved(final ColorSpace cs, final int[] bandOffsets, final boolean hasAlpha, final boolean isAlphaPremultiplied) {
|
||||
return create(
|
||||
return new UInt32ImageTypeSpecifier(
|
||||
cs, hasAlpha, isAlphaPremultiplied,
|
||||
new PixelInterleavedSampleModel(
|
||||
DataBuffer.TYPE_INT, 1, 1,
|
||||
@@ -63,7 +66,7 @@ final class UInt32ImageTypeSpecifier {
|
||||
}
|
||||
|
||||
static ImageTypeSpecifier createBanded(final ColorSpace cs, final int[] bandIndices, final int[] bandOffsets, final boolean hasAlpha, final boolean isAlphaPremultiplied) {
|
||||
return create(
|
||||
return new UInt32ImageTypeSpecifier(
|
||||
cs, hasAlpha, isAlphaPremultiplied,
|
||||
new BandedSampleModel(
|
||||
DataBuffer.TYPE_INT, 1, 1, 1,
|
||||
@@ -72,7 +75,13 @@ final class UInt32ImageTypeSpecifier {
|
||||
);
|
||||
}
|
||||
|
||||
private static ImageTypeSpecifier create(final ColorSpace cs, final boolean hasAlpha, final boolean isAlphaPremultiplied, final SampleModel sampleModel) {
|
||||
return new ImageTypeSpecifier(new UInt32ColorModel(cs, hasAlpha, isAlphaPremultiplied), sampleModel);
|
||||
@Override
|
||||
public boolean equals(final Object other) {
|
||||
if (!(other instanceof UInt32ImageTypeSpecifier)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UInt32ImageTypeSpecifier that = (UInt32ImageTypeSpecifier) other;
|
||||
return colorModel.equals(that.colorModel) && sampleModel.equals(that.sampleModel);
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
com.twelvemonkeys.imageio.stream.BufferedFileImageInputStreamSpi
|
||||
com.twelvemonkeys.imageio.stream.BufferedRAFImageInputStreamSpi
|
||||
+85
-1
@@ -35,6 +35,7 @@ import org.junit.Test;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.color.ICC_ColorSpace;
|
||||
import java.awt.color.ICC_Profile;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
@@ -184,9 +185,27 @@ public class ColorSpacesTest {
|
||||
ColorSpaces.isCS_sRGB(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsCS_GRAYTrue() {
|
||||
assertTrue(ColorSpaces.isCS_GRAY(ICC_Profile.getInstance(ColorSpace.CS_GRAY)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsCS_GRAYFalse() {
|
||||
assertFalse(ColorSpaces.isCS_GRAY(ICC_Profile.getInstance(ColorSpace.CS_sRGB)));
|
||||
assertFalse(ColorSpaces.isCS_GRAY(ICC_Profile.getInstance(ColorSpace.CS_LINEAR_RGB)));
|
||||
assertFalse(ColorSpaces.isCS_GRAY(ICC_Profile.getInstance(ColorSpace.CS_CIEXYZ)));
|
||||
assertFalse(ColorSpaces.isCS_GRAY(ICC_Profile.getInstance(ColorSpace.CS_PYCC)));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testIsCS_GRAYNull() {
|
||||
ColorSpaces.isCS_GRAY(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEqualHeadersDifferentProfile() throws IOException {
|
||||
// These profiles are extracted from various JPEGs, and have the exact same profile header...
|
||||
// These profiles are extracted from various JPEGs, and have the exact same profile header (but are different)...
|
||||
ICC_Profile profile1 = ICC_Profile.getInstance(getClass().getResourceAsStream("/profiles/adobe_rgb_1998.icc"));
|
||||
ICC_Profile profile2 = ICC_Profile.getInstance(getClass().getResourceAsStream("/profiles/color_match_rgb.icc"));
|
||||
|
||||
@@ -197,4 +216,69 @@ public class ColorSpacesTest {
|
||||
|
||||
assertNotSame(cs1, cs2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadProfileBytesSame() throws IOException {
|
||||
ICC_Profile profile = ICC_Profile.getInstance(getClass().getResourceAsStream("/profiles/adobe_rgb_1998.icc"));
|
||||
ICC_Profile profile1 = ColorSpaces.createProfile(profile.getData());
|
||||
ICC_Profile profile2 = ColorSpaces.createProfile(profile.getData());
|
||||
|
||||
assertEquals(profile1, profile2);
|
||||
assertSame(profile1, profile2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadProfileInputStreamSame() throws IOException {
|
||||
ICC_Profile profile1 = ColorSpaces.readProfile(getClass().getResourceAsStream("/profiles/adobe_rgb_1998.icc"));
|
||||
ICC_Profile profile2 = ColorSpaces.readProfile(getClass().getResourceAsStream("/profiles/adobe_rgb_1998.icc"));
|
||||
|
||||
assertEquals(profile1, profile2);
|
||||
assertSame(profile1, profile2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadProfileDifferent() throws IOException {
|
||||
// These profiles are extracted from various JPEGs, and have the exact same profile header (but are different profiles)...
|
||||
ICC_Profile profile1 = ColorSpaces.readProfile(getClass().getResourceAsStream("/profiles/adobe_rgb_1998.icc"));
|
||||
ICC_Profile profile2 = ColorSpaces.readProfile(getClass().getResourceAsStream("/profiles/color_match_rgb.icc"));
|
||||
|
||||
assertNotSame(profile1, profile2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadProfileBytesSameAsCached() throws IOException {
|
||||
ICC_Profile profile = ICC_Profile.getInstance(getClass().getResourceAsStream("/profiles/adobe_rgb_1998.icc"));
|
||||
ICC_ColorSpace cs1 = ColorSpaces.createColorSpace(profile);
|
||||
ICC_Profile profile2 = ColorSpaces.createProfile(profile.getData());
|
||||
|
||||
assertEquals(cs1.getProfile(), profile2);
|
||||
assertSame(cs1.getProfile(), profile2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadProfileInputStreamSameAsCached() throws IOException {
|
||||
ICC_ColorSpace cs1 = ColorSpaces.createColorSpace(ICC_Profile.getInstance(getClass().getResourceAsStream("/profiles/adobe_rgb_1998.icc")));
|
||||
ICC_Profile profile2 = ColorSpaces.readProfile(getClass().getResourceAsStream("/profiles/adobe_rgb_1998.icc"));
|
||||
|
||||
assertEquals(cs1.getProfile(), profile2);
|
||||
assertSame(cs1.getProfile(), profile2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadProfileBytesSameAsInternal() {
|
||||
ICC_Profile profile1 = ICC_Profile.getInstance(ColorSpace.CS_sRGB);
|
||||
ICC_Profile profile2 = ColorSpaces.createProfile(ICC_Profile.getInstance(ColorSpace.CS_sRGB).getData());
|
||||
|
||||
assertEquals(profile1, profile2);
|
||||
assertSame(profile1, profile2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadProfileInputStreamSameAsInternal() throws IOException {
|
||||
ICC_Profile profile1 = ICC_Profile.getInstance(ColorSpace.CS_sRGB);
|
||||
ICC_Profile profile2 = ColorSpaces.readProfile(new ByteArrayInputStream(ICC_Profile.getInstance(ColorSpace.CS_sRGB).getData()));
|
||||
|
||||
assertEquals(profile1, profile2);
|
||||
assertSame(profile1, profile2);
|
||||
}
|
||||
}
|
||||
|
||||
-2
@@ -39,8 +39,6 @@ import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
public class KCMSSanitizerStrategyTest {
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.twelvemonkeys.imageio.stream;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.spi.ImageInputStreamSpi;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assume.assumeFalse;
|
||||
|
||||
public class BufferedFileImageInputStreamSpiTest extends ImageInputStreamSpiTest<File> {
|
||||
@Override
|
||||
protected ImageInputStreamSpi createProvider() {
|
||||
return new BufferedFileImageInputStreamSpi();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected File createInput() throws IOException {
|
||||
return File.createTempFile("test-", ".tst");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReturnNullWhenFileDoesNotExist() throws IOException {
|
||||
// This is really stupid behavior, but it is consistent with the JRE bundled SPIs.
|
||||
File input = new File("a-file-that-should-not-exist-ever.fnf");
|
||||
assumeFalse("File should not exist: " + input.getPath(), input.exists());
|
||||
assertNull(provider.createInputStreamInstance(input));
|
||||
}
|
||||
}
|
||||
+429
@@ -0,0 +1,429 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
* * 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.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.stream;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.function.ThrowingRunnable;
|
||||
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Random;
|
||||
|
||||
import static com.twelvemonkeys.imageio.stream.BufferedImageInputStreamTest.rangeEquals;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* BufferedFileImageInputStreamTestCase
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: BufferedFileImageInputStreamTestCase.java,v 1.0 Apr 21, 2009 10:58:48 AM haraldk Exp$
|
||||
*/
|
||||
public class BufferedFileImageInputStreamTest {
|
||||
private final Random random = new Random(170984354357234566L);
|
||||
|
||||
private File randomDataToFile(byte[] data) throws IOException {
|
||||
random.nextBytes(data);
|
||||
|
||||
File file = File.createTempFile("read", ".tmp");
|
||||
Files.write(file.toPath(), data);
|
||||
return file;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate() throws IOException {
|
||||
try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(File.createTempFile("empty", ".tmp"))) {
|
||||
assertEquals("Data length should be same as stream length", 0, stream.length());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateNullFile() throws IOException {
|
||||
try {
|
||||
new BufferedFileImageInputStream((File) null);
|
||||
fail("Expected IllegalArgumentException");
|
||||
}
|
||||
catch (IllegalArgumentException expected) {
|
||||
assertNotNull("Null exception message", expected.getMessage());
|
||||
String message = expected.getMessage().toLowerCase();
|
||||
assertTrue("Exception message does not contain parameter name", message.contains("file"));
|
||||
assertTrue("Exception message does not contain null", message.contains("null"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateNullRAF() {
|
||||
try {
|
||||
new BufferedFileImageInputStream((RandomAccessFile) null);
|
||||
fail("Expected IllegalArgumentException");
|
||||
}
|
||||
catch (IllegalArgumentException expected) {
|
||||
assertNotNull("Null exception message", expected.getMessage());
|
||||
String message = expected.getMessage().toLowerCase();
|
||||
assertTrue("Exception message does not contain parameter name", message.contains("raf"));
|
||||
assertTrue("Exception message does not contain null", message.contains("null"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRead() throws IOException {
|
||||
byte[] data = new byte[1024 * 1024];
|
||||
File file = randomDataToFile(data);
|
||||
|
||||
try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file)) {
|
||||
assertEquals("File length should be same as stream length", file.length(), stream.length());
|
||||
|
||||
for (byte value : data) {
|
||||
assertEquals("Wrong data read", value & 0xff, stream.read());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadArray() throws IOException {
|
||||
byte[] data = new byte[1024 * 1024];
|
||||
File file = randomDataToFile(data);
|
||||
|
||||
try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file)) {
|
||||
assertEquals("File length should be same as stream length", file.length(), stream.length());
|
||||
|
||||
byte[] result = new byte[1024];
|
||||
|
||||
for (int i = 0; i < data.length / result.length; i++) {
|
||||
stream.readFully(result);
|
||||
assertTrue("Wrong data read: " + i, rangeEquals(data, i * result.length, result, 0, result.length));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadSkip() throws IOException {
|
||||
byte[] data = new byte[1024 * 14];
|
||||
File file = randomDataToFile(data);
|
||||
|
||||
try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file)) {
|
||||
assertEquals("File length should be same as stream length", file.length(), stream.length());
|
||||
|
||||
byte[] result = new byte[7];
|
||||
|
||||
for (int i = 0; i < data.length / result.length; i += 2) {
|
||||
stream.readFully(result);
|
||||
stream.skipBytes(result.length);
|
||||
assertTrue("Wrong data read: " + i, rangeEquals(data, i * result.length, result, 0, result.length));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadSeek() throws IOException {
|
||||
byte[] data = new byte[1024 * 18];
|
||||
File file = randomDataToFile(data);
|
||||
|
||||
try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file)) {
|
||||
assertEquals("File length should be same as stream length", file.length(), stream.length());
|
||||
|
||||
byte[] result = new byte[9];
|
||||
|
||||
for (int i = 0; i < data.length / result.length; i++) {
|
||||
// Read backwards
|
||||
long newPos = stream.length() - result.length - i * result.length;
|
||||
stream.seek(newPos);
|
||||
assertEquals("Wrong stream position", newPos, stream.getStreamPosition());
|
||||
stream.readFully(result);
|
||||
assertTrue("Wrong data read: " + i, rangeEquals(data, (int) newPos, result, 0, result.length));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadOutsideDataSeek0Read() throws IOException {
|
||||
byte[] data = new byte[256];
|
||||
File file = randomDataToFile(data);
|
||||
|
||||
try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file)) {
|
||||
assertEquals("File length should be same as stream length", file.length(), stream.length());
|
||||
|
||||
byte[] buffer = new byte[data.length * 2];
|
||||
stream.read(buffer);
|
||||
stream.seek(0);
|
||||
assertNotEquals(-1, stream.read());
|
||||
assertNotEquals(-1, stream.read(buffer));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadBitRandom() throws IOException {
|
||||
byte[] bytes = new byte[8];
|
||||
File file = randomDataToFile(bytes);
|
||||
long value = ByteBuffer.wrap(bytes).getLong();
|
||||
|
||||
// Create stream
|
||||
try (ImageInputStream stream = new BufferedFileImageInputStream(file)) {
|
||||
for (int i = 1; i <= 64; i++) {
|
||||
assertEquals(String.format("bit %d differ", i), (value << (i - 1L)) >>> 63L, stream.readBit());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadBitsRandom() throws IOException {
|
||||
byte[] bytes = new byte[8];
|
||||
File file = randomDataToFile(bytes);
|
||||
long value = ByteBuffer.wrap(bytes).getLong();
|
||||
|
||||
// Create stream
|
||||
try (ImageInputStream stream = new BufferedFileImageInputStream(file)) {
|
||||
for (int i = 1; i <= 64; i++) {
|
||||
stream.seek(0);
|
||||
assertEquals(String.format("bit %d differ", i), value >>> (64L - i), stream.readBits(i));
|
||||
assertEquals(i % 8, stream.getBitOffset());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadBitsRandomOffset() throws IOException {
|
||||
byte[] bytes = new byte[8];
|
||||
File file = randomDataToFile(bytes);
|
||||
long value = ByteBuffer.wrap(bytes).getLong();
|
||||
|
||||
// Create stream
|
||||
try (ImageInputStream stream = new BufferedFileImageInputStream(file)) {
|
||||
for (int i = 1; i <= 60; i++) {
|
||||
stream.seek(0);
|
||||
stream.setBitOffset(i % 8);
|
||||
assertEquals(String.format("bit %d differ", i), (value << (i % 8)) >>> (64L - i), stream.readBits(i));
|
||||
assertEquals(i * 2 % 8, stream.getBitOffset());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadShort() throws IOException {
|
||||
byte[] bytes = new byte[8743]; // Slightly more than one buffer size
|
||||
File file = randomDataToFile(bytes);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
try (final ImageInputStream stream = new BufferedFileImageInputStream(file)) {
|
||||
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 2; i++) {
|
||||
assertEquals(buffer.getShort(), stream.readShort());
|
||||
}
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readShort();
|
||||
}
|
||||
});
|
||||
|
||||
stream.seek(0);
|
||||
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
buffer.position(0);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 2; i++) {
|
||||
assertEquals(buffer.getShort(), stream.readShort());
|
||||
}
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readShort();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadInt() throws IOException {
|
||||
byte[] bytes = new byte[8743]; // Slightly more than one buffer size
|
||||
File file = randomDataToFile(bytes);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
try (final ImageInputStream stream = new BufferedFileImageInputStream(file)) {
|
||||
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 4; i++) {
|
||||
assertEquals(buffer.getInt(), stream.readInt());
|
||||
}
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readInt();
|
||||
}
|
||||
});
|
||||
|
||||
stream.seek(0);
|
||||
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
buffer.position(0);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 4; i++) {
|
||||
assertEquals(buffer.getInt(), stream.readInt());
|
||||
}
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readInt();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadLong() throws IOException {
|
||||
byte[] bytes = new byte[8743]; // Slightly more than one buffer size
|
||||
File file = randomDataToFile(bytes);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
try (final ImageInputStream stream = new BufferedFileImageInputStream(file)) {
|
||||
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 8; i++) {
|
||||
assertEquals(buffer.getLong(), stream.readLong());
|
||||
}
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readLong();
|
||||
}
|
||||
});
|
||||
|
||||
stream.seek(0);
|
||||
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
buffer.position(0);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 8; i++) {
|
||||
assertEquals(buffer.getLong(), stream.readLong());
|
||||
}
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readLong();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSeekPastEOF() throws IOException {
|
||||
byte[] bytes = new byte[9];
|
||||
File file = randomDataToFile(bytes);
|
||||
|
||||
try (final ImageInputStream stream = new BufferedFileImageInputStream(file)) {
|
||||
stream.seek(1000);
|
||||
|
||||
assertEquals(-1, stream.read());
|
||||
assertEquals(-1, stream.read(new byte[1], 0, 1));
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readFully(new byte[1]);
|
||||
}
|
||||
});
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readByte();
|
||||
}
|
||||
});
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readShort();
|
||||
}
|
||||
});
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readInt();
|
||||
}
|
||||
});
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readLong();
|
||||
}
|
||||
});
|
||||
|
||||
stream.seek(0);
|
||||
for (byte value : bytes) {
|
||||
assertEquals(value, stream.readByte());
|
||||
}
|
||||
|
||||
assertEquals(-1, stream.read());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClose() throws IOException {
|
||||
// Create wrapper stream
|
||||
RandomAccessFile mock = mock(RandomAccessFile.class);
|
||||
ImageInputStream stream = new BufferedFileImageInputStream(mock);
|
||||
|
||||
stream.close();
|
||||
verify(mock, only()).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWorkaroundForWBMPImageReaderExpectsReadToBehaveAsReadFully() throws IOException {
|
||||
// See #606 for details.
|
||||
// Bug in JDK WBMPImageReader, uses read(byte[], int, int) instead of readFully(byte[], int, int).
|
||||
// Ie: Relies on read to return all bytes at once, without blocking
|
||||
int size = BufferedFileImageInputStream.DEFAULT_BUFFER_SIZE * 7;
|
||||
byte[] bytes = new byte[size];
|
||||
File file = randomDataToFile(bytes);
|
||||
|
||||
try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file)) {
|
||||
byte[] result = new byte[size];
|
||||
int head = stream.read(result, 0, 12); // Provoke a buffered read
|
||||
int len = stream.read(result, 12, size - 12); // Rest of buffer + direct read
|
||||
|
||||
assertEquals(size, len + head);
|
||||
assertArrayEquals(bytes, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.twelvemonkeys.imageio.stream;
|
||||
|
||||
import javax.imageio.spi.ImageInputStreamSpi;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
|
||||
public class BufferedRAFImageInputStreamSpiTest extends ImageInputStreamSpiTest<RandomAccessFile> {
|
||||
@Override
|
||||
protected ImageInputStreamSpi createProvider() {
|
||||
return new BufferedRAFImageInputStreamSpi();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RandomAccessFile createInput() throws IOException {
|
||||
return new RandomAccessFile(File.createTempFile("test-", ".tst"), "r");
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -39,11 +39,11 @@ import static com.twelvemonkeys.imageio.stream.BufferedImageInputStreamTest.rang
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* ByteArrayImageInputStreamTestCase
|
||||
* ByteArrayImageInputStreamTest
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: ByteArrayImageInputStreamTestCase.java,v 1.0 Apr 21, 2009 10:58:48 AM haraldk Exp$
|
||||
* @version $Id: ByteArrayImageInputStreamTest.java,v 1.0 Apr 21, 2009 10:58:48 AM haraldk Exp$
|
||||
*/
|
||||
public class ByteArrayImageInputStreamTest {
|
||||
private final Random random = new Random(1709843507234566L);
|
||||
|
||||
+3
-3
@@ -11,14 +11,14 @@ import java.util.Locale;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
abstract class ImageInputStreamSpiTest<T> {
|
||||
private final ImageInputStreamSpi provider = createProvider();
|
||||
protected final ImageInputStreamSpi provider = createProvider();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private final Class<T> inputClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
|
||||
protected final Class<T> inputClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
|
||||
|
||||
protected abstract ImageInputStreamSpi createProvider();
|
||||
|
||||
protected abstract T createInput();
|
||||
protected abstract T createInput() throws IOException;
|
||||
|
||||
@Test
|
||||
public void testInputClass() {
|
||||
|
||||
+145
-71
@@ -45,9 +45,8 @@ import javax.imageio.spi.IIORegistry;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.RenderedImage;
|
||||
import java.awt.image.SampleModel;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.image.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
@@ -57,6 +56,7 @@ import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import static java.lang.Math.min;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@@ -97,10 +97,6 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
|
||||
protected abstract List<String> getMIMETypes();
|
||||
|
||||
protected boolean allowsNullRawImageType() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static void failBecause(String message, Throwable exception) {
|
||||
throw new AssertionError(message, exception);
|
||||
}
|
||||
@@ -221,6 +217,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
image = reader.read(i);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
failBecause(String.format("Image %s index %s could not be read: %s", data.getInput(), i, e), e);
|
||||
}
|
||||
|
||||
@@ -283,26 +280,6 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
reader.dispose();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadNoInput() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
// Do not set input
|
||||
|
||||
BufferedImage image = null;
|
||||
try {
|
||||
image = reader.read(0);
|
||||
fail("Read image with no input");
|
||||
}
|
||||
catch (IllegalStateException ignore) {
|
||||
}
|
||||
catch (IOException e) {
|
||||
failBecause("Image could not be read", e);
|
||||
}
|
||||
assertNull(image);
|
||||
|
||||
reader.dispose();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReRead() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
@@ -323,69 +300,71 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
reader.dispose();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void testReadNoInput() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
// Do not set input
|
||||
|
||||
try {
|
||||
reader.read(0);
|
||||
fail("Read image with no input");
|
||||
}
|
||||
catch (IOException e) {
|
||||
failBecause("Image could not be read", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = IndexOutOfBoundsException.class)
|
||||
public void testReadIndexNegativeWithParam() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
TestData data = getTestData().get(0);
|
||||
reader.setInput(data.getInputStream());
|
||||
|
||||
BufferedImage image = null;
|
||||
try {
|
||||
image = reader.read(-1, reader.getDefaultReadParam());
|
||||
reader.read(-1, reader.getDefaultReadParam());
|
||||
fail("Read image with illegal index");
|
||||
}
|
||||
catch (IndexOutOfBoundsException ignore) {
|
||||
}
|
||||
catch (IOException e) {
|
||||
failBecause("Image could not be read", e);
|
||||
}
|
||||
|
||||
assertNull(image);
|
||||
|
||||
reader.dispose();
|
||||
finally {
|
||||
reader.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(expected = IndexOutOfBoundsException.class)
|
||||
public void testReadIndexOutOfBoundsWithParam() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
TestData data = getTestData().get(0);
|
||||
reader.setInput(data.getInputStream());
|
||||
|
||||
BufferedImage image = null;
|
||||
try {
|
||||
image = reader.read(Short.MAX_VALUE, reader.getDefaultReadParam());
|
||||
reader.read(Short.MAX_VALUE, reader.getDefaultReadParam());
|
||||
fail("Read image with index out of bounds");
|
||||
}
|
||||
catch (IndexOutOfBoundsException ignore) {
|
||||
}
|
||||
catch (IOException e) {
|
||||
failBecause("Image could not be read", e);
|
||||
}
|
||||
|
||||
assertNull(image);
|
||||
|
||||
reader.dispose();
|
||||
finally {
|
||||
reader.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void testReadNoInputWithParam() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
// Do not set input
|
||||
|
||||
BufferedImage image = null;
|
||||
try {
|
||||
image = reader.read(0, reader.getDefaultReadParam());
|
||||
reader.read(0, reader.getDefaultReadParam());
|
||||
fail("Read image with no input");
|
||||
}
|
||||
catch (IllegalStateException ignore) {
|
||||
}
|
||||
catch (IOException e) {
|
||||
failBecause("Image could not be read", e);
|
||||
}
|
||||
|
||||
assertNull(image);
|
||||
|
||||
reader.dispose();
|
||||
finally {
|
||||
reader.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1172,7 +1151,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
// At least imageStarted and imageComplete, plus any number of imageProgress
|
||||
InOrder ordered = inOrder(listener);
|
||||
ordered.verify(listener).imageStarted(reader, 0);
|
||||
ordered.verify(listener, atLeastOnce()).imageProgress(eq(reader), anyInt());
|
||||
ordered.verify(listener, atLeastOnce()).imageProgress(eq(reader), anyFloat());
|
||||
ordered.verify(listener).imageComplete(reader);
|
||||
reader.dispose();
|
||||
}
|
||||
@@ -1205,9 +1184,9 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
ordered.verify(listenerToo).imageStarted(reader, 0);
|
||||
ordered.verify(listenerThree).imageStarted(reader, 0);
|
||||
|
||||
ordered.verify(listener, atLeastOnce()).imageProgress(eq(reader), anyInt());
|
||||
ordered.verify(listenerToo, atLeastOnce()).imageProgress(eq(reader), anyInt());
|
||||
ordered.verify(listenerThree, atLeastOnce()).imageProgress(eq(reader), anyInt());
|
||||
ordered.verify(listener, atLeastOnce()).imageProgress(eq(reader), anyFloat());
|
||||
ordered.verify(listenerToo, atLeastOnce()).imageProgress(eq(reader), anyFloat());
|
||||
ordered.verify(listenerThree, atLeastOnce()).imageProgress(eq(reader), anyFloat());
|
||||
|
||||
ordered.verify(listener).imageComplete(reader);
|
||||
ordered.verify(listenerToo).imageComplete(reader);
|
||||
@@ -1247,7 +1226,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
// Should not have called any methods...
|
||||
verifyZeroInteractions(listener);
|
||||
verifyNoInteractions(listener);
|
||||
reader.dispose();
|
||||
}
|
||||
|
||||
@@ -1274,11 +1253,11 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
// Should not have called any methods on listener1...
|
||||
verifyZeroInteractions(listener);
|
||||
verifyNoInteractions(listener);
|
||||
|
||||
InOrder ordered = inOrder(listenerToo);
|
||||
ordered.verify(listenerToo).imageStarted(reader, 0);
|
||||
ordered.verify(listenerToo, atLeastOnce()).imageProgress(eq(reader), anyInt());
|
||||
ordered.verify(listenerToo, atLeastOnce()).imageProgress(eq(reader), anyFloat());
|
||||
ordered.verify(listenerToo).imageComplete(reader);
|
||||
reader.dispose();
|
||||
}
|
||||
@@ -1302,7 +1281,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
// Should not have called any methods...
|
||||
verifyZeroInteractions(listener);
|
||||
verifyNoInteractions(listener);
|
||||
reader.dispose();
|
||||
}
|
||||
|
||||
@@ -1328,8 +1307,8 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
// Should not have called any methods...
|
||||
verifyZeroInteractions(listener);
|
||||
verifyZeroInteractions(listenerToo);
|
||||
verifyNoInteractions(listener);
|
||||
verifyNoInteractions(listenerToo);
|
||||
reader.dispose();
|
||||
}
|
||||
|
||||
@@ -1354,7 +1333,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
};
|
||||
doAnswer(abort).when(abortingListener).imageStarted(any(ImageReader.class), anyInt());
|
||||
doAnswer(abort).when(abortingListener).imageProgress(any(ImageReader.class), anyInt());
|
||||
doAnswer(abort).when(abortingListener).imageProgress(any(ImageReader.class), anyFloat());
|
||||
|
||||
reader.addIIOReadProgressListener(abortingListener);
|
||||
|
||||
@@ -1377,9 +1356,6 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
reader.setInput(data.getInputStream());
|
||||
|
||||
ImageTypeSpecifier rawType = reader.getRawImageType(0);
|
||||
if (rawType == null && allowsNullRawImageType()) {
|
||||
continue;
|
||||
}
|
||||
assertNotNull(rawType);
|
||||
|
||||
Iterator<ImageTypeSpecifier> types = reader.getImageTypes(0);
|
||||
@@ -1401,6 +1377,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
|
||||
assertTrue("ImageTypeSpecifier from getRawImageType should be in the iterator from getImageTypes", rawFound);
|
||||
}
|
||||
|
||||
reader.dispose();
|
||||
}
|
||||
|
||||
@@ -1649,12 +1626,72 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
BufferedImage one = reader.read(0);
|
||||
BufferedImage two = reader.read(0);
|
||||
|
||||
// Test for same BufferedImage instance
|
||||
assertNotSame("Multiple reads return same (mutable) image", one, two);
|
||||
|
||||
one.setRGB(0, 0, Color.BLUE.getRGB());
|
||||
two.setRGB(0, 0, Color.RED.getRGB());
|
||||
|
||||
// Test for same backing storage (array)
|
||||
one.setRGB(0, 0, Color.BLACK.getRGB());
|
||||
two.setRGB(0, 0, Color.WHITE.getRGB());
|
||||
assertTrue(one.getRGB(0, 0) != two.getRGB(0, 0));
|
||||
|
||||
reader.dispose();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadThumbnails() throws IOException {
|
||||
T reader = createReader();
|
||||
|
||||
if (reader.readerSupportsThumbnails()) {
|
||||
for (TestData testData : getTestData()) {
|
||||
try (ImageInputStream inputStream = testData.getInputStream()) {
|
||||
reader.setInput(inputStream);
|
||||
|
||||
int numImages = reader.getNumImages(true);
|
||||
|
||||
for (int i = 0; i < numImages; i++) {
|
||||
int numThumbnails = reader.getNumThumbnails(0);
|
||||
|
||||
for (int t = 0; t < numThumbnails; t++) {
|
||||
BufferedImage thumbnail = reader.readThumbnail(0, t);
|
||||
|
||||
assertNotNull(thumbnail);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reader.dispose();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThumbnailProgress() throws IOException {
|
||||
T reader = createReader();
|
||||
|
||||
IIOReadProgressListener listener = mock(IIOReadProgressListener.class);
|
||||
reader.addIIOReadProgressListener(listener);
|
||||
|
||||
if (reader.readerSupportsThumbnails()) {
|
||||
for (TestData testData : getTestData()) {
|
||||
try (ImageInputStream inputStream = testData.getInputStream()) {
|
||||
|
||||
reader.setInput(inputStream);
|
||||
|
||||
int numThumbnails = reader.getNumThumbnails(0);
|
||||
for (int i = 0; i < numThumbnails; i++) {
|
||||
reset(listener);
|
||||
|
||||
reader.readThumbnail(0, i);
|
||||
|
||||
InOrder order = inOrder(listener);
|
||||
order.verify(listener).thumbnailStarted(reader, 0, i);
|
||||
order.verify(listener, atLeastOnce()).thumbnailProgress(reader, 100f);
|
||||
order.verify(listener).thumbnailComplete(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reader.dispose();
|
||||
}
|
||||
|
||||
@@ -1701,6 +1738,43 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
reader.dispose();
|
||||
}
|
||||
|
||||
protected List<TestData> getTestDataForAffineTransformOpCompatibility() {
|
||||
// Allow subclasses to filter out test data that can't be converted to a compatible image without data loss
|
||||
return getTestData();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAffineTransformOpCompatibility() throws IOException {
|
||||
// Test that the output of normal images are compatible with AffineTransformOp. Is unlikely to work on all test data
|
||||
ImageReader reader = createReader();
|
||||
|
||||
for (TestData testData : getTestDataForAffineTransformOpCompatibility()) {
|
||||
try (ImageInputStream input = testData.getInputStream()) {
|
||||
reader.setInput(input);
|
||||
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
param.setSourceRegion(new Rectangle(min(reader.getWidth(0), 64), min(reader.getHeight(0), 64)));
|
||||
|
||||
BufferedImage originalImage = reader.read(0, param);
|
||||
|
||||
AffineTransform transform = AffineTransform.getTranslateInstance(10, 10);
|
||||
AffineTransformOp op = new AffineTransformOp(transform, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
|
||||
|
||||
try {
|
||||
BufferedImage resultImage = op.filter(originalImage, null); // The exception happens here
|
||||
assertNotNull(resultImage);
|
||||
}
|
||||
catch (ImagingOpException e) {
|
||||
fail(e.getMessage() + ".\n\t"
|
||||
+ originalImage + "\n\t"
|
||||
+ testData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reader.dispose();
|
||||
}
|
||||
|
||||
@Ignore("TODO: Implement")
|
||||
@Test
|
||||
public void testSetDestinationBands() {
|
||||
|
||||
+20
-15
@@ -30,14 +30,20 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.util;
|
||||
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ColorModel;
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.awt.image.DirectColorModel;
|
||||
import java.awt.image.IndexColorModel;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.*;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
|
||||
public class ImageTypeSpecifiersTest {
|
||||
|
||||
@@ -541,8 +547,7 @@ public class ImageTypeSpecifiersTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatePackedGrayscale1() {
|
||||
// TODO: Fails on Java 11, because IndexColorModel now has an overloaded equals that actually tests the color entries
|
||||
public void testCreatePackedGrayscale1BPP() {
|
||||
assertEquals(
|
||||
ImageTypeSpecifier.createGrayscale(1, DataBuffer.TYPE_BYTE, false),
|
||||
ImageTypeSpecifiers.createPackedGrayscale(GRAY, 1, DataBuffer.TYPE_BYTE)
|
||||
@@ -550,8 +555,8 @@ public class ImageTypeSpecifiersTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatePackedGrayscale2() {
|
||||
// TODO: Fails on Java 11, because IndexColorModel now has an overloaded equals that actually tests the color entries
|
||||
public void testCreatePackedGrayscale2BPP() {
|
||||
// TODO: Fails on Java 11+, because IndexColorModel now has an overloaded equals that actually tests the color entries
|
||||
assertEquals(
|
||||
ImageTypeSpecifier.createGrayscale(2, DataBuffer.TYPE_BYTE, false),
|
||||
ImageTypeSpecifiers.createPackedGrayscale(GRAY, 2, DataBuffer.TYPE_BYTE)
|
||||
@@ -559,8 +564,8 @@ public class ImageTypeSpecifiersTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatePackedGrayscale4() throws Exception {
|
||||
// TODO: Fails on Java 11, because IndexColorModel now has an overloaded equals that actually tests the color entries
|
||||
public void testCreatePackedGrayscale4BPP() {
|
||||
// TODO: Fails on Java 11+, because IndexColorModel now has an overloaded equals that actually tests the color entries
|
||||
assertEquals(
|
||||
ImageTypeSpecifier.createGrayscale(4, DataBuffer.TYPE_BYTE, false),
|
||||
ImageTypeSpecifiers.createPackedGrayscale(GRAY, 4, DataBuffer.TYPE_BYTE)
|
||||
@@ -653,7 +658,7 @@ public class ImageTypeSpecifiersTest {
|
||||
for (int bits = 1; bits <= 8; bits <<= 1) {
|
||||
int[] colors = createIntLut(1 << bits);
|
||||
assertEquals(
|
||||
IndexedImageTypeSpecifier.createFromIndexColorModel(new IndexColorModel(bits, colors.length, colors, 0, false, -1, DataBuffer.TYPE_BYTE)),
|
||||
new IndexedImageTypeSpecifier(new IndexColorModel(bits, colors.length, colors, 0, false, -1, DataBuffer.TYPE_BYTE)),
|
||||
ImageTypeSpecifiers.createIndexed(colors, false, -1, bits, DataBuffer.TYPE_BYTE)
|
||||
);
|
||||
}
|
||||
@@ -663,7 +668,7 @@ public class ImageTypeSpecifiersTest {
|
||||
public void testCreateIndexedIntArray16() {
|
||||
int[] colors = createIntLut(1 << 16);
|
||||
assertEquals(
|
||||
IndexedImageTypeSpecifier.createFromIndexColorModel(new IndexColorModel(16, colors.length, colors, 0, false, -1, DataBuffer.TYPE_USHORT)),
|
||||
new IndexedImageTypeSpecifier(new IndexColorModel(16, colors.length, colors, 0, false, -1, DataBuffer.TYPE_USHORT)),
|
||||
ImageTypeSpecifiers.createIndexed(colors, false, -1, 16, DataBuffer.TYPE_USHORT)
|
||||
);
|
||||
|
||||
@@ -675,7 +680,7 @@ public class ImageTypeSpecifiersTest {
|
||||
int[] colors = createIntLut(1 << bits);
|
||||
IndexColorModel colorModel = new IndexColorModel(bits, colors.length, colors, 0, false, -1, DataBuffer.TYPE_BYTE);
|
||||
assertEquals(
|
||||
IndexedImageTypeSpecifier.createFromIndexColorModel(colorModel),
|
||||
new IndexedImageTypeSpecifier(colorModel),
|
||||
ImageTypeSpecifiers.createFromIndexColorModel(colorModel)
|
||||
);
|
||||
}
|
||||
@@ -686,7 +691,7 @@ public class ImageTypeSpecifiersTest {
|
||||
int[] colors = createIntLut(1 << 16);
|
||||
IndexColorModel colorModel = new IndexColorModel(16, colors.length, colors, 0, false, -1, DataBuffer.TYPE_USHORT);
|
||||
assertEquals(
|
||||
IndexedImageTypeSpecifier.createFromIndexColorModel(colorModel),
|
||||
new IndexedImageTypeSpecifier(colorModel),
|
||||
ImageTypeSpecifiers.createFromIndexColorModel(colorModel)
|
||||
);
|
||||
}
|
||||
|
||||
+11
-12
@@ -52,7 +52,6 @@ import java.net.URL;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
@@ -104,7 +103,7 @@ public abstract class ImageWriterAbstractTest<T extends ImageWriter> {
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
|
||||
protected final RenderedImage getTestData(final int index) {
|
||||
return getTestData().get(index);
|
||||
}
|
||||
@@ -219,7 +218,7 @@ public abstract class ImageWriterAbstractTest<T extends ImageWriter> {
|
||||
// At least imageStarted and imageComplete, plus any number of imageProgress
|
||||
InOrder ordered = inOrder(listener);
|
||||
ordered.verify(listener).imageStarted(writer, 0);
|
||||
ordered.verify(listener, atLeastOnce()).imageProgress(eq(writer), anyInt());
|
||||
ordered.verify(listener, atLeastOnce()).imageProgress(eq(writer), anyFloat());
|
||||
ordered.verify(listener).imageComplete(writer);
|
||||
}
|
||||
|
||||
@@ -251,9 +250,9 @@ public abstract class ImageWriterAbstractTest<T extends ImageWriter> {
|
||||
ordered.verify(listenerToo).imageStarted(writer, 0);
|
||||
ordered.verify(listenerThree).imageStarted(writer, 0);
|
||||
|
||||
ordered.verify(listener, atLeastOnce()).imageProgress(eq(writer), anyInt());
|
||||
ordered.verify(listenerToo, atLeastOnce()).imageProgress(eq(writer), anyInt());
|
||||
ordered.verify(listenerThree, atLeastOnce()).imageProgress(eq(writer), anyInt());
|
||||
ordered.verify(listener, atLeastOnce()).imageProgress(eq(writer), anyFloat());
|
||||
ordered.verify(listenerToo, atLeastOnce()).imageProgress(eq(writer), anyFloat());
|
||||
ordered.verify(listenerThree, atLeastOnce()).imageProgress(eq(writer), anyFloat());
|
||||
|
||||
ordered.verify(listener).imageComplete(writer);
|
||||
ordered.verify(listenerToo).imageComplete(writer);
|
||||
@@ -290,7 +289,7 @@ public abstract class ImageWriterAbstractTest<T extends ImageWriter> {
|
||||
}
|
||||
|
||||
// Should not have called any methods...
|
||||
verifyZeroInteractions(listener);
|
||||
verifyNoInteractions(listener);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -315,12 +314,12 @@ public abstract class ImageWriterAbstractTest<T extends ImageWriter> {
|
||||
}
|
||||
|
||||
// Should not have called any methods...
|
||||
verifyZeroInteractions(listener);
|
||||
verifyNoInteractions(listener);
|
||||
|
||||
// At least imageStarted and imageComplete, plus any number of imageProgress
|
||||
InOrder ordered = inOrder(listenerToo);
|
||||
ordered.verify(listenerToo).imageStarted(writer, 0);
|
||||
ordered.verify(listenerToo, atLeastOnce()).imageProgress(eq(writer), anyInt());
|
||||
ordered.verify(listenerToo, atLeastOnce()).imageProgress(eq(writer), anyFloat());
|
||||
ordered.verify(listenerToo).imageComplete(writer);
|
||||
|
||||
}
|
||||
@@ -345,7 +344,7 @@ public abstract class ImageWriterAbstractTest<T extends ImageWriter> {
|
||||
}
|
||||
|
||||
// Should not have called any methods...
|
||||
verifyZeroInteractions(listener);
|
||||
verifyNoInteractions(listener);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -371,7 +370,7 @@ public abstract class ImageWriterAbstractTest<T extends ImageWriter> {
|
||||
}
|
||||
|
||||
// Should not have called any methods...
|
||||
verifyZeroInteractions(listener);
|
||||
verifyZeroInteractions(listenerToo);
|
||||
verifyNoInteractions(listener);
|
||||
verifyNoInteractions(listenerToo);
|
||||
}
|
||||
}
|
||||
+17
-17
@@ -30,14 +30,17 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.util;
|
||||
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.awt.image.IndexColorModel;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* IndexedImageTypeSpecifierTestCase
|
||||
@@ -51,46 +54,43 @@ public class IndexedImageTypeSpecifierTest {
|
||||
public void testEquals() {
|
||||
IndexColorModel cm = new IndexColorModel(1, 2, new int[]{0xffffff, 0x00}, 0, false, -1, DataBuffer.TYPE_BYTE);
|
||||
|
||||
ImageTypeSpecifier spec = IndexedImageTypeSpecifier.createFromIndexColorModel(cm);
|
||||
ImageTypeSpecifier other = IndexedImageTypeSpecifier.createFromIndexColorModel(cm);
|
||||
ImageTypeSpecifier different = IndexedImageTypeSpecifier.createFromIndexColorModel(new IndexColorModel(2, 2, new int[]{0xff00ff, 0x00, 0xff00ff, 0x00}, 0, false, -1, DataBuffer.TYPE_BYTE));
|
||||
ImageTypeSpecifier spec = new IndexedImageTypeSpecifier(cm);
|
||||
ImageTypeSpecifier other = new IndexedImageTypeSpecifier(cm);
|
||||
ImageTypeSpecifier different = new IndexedImageTypeSpecifier(new IndexColorModel(2, 2, new int[]{0xff00ff, 0x00, 0xff00ff, 0x00}, 0, false, -1, DataBuffer.TYPE_BYTE));
|
||||
|
||||
assertEquals(spec, other);
|
||||
assertEquals(other, spec);
|
||||
|
||||
assertEquals(spec.hashCode(), other.hashCode());
|
||||
|
||||
assertTrue(spec.equals(other));
|
||||
assertTrue(other.equals(spec));
|
||||
|
||||
// TODO: There is still a problem that IndexColorModel does not override equals,
|
||||
// so any model with the same number of bits, transparency, and transfer type will be treated as equal
|
||||
assertFalse(other.equals(different));
|
||||
assertNotEquals(other, different);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHashCode() {
|
||||
IndexColorModel cm = new IndexColorModel(1, 2, new int[]{0xffffff, 0x00}, 0, false, -1, DataBuffer.TYPE_BYTE);
|
||||
|
||||
ImageTypeSpecifier spec = IndexedImageTypeSpecifier.createFromIndexColorModel(cm);
|
||||
ImageTypeSpecifier other = IndexedImageTypeSpecifier.createFromIndexColorModel(cm);
|
||||
ImageTypeSpecifier different = IndexedImageTypeSpecifier.createFromIndexColorModel(new IndexColorModel(2, 2, new int[]{0xff00ff, 0x00, 0xff00ff, 0x00}, 0, false, -1, DataBuffer.TYPE_BYTE));
|
||||
ImageTypeSpecifier spec = new IndexedImageTypeSpecifier(cm);
|
||||
ImageTypeSpecifier other = new IndexedImageTypeSpecifier(cm);
|
||||
ImageTypeSpecifier different = new IndexedImageTypeSpecifier(new IndexColorModel(2, 2, new int[]{0xff00ff, 0x00, 0xff00ff, 0x00}, 0, false, -1, DataBuffer.TYPE_BYTE));
|
||||
|
||||
// TODO: There is still a problem that IndexColorModel does not override hashCode,
|
||||
// so any model with the same number of bits, transparency, and transfer type will have same hash
|
||||
assertEquals(spec.hashCode(), other.hashCode());
|
||||
assertFalse(spec.hashCode() == different.hashCode());
|
||||
assertNotEquals(spec.hashCode(), different.hashCode());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testCreateNull() {
|
||||
IndexedImageTypeSpecifier.createFromIndexColorModel(null);
|
||||
new IndexedImageTypeSpecifier(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateBufferedImageBinary() {
|
||||
IndexColorModel cm = new IndexColorModel(1, 2, new int[]{0xffffff, 0x00}, 0, false, -1, DataBuffer.TYPE_BYTE);
|
||||
ImageTypeSpecifier spec = IndexedImageTypeSpecifier.createFromIndexColorModel(cm);
|
||||
ImageTypeSpecifier spec = new IndexedImageTypeSpecifier(cm);
|
||||
|
||||
BufferedImage image = spec.createBufferedImage(2, 2);
|
||||
|
||||
@@ -102,7 +102,7 @@ public class IndexedImageTypeSpecifierTest {
|
||||
@Test
|
||||
public void testCreateBufferedImageIndexed() {
|
||||
IndexColorModel cm = new IndexColorModel(8, 256, new int[256], 0, false, -1, DataBuffer.TYPE_BYTE);
|
||||
ImageTypeSpecifier spec = IndexedImageTypeSpecifier.createFromIndexColorModel(cm);
|
||||
ImageTypeSpecifier spec = new IndexedImageTypeSpecifier(cm);
|
||||
|
||||
BufferedImage image = spec.createBufferedImage(2, 2);
|
||||
|
||||
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
package com.twelvemonkeys.imageio.util;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import java.awt.*;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.*;
|
||||
import java.util.Random;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertSame;
|
||||
|
||||
/**
|
||||
* RasterUtilsTest.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: RasterUtilsTest.java,v 1.0 05/05/2021 haraldk Exp$
|
||||
*/
|
||||
public class RasterUtilsTest {
|
||||
@Test(expected = NullPointerException.class)
|
||||
public void testAsByteRasterFromNull() {
|
||||
RasterUtils.asByteRaster((Raster) null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("RedundantCast")
|
||||
@Test(expected = NullPointerException.class)
|
||||
public void testAsByteRasterWritableFromNull() {
|
||||
RasterUtils.asByteRaster((WritableRaster) null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsByteRasterPassThrough() {
|
||||
WritableRaster[] rasters = new WritableRaster[] {
|
||||
new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR).getRaster(),
|
||||
new BufferedImage(1, 1, BufferedImage.TYPE_4BYTE_ABGR).getRaster(),
|
||||
new BufferedImage(1, 1, BufferedImage.TYPE_4BYTE_ABGR_PRE).getRaster(),
|
||||
new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY).getRaster(),
|
||||
Raster.createBandedRaster(DataBuffer.TYPE_BYTE, 1, 1, 7, null),
|
||||
Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, 1, 1, 2, null),
|
||||
new WritableRaster(new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE, 1, 1, 1, 1, new int[1]), new Point(0, 0)) {}
|
||||
};
|
||||
|
||||
for (Raster raster : rasters) {
|
||||
assertSame(raster, RasterUtils.asByteRaster(raster));
|
||||
}
|
||||
|
||||
for (WritableRaster raster : rasters) {
|
||||
assertSame(raster, RasterUtils.asByteRaster(raster));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsByteRasterWritableFromTYPE_INT_RGB() {
|
||||
BufferedImage image = new BufferedImage(9, 11, BufferedImage.TYPE_INT_RGB);
|
||||
|
||||
WritableRaster raster = RasterUtils.asByteRaster(image.getRaster());
|
||||
|
||||
assertEquals(DataBuffer.TYPE_BYTE, raster.getTransferType());
|
||||
assertEquals(PixelInterleavedSampleModel.class, raster.getSampleModel().getClass());
|
||||
assertEquals(image.getWidth(), raster.getWidth());
|
||||
assertEquals(image.getHeight(), raster.getHeight());
|
||||
|
||||
assertEquals(3, raster.getNumBands());
|
||||
assertEquals(3, raster.getNumDataElements());
|
||||
|
||||
assertImageRasterEquals(image, raster);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsByteRasterWritableFromTYPE_INT_ARGB() {
|
||||
BufferedImage image = new BufferedImage(9, 11, BufferedImage.TYPE_INT_ARGB);
|
||||
|
||||
WritableRaster raster = RasterUtils.asByteRaster(image.getRaster());
|
||||
|
||||
assertEquals(DataBuffer.TYPE_BYTE, raster.getTransferType());
|
||||
assertEquals(PixelInterleavedSampleModel.class, raster.getSampleModel().getClass());
|
||||
assertEquals(image.getWidth(), raster.getWidth());
|
||||
assertEquals(image.getHeight(), raster.getHeight());
|
||||
|
||||
assertEquals(4, raster.getNumBands());
|
||||
assertEquals(4, raster.getNumDataElements());
|
||||
|
||||
assertImageRasterEquals(image, raster);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsByteRasterWritableFromTYPE_INT_ARGB_PRE() {
|
||||
BufferedImage image = new BufferedImage(9, 11, BufferedImage.TYPE_INT_ARGB_PRE);
|
||||
|
||||
WritableRaster raster = RasterUtils.asByteRaster(image.getRaster());
|
||||
|
||||
assertEquals(DataBuffer.TYPE_BYTE, raster.getTransferType());
|
||||
assertEquals(PixelInterleavedSampleModel.class, raster.getSampleModel().getClass());
|
||||
assertEquals(image.getWidth(), raster.getWidth());
|
||||
assertEquals(image.getHeight(), raster.getHeight());
|
||||
|
||||
assertEquals(4, raster.getNumBands());
|
||||
assertEquals(4, raster.getNumDataElements());
|
||||
|
||||
// We don't assert on values here, as the premultiplied values makes it hard...
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsByteRasterWritableFromTYPE_INT_BGR() {
|
||||
BufferedImage image = new BufferedImage(9, 11, BufferedImage.TYPE_INT_BGR);
|
||||
|
||||
WritableRaster raster = RasterUtils.asByteRaster(image.getRaster());
|
||||
|
||||
assertEquals(DataBuffer.TYPE_BYTE, raster.getTransferType());
|
||||
assertEquals(PixelInterleavedSampleModel.class, raster.getSampleModel().getClass());
|
||||
assertEquals(image.getWidth(), raster.getWidth());
|
||||
assertEquals(image.getHeight(), raster.getHeight());
|
||||
|
||||
assertEquals(3, raster.getNumBands());
|
||||
assertEquals(3, raster.getNumDataElements());
|
||||
|
||||
assertImageRasterEquals(image, raster);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsByteRasterWritableFromTYPE_CUSTOM_GRAB() {
|
||||
BufferedImage image = ImageTypeSpecifier.createPacked(ColorSpace.getInstance(ColorSpace.CS_sRGB),
|
||||
0x00FF0000,
|
||||
0xFF000000,
|
||||
0x000000FF,
|
||||
0x0000FF00,
|
||||
DataBuffer.TYPE_INT, false).createBufferedImage(7, 13);
|
||||
|
||||
WritableRaster raster = RasterUtils.asByteRaster(image.getRaster());
|
||||
|
||||
assertEquals(DataBuffer.TYPE_BYTE, raster.getTransferType());
|
||||
assertEquals(PixelInterleavedSampleModel.class, raster.getSampleModel().getClass());
|
||||
assertEquals(image.getWidth(), raster.getWidth());
|
||||
assertEquals(image.getHeight(), raster.getHeight());
|
||||
|
||||
assertEquals(4, raster.getNumBands());
|
||||
assertEquals(4, raster.getNumDataElements());
|
||||
|
||||
assertImageRasterEquals(image, raster);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsByteRasterWritableFromTYPE_CUSTOM_BxRG() {
|
||||
BufferedImage image = ImageTypeSpecifier.createPacked(ColorSpace.getInstance(ColorSpace.CS_sRGB),
|
||||
0x0000FF00,
|
||||
0x000000FF,
|
||||
0xFF000000,
|
||||
0,
|
||||
DataBuffer.TYPE_INT, false).createBufferedImage(7, 13);
|
||||
|
||||
WritableRaster raster = RasterUtils.asByteRaster(image.getRaster());
|
||||
|
||||
assertEquals(DataBuffer.TYPE_BYTE, raster.getTransferType());
|
||||
assertEquals(PixelInterleavedSampleModel.class, raster.getSampleModel().getClass());
|
||||
assertEquals(image.getWidth(), raster.getWidth());
|
||||
assertEquals(image.getHeight(), raster.getHeight());
|
||||
|
||||
assertEquals(3, raster.getNumBands());
|
||||
assertEquals(3, raster.getNumDataElements());
|
||||
|
||||
assertImageRasterEquals(image, raster);
|
||||
}
|
||||
|
||||
private static void assertImageRasterEquals(BufferedImage image, WritableRaster raster) {
|
||||
// NOTE: This is NOT necessarily how the values are stored in the data buffer
|
||||
int[] argbOffs = new int[] {16, 8, 0, 24};
|
||||
|
||||
Raster imageRaster = image.getRaster();
|
||||
|
||||
Random rng = new Random(27365481723L);
|
||||
|
||||
for (int y = 0; y < raster.getHeight(); y++) {
|
||||
for (int x = 0; x < raster.getWidth(); x++) {
|
||||
int argb = 0;
|
||||
|
||||
for (int b = 0; b < raster.getNumBands(); b++) {
|
||||
int s = rng.nextInt(0xFF);
|
||||
raster.setSample(x, y, b, s);
|
||||
|
||||
assertEquals(s, raster.getSample(x, y, b));
|
||||
assertEquals(s, imageRaster.getSample(x, y, b));
|
||||
|
||||
argb |= (s << argbOffs[b]);
|
||||
}
|
||||
|
||||
if (raster.getNumBands() < 4) {
|
||||
argb |= 0xFF000000;
|
||||
}
|
||||
|
||||
int expectedArgb = image.getRGB(x, y);
|
||||
if (argb != expectedArgb) {
|
||||
assertEquals(x + ", " + y + ": ", String.format("#%08x", expectedArgb), String.format("#%08x", argb));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.6.3</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-hdr</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: HDR plugin</name>
|
||||
@@ -12,6 +12,10 @@
|
||||
ImageIO plugin for Radiance RGBE High Dynaimc Range format (HDR).
|
||||
</description>
|
||||
|
||||
<properties>
|
||||
<project.jpms.module.name>com.twelvemonkeys.imageio.hdr</project.jpms.module.name>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
|
||||
+10
-2
@@ -38,12 +38,14 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
/**
|
||||
* TGAImageReaderTest
|
||||
* HDRImageReaderTest
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: TGAImageReaderTest.java,v 1.0 03.07.14 22:28 haraldk Exp$
|
||||
* @version $Id: HDRImageReaderTest.java,v 1.0 03.07.14 22:28 haraldk Exp$
|
||||
*/
|
||||
public class HDRImageReaderTest extends ImageReaderAbstractTest<HDRImageReader> {
|
||||
@Override
|
||||
@@ -58,6 +60,12 @@ public class HDRImageReaderTest extends ImageReaderAbstractTest<HDRImageReader>
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<TestData> getTestDataForAffineTransformOpCompatibility() {
|
||||
// HDR images uses floating point buffers...
|
||||
return emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getFormatNames() {
|
||||
return Arrays.asList("HDR", "hdr", "RGBE", "rgbe");
|
||||
|
||||
@@ -4,12 +4,16 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.6.3</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-icns</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: ICNS plugin</name>
|
||||
<description>ImageIO plugin for Apple Icon Image (ICNS) format.</description>
|
||||
|
||||
<properties>
|
||||
<project.jpms.module.name>com.twelvemonkeys.imageio.icns</project.jpms.module.name>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.6.3</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-iff</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: IFF plugin</name>
|
||||
@@ -13,6 +13,10 @@
|
||||
type ILBM and PBM format.
|
||||
</description>
|
||||
|
||||
<properties>
|
||||
<project.jpms.module.name>com.twelvemonkeys.imageio.iff</project.jpms.module.name>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
|
||||
+3
-2
@@ -102,14 +102,15 @@ abstract class AbstractMultiPaletteChunk extends IFFChunk implements MultiPalett
|
||||
}
|
||||
|
||||
@Override
|
||||
void writeChunk(DataOutput pOutput) throws IOException {
|
||||
void writeChunk(DataOutput pOutput) {
|
||||
throw new UnsupportedOperationException("Method writeChunk not implemented");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ColorModel getColorModel(final IndexColorModel colorModel, final int rowIndex, final boolean laced) {
|
||||
if (rowIndex < lastRow || mutablePalette == null || originalPalette != null && originalPalette.get() != colorModel) {
|
||||
originalPalette = new WeakReference<IndexColorModel>(colorModel);
|
||||
originalPalette = new WeakReference<>(colorModel);
|
||||
mutablePalette = new MutableIndexColorModel(colorModel);
|
||||
|
||||
if (initialChanges != null) {
|
||||
|
||||
+7
-3
@@ -30,11 +30,12 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.iff;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
|
||||
/**
|
||||
* BMHDChunk
|
||||
*
|
||||
@@ -129,7 +130,8 @@ final class BMHDChunk extends IFFChunk {
|
||||
pageHeight = Math.min(pHeight, Short.MAX_VALUE);
|
||||
}
|
||||
|
||||
void readChunk(DataInput pInput) throws IOException {
|
||||
@Override
|
||||
void readChunk(final DataInput pInput) throws IOException {
|
||||
if (chunkLength != 20) {
|
||||
throw new IIOException("Unknown BMHD chunk length: " + chunkLength);
|
||||
}
|
||||
@@ -148,7 +150,8 @@ final class BMHDChunk extends IFFChunk {
|
||||
pageHeight = pInput.readShort();
|
||||
}
|
||||
|
||||
void writeChunk(DataOutput pOutput) throws IOException {
|
||||
@Override
|
||||
void writeChunk(final DataOutput pOutput) throws IOException {
|
||||
pOutput.writeInt(chunkId);
|
||||
pOutput.writeInt(chunkLength);
|
||||
|
||||
@@ -167,6 +170,7 @@ final class BMHDChunk extends IFFChunk {
|
||||
pOutput.writeShort(pageHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString()
|
||||
+ " {w=" + width + ", h=" + height
|
||||
|
||||
+4
-3
@@ -32,7 +32,6 @@ package com.twelvemonkeys.imageio.plugins.iff;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* BODYChunk
|
||||
@@ -45,11 +44,13 @@ final class BODYChunk extends IFFChunk {
|
||||
super(IFF.CHUNK_BODY, pChunkLength);
|
||||
}
|
||||
|
||||
void readChunk(DataInput pInput) throws IOException {
|
||||
@Override
|
||||
void readChunk(final DataInput pInput) {
|
||||
throw new InternalError("BODY chunk should only be read from IFFImageReader");
|
||||
}
|
||||
|
||||
void writeChunk(DataOutput pOutput) throws IOException {
|
||||
@Override
|
||||
void writeChunk(final DataOutput pOutput) {
|
||||
throw new InternalError("BODY chunk should only be written from IFFImageWriter");
|
||||
}
|
||||
}
|
||||
|
||||
+8
-4
@@ -30,11 +30,12 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.iff;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
|
||||
/**
|
||||
* CAMGChunk
|
||||
*
|
||||
@@ -46,13 +47,14 @@ final class CAMGChunk extends IFFChunk {
|
||||
// #define CAMG_HAM 0x800 /* hold and modify */
|
||||
// #define CAMG_EHB 0x80 /* extra halfbrite */
|
||||
|
||||
private int camg;
|
||||
int camg;
|
||||
|
||||
public CAMGChunk(int pLength) {
|
||||
super(IFF.CHUNK_CAMG, pLength);
|
||||
}
|
||||
|
||||
void readChunk(DataInput pInput) throws IOException {
|
||||
@Override
|
||||
void readChunk(final DataInput pInput) throws IOException {
|
||||
if (chunkLength != 4) {
|
||||
throw new IIOException("Unknown CAMG chunk length: " + chunkLength);
|
||||
}
|
||||
@@ -60,7 +62,8 @@ final class CAMGChunk extends IFFChunk {
|
||||
camg = pInput.readInt();
|
||||
}
|
||||
|
||||
void writeChunk(DataOutput pOutput) throws IOException {
|
||||
@Override
|
||||
void writeChunk(final DataOutput pOutput) {
|
||||
throw new InternalError("Not implemented: writeChunk()");
|
||||
}
|
||||
|
||||
@@ -80,6 +83,7 @@ final class CAMGChunk extends IFFChunk {
|
||||
return (camg & 0x80) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + " {mode=" + (isHAM() ? "HAM" : isEHB() ? "EHB" : "Normal") + "}";
|
||||
}
|
||||
|
||||
+5
-1
@@ -30,7 +30,6 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.iff;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.IndexColorModel;
|
||||
import java.awt.image.WritableRaster;
|
||||
@@ -39,6 +38,8 @@ import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
|
||||
/**
|
||||
* CMAPChunk
|
||||
*
|
||||
@@ -68,6 +69,7 @@ final class CMAPChunk extends IFFChunk {
|
||||
model = pModel;
|
||||
}
|
||||
|
||||
@Override
|
||||
void readChunk(final DataInput pInput) throws IOException {
|
||||
int numColors = chunkLength / 3;
|
||||
|
||||
@@ -95,6 +97,7 @@ final class CMAPChunk extends IFFChunk {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void writeChunk(final DataOutput pOutput) throws IOException {
|
||||
pOutput.writeInt(chunkId);
|
||||
pOutput.writeInt(chunkLength);
|
||||
@@ -112,6 +115,7 @@ final class CMAPChunk extends IFFChunk {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + " {colorMap=" + model + "}";
|
||||
}
|
||||
|
||||
+8
-5
@@ -35,10 +35,10 @@ import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* UnknownChunk
|
||||
* GenericChunk
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @version $Id: UnknownChunk.java,v 1.0 28.feb.2006 00:53:47 haku Exp$
|
||||
* @version $Id: GenericChunk.java,v 1.0 28.feb.2006 00:53:47 haku Exp$
|
||||
*/
|
||||
final class GenericChunk extends IFFChunk {
|
||||
|
||||
@@ -46,7 +46,7 @@ final class GenericChunk extends IFFChunk {
|
||||
|
||||
protected GenericChunk(int pChunkId, int pChunkLength) {
|
||||
super(pChunkId, pChunkLength);
|
||||
data = new byte[pChunkLength <= 50 ? pChunkLength : 47];
|
||||
data = new byte[chunkLength];
|
||||
}
|
||||
|
||||
protected GenericChunk(int pChunkId, byte[] pChunkData) {
|
||||
@@ -54,13 +54,15 @@ final class GenericChunk extends IFFChunk {
|
||||
data = pChunkData;
|
||||
}
|
||||
|
||||
void readChunk(DataInput pInput) throws IOException {
|
||||
@Override
|
||||
void readChunk(final DataInput pInput) throws IOException {
|
||||
pInput.readFully(data, 0, data.length);
|
||||
|
||||
skipData(pInput, chunkLength, data.length);
|
||||
}
|
||||
|
||||
void writeChunk(DataOutput pOutput) throws IOException {
|
||||
@Override
|
||||
void writeChunk(final DataOutput pOutput) throws IOException {
|
||||
pOutput.writeInt(chunkId);
|
||||
pOutput.writeInt(chunkLength);
|
||||
pOutput.write(data, 0, data.length);
|
||||
@@ -70,6 +72,7 @@ final class GenericChunk extends IFFChunk {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + " {value=\""
|
||||
+ new String(data, 0, data.length <= 50 ? data.length : 47)
|
||||
|
||||
@@ -91,6 +91,12 @@ interface IFF {
|
||||
/** EA IFF 85 Generic Copyright text chunk */
|
||||
int CHUNK_COPY = ('(' << 24) + ('c' << 16) + (')' << 8) + ' ';
|
||||
|
||||
/** EA IFF 85 Generic annotation chunk (usually used for Software) */
|
||||
int CHUNK_ANNO = ('A' << 24) + ('N' << 16) + ('N' << 8) + 'O';;
|
||||
|
||||
/** Third-party defined UTF-8 text. */
|
||||
int CHUNK_UTF8 = ('U' << 24) + ('T' << 16) + ('F' << 8) + '8';
|
||||
|
||||
/** color cycling */
|
||||
int CHUNK_CRNG = ('C' << 24) + ('R' << 16) + ('N' << 8) + 'G';
|
||||
/** color cycling */
|
||||
|
||||
+269
@@ -0,0 +1,269 @@
|
||||
package com.twelvemonkeys.imageio.plugins.iff;
|
||||
|
||||
import com.twelvemonkeys.imageio.AbstractMetadata;
|
||||
|
||||
import javax.imageio.metadata.IIOMetadataNode;
|
||||
import java.awt.*;
|
||||
import java.awt.image.IndexColorModel;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
import static com.twelvemonkeys.imageio.plugins.iff.IFF.*;
|
||||
import static com.twelvemonkeys.lang.Validate.isTrue;
|
||||
import static com.twelvemonkeys.lang.Validate.notNull;
|
||||
|
||||
final class IFFImageMetadata extends AbstractMetadata {
|
||||
private final int formType;
|
||||
private final BMHDChunk header;
|
||||
private final IndexColorModel colorMap;
|
||||
private final CAMGChunk viewPort;
|
||||
private final List<GenericChunk> meta;
|
||||
|
||||
IFFImageMetadata(int formType, BMHDChunk header, IndexColorModel colorMap, CAMGChunk viewPort, List<GenericChunk> meta) {
|
||||
this.formType = isTrue(validFormType(formType), formType, "Unknown IFF Form type: %s");
|
||||
this.header = notNull(header, "header");
|
||||
this.colorMap = colorMap;
|
||||
this.viewPort = viewPort;
|
||||
this.meta = meta;
|
||||
}
|
||||
|
||||
private boolean validFormType(int formType) {
|
||||
switch (formType) {
|
||||
case TYPE_ACBM:
|
||||
case TYPE_DEEP:
|
||||
case TYPE_ILBM:
|
||||
case TYPE_PBM:
|
||||
case TYPE_RGB8:
|
||||
case TYPE_RGBN:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardChromaNode() {
|
||||
IIOMetadataNode chroma = new IIOMetadataNode("Chroma");
|
||||
|
||||
IIOMetadataNode csType = new IIOMetadataNode("ColorSpaceType");
|
||||
chroma.appendChild(csType);
|
||||
|
||||
switch (header.bitplanes) {
|
||||
case 8:
|
||||
if (colorMap == null) {
|
||||
csType.setAttribute("name", "GRAY");
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
case 24:
|
||||
case 32:
|
||||
csType.setAttribute("name", "RGB");
|
||||
break;
|
||||
default:
|
||||
csType.setAttribute("name", "Unknown");
|
||||
}
|
||||
|
||||
// NOTE: Channels in chroma node reflects channels in color model (see data node, for channels in data)
|
||||
IIOMetadataNode numChannels = new IIOMetadataNode("NumChannels");
|
||||
chroma.appendChild(numChannels);
|
||||
if (colorMap == null && header.bitplanes == 8) {
|
||||
numChannels.setAttribute("value", Integer.toString(1));
|
||||
}
|
||||
else if (header.bitplanes == 32) {
|
||||
numChannels.setAttribute("value", Integer.toString(4));
|
||||
}
|
||||
else {
|
||||
numChannels.setAttribute("value", Integer.toString(3));
|
||||
}
|
||||
|
||||
IIOMetadataNode blackIsZero = new IIOMetadataNode("BlackIsZero");
|
||||
chroma.appendChild(blackIsZero);
|
||||
blackIsZero.setAttribute("value", "TRUE");
|
||||
|
||||
// NOTE: TGA files may contain a color map, even if true color...
|
||||
// Not sure if this is a good idea to expose to the meta data,
|
||||
// as it might be unexpected... Then again...
|
||||
if (colorMap != null) {
|
||||
IIOMetadataNode palette = new IIOMetadataNode("Palette");
|
||||
chroma.appendChild(palette);
|
||||
|
||||
for (int i = 0; i < colorMap.getMapSize(); i++) {
|
||||
IIOMetadataNode paletteEntry = new IIOMetadataNode("PaletteEntry");
|
||||
palette.appendChild(paletteEntry);
|
||||
paletteEntry.setAttribute("index", Integer.toString(i));
|
||||
|
||||
paletteEntry.setAttribute("red", Integer.toString(colorMap.getRed(i)));
|
||||
paletteEntry.setAttribute("green", Integer.toString(colorMap.getGreen(i)));
|
||||
paletteEntry.setAttribute("blue", Integer.toString(colorMap.getBlue(i)));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Background color is the color of the transparent index in the color model?
|
||||
// if (extensions != null && extensions.getBackgroundColor() != 0) {
|
||||
// Color background = new Color(extensions.getBackgroundColor(), true);
|
||||
//
|
||||
// IIOMetadataNode backgroundColor = new IIOMetadataNode("BackgroundColor");
|
||||
// chroma.appendChild(backgroundColor);
|
||||
//
|
||||
// backgroundColor.setAttribute("red", Integer.toString(background.getRed()));
|
||||
// backgroundColor.setAttribute("green", Integer.toString(background.getGreen()));
|
||||
// backgroundColor.setAttribute("blue", Integer.toString(background.getBlue()));
|
||||
// }
|
||||
|
||||
return chroma;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardCompressionNode() {
|
||||
if (header.compressionType == BMHDChunk.COMPRESSION_NONE) {
|
||||
return null; // All defaults
|
||||
}
|
||||
|
||||
IIOMetadataNode node = new IIOMetadataNode("Compression");
|
||||
|
||||
IIOMetadataNode compressionTypeName = new IIOMetadataNode("CompressionTypeName");
|
||||
compressionTypeName.setAttribute("value", "RLE");
|
||||
node.appendChild(compressionTypeName);
|
||||
|
||||
IIOMetadataNode lossless = new IIOMetadataNode("Lossless");
|
||||
lossless.setAttribute("value", "TRUE");
|
||||
node.appendChild(lossless);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardDataNode() {
|
||||
IIOMetadataNode data = new IIOMetadataNode("Data");
|
||||
|
||||
// PlanarConfiguration
|
||||
IIOMetadataNode planarConfiguration = new IIOMetadataNode("PlanarConfiguration");
|
||||
switch (formType) {
|
||||
case TYPE_PBM:
|
||||
planarConfiguration.setAttribute("value", "PixelInterleaved");
|
||||
break;
|
||||
case TYPE_ILBM:
|
||||
planarConfiguration.setAttribute("value", "PlaneInterleaved");
|
||||
break;
|
||||
default:
|
||||
planarConfiguration.setAttribute("value", "Unknown " + IFFUtil.toChunkStr(formType));
|
||||
break;
|
||||
}
|
||||
data.appendChild(planarConfiguration);
|
||||
|
||||
IIOMetadataNode sampleFormat = new IIOMetadataNode("SampleFormat");
|
||||
sampleFormat.setAttribute("value", colorMap != null ? "Index" : "UnsignedIntegral");
|
||||
data.appendChild(sampleFormat);
|
||||
|
||||
// BitsPerSample
|
||||
IIOMetadataNode bitsPerSample = new IIOMetadataNode("BitsPerSample");
|
||||
String value = bitsPerSampleValue(header.bitplanes);
|
||||
bitsPerSample.setAttribute("value", value);
|
||||
data.appendChild(bitsPerSample);
|
||||
|
||||
// SignificantBitsPerSample not in format
|
||||
// SampleMSB not in format
|
||||
|
||||
return data;
|
||||
|
||||
}
|
||||
|
||||
private String bitsPerSampleValue(int bitplanes) {
|
||||
switch (bitplanes) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
case 8:
|
||||
return Integer.toString(bitplanes);
|
||||
case 24:
|
||||
return "8 8 8";
|
||||
case 32:
|
||||
return "8 8 8 8";
|
||||
default:
|
||||
throw new IllegalArgumentException("Ubknown bit count: " + bitplanes);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardDimensionNode() {
|
||||
if (viewPort == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
IIOMetadataNode dimension = new IIOMetadataNode("Dimension");
|
||||
|
||||
// PixelAspectRatio
|
||||
IIOMetadataNode pixelAspectRatio = new IIOMetadataNode("PixelAspectRatio");
|
||||
pixelAspectRatio.setAttribute("value", String.valueOf((viewPort.isHires() ? 2f : 1f) / (viewPort.isLaced() ? 2f : 1f)));
|
||||
dimension.appendChild(pixelAspectRatio);
|
||||
|
||||
// TODO: HorizontalScreenSize?
|
||||
// TODO: VerticalScreenSize?
|
||||
|
||||
return dimension;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardDocumentNode() {
|
||||
IIOMetadataNode document = new IIOMetadataNode("Document");
|
||||
|
||||
IIOMetadataNode formatVersion = new IIOMetadataNode("FormatVersion");
|
||||
document.appendChild(formatVersion);
|
||||
formatVersion.setAttribute("value", "1.0");
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardTextNode() {
|
||||
if (meta.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
IIOMetadataNode text = new IIOMetadataNode("Text");
|
||||
|
||||
// /Text/TextEntry@keyword = field name, /Text/TextEntry@value = field value.
|
||||
for (GenericChunk chunk : meta) {
|
||||
IIOMetadataNode node = new IIOMetadataNode("TextEntry");
|
||||
node.setAttribute("keyword", IFFUtil.toChunkStr(chunk.chunkId));
|
||||
node.setAttribute("value", new String(chunk.data, chunk.chunkId == IFF.CHUNK_UTF8 ? StandardCharsets.UTF_8 : StandardCharsets.US_ASCII));
|
||||
text.appendChild(node);
|
||||
}
|
||||
|
||||
return text;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardTransparencyNode() {
|
||||
if ((colorMap == null || !colorMap.hasAlpha()) && header.bitplanes != 32) {
|
||||
return null;
|
||||
}
|
||||
|
||||
IIOMetadataNode transparency = new IIOMetadataNode("Transparency");
|
||||
|
||||
if (header.bitplanes == 32) {
|
||||
IIOMetadataNode alpha = new IIOMetadataNode("Alpha");
|
||||
alpha.setAttribute("value", "nonpremultiplied");
|
||||
transparency.appendChild(alpha);
|
||||
}
|
||||
|
||||
if (colorMap != null && colorMap.getTransparency() == Transparency.BITMASK) {
|
||||
IIOMetadataNode transparentIndex = new IIOMetadataNode("TransparentIndex");
|
||||
transparentIndex.setAttribute("value", Integer.toString(colorMap.getTransparentPixel()));
|
||||
transparency.appendChild(transparentIndex);
|
||||
}
|
||||
|
||||
return transparency;
|
||||
}
|
||||
}
|
||||
+38
-21
@@ -32,13 +32,13 @@ package com.twelvemonkeys.imageio.plugins.iff;
|
||||
|
||||
import com.twelvemonkeys.image.ResampleOp;
|
||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||
import com.twelvemonkeys.imageio.stream.BufferedImageInputStream;
|
||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
||||
import com.twelvemonkeys.io.enc.DecoderStream;
|
||||
import com.twelvemonkeys.io.enc.PackBitsDecoder;
|
||||
|
||||
import javax.imageio.*;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.*;
|
||||
@@ -47,6 +47,7 @@ import java.awt.image.*;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@@ -96,7 +97,7 @@ import java.util.List;
|
||||
* @see <a href="http://en.wikipedia.org/wiki/Interchange_File_Format">Wikipedia: IFF</a>
|
||||
* @see <a href="http://en.wikipedia.org/wiki/ILBM">Wikipedia: IFF ILBM</a>
|
||||
*/
|
||||
public class IFFImageReader extends ImageReaderBase {
|
||||
public final class IFFImageReader extends ImageReaderBase {
|
||||
// http://home.comcast.net/~erniew/lwsdk/docs/filefmts/ilbm.html
|
||||
// http://www.fileformat.info/format/iff/spec/7866a9f0e53c42309af667c5da3bd426/view.htm
|
||||
// - Contains definitions of some "new" chunks, as well as alternative FORM types
|
||||
@@ -111,17 +112,14 @@ public class IFFImageReader extends ImageReaderBase {
|
||||
private GRABChunk grab;
|
||||
private CAMGChunk viewPort;
|
||||
private MultiPalette paletteChange;
|
||||
private final List<GenericChunk> meta = new ArrayList<>();
|
||||
private int formType;
|
||||
private long bodyStart;
|
||||
|
||||
private BufferedImage image;
|
||||
private DataInputStream byteRunStream;
|
||||
|
||||
public IFFImageReader() {
|
||||
super(new IFFImageReaderSpi());
|
||||
}
|
||||
|
||||
protected IFFImageReader(ImageReaderSpi pProvider) {
|
||||
IFFImageReader(ImageReaderSpi pProvider) {
|
||||
super(pProvider);
|
||||
}
|
||||
|
||||
@@ -133,6 +131,7 @@ public class IFFImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resetMembers() {
|
||||
header = null;
|
||||
colorMap = null;
|
||||
@@ -140,6 +139,7 @@ public class IFFImageReader extends ImageReaderBase {
|
||||
body = null;
|
||||
viewPort = null;
|
||||
formType = 0;
|
||||
meta.clear();
|
||||
|
||||
image = null;
|
||||
byteRunStream = null;
|
||||
@@ -258,11 +258,6 @@ public class IFFImageReader extends ImageReaderBase {
|
||||
// System.out.println(ctbl);
|
||||
break;
|
||||
|
||||
case IFF.CHUNK_JUNK:
|
||||
// Always skip junk chunks
|
||||
IFFChunk.skipData(imageInput, length, 0);
|
||||
break;
|
||||
|
||||
case IFF.CHUNK_BODY:
|
||||
if (body != null) {
|
||||
throw new IIOException("Multiple BODY chunks not allowed");
|
||||
@@ -274,18 +269,32 @@ public class IFFImageReader extends ImageReaderBase {
|
||||
// NOTE: We don't read the body here, it's done later in the read(int, ImageReadParam) method
|
||||
// Done reading meta
|
||||
return;
|
||||
default:
|
||||
// TODO: We probably want to store ANNO, TEXT, AUTH, COPY etc chunks as Metadata
|
||||
// SHAM, ANNO, DEST, SPRT and more
|
||||
IFFChunk generic = new GenericChunk(chunkId, length);
|
||||
|
||||
case IFF.CHUNK_ANNO:
|
||||
case IFF.CHUNK_AUTH:
|
||||
case IFF.CHUNK_COPY:
|
||||
case IFF.CHUNK_NAME:
|
||||
case IFF.CHUNK_TEXT:
|
||||
case IFF.CHUNK_UTF8:
|
||||
GenericChunk generic = new GenericChunk(chunkId, length);
|
||||
generic.readChunk(imageInput);
|
||||
meta.add(generic);
|
||||
|
||||
// System.out.println(generic);
|
||||
break;
|
||||
|
||||
case IFF.CHUNK_JUNK:
|
||||
// Always skip junk chunks
|
||||
default:
|
||||
// TODO: SHAM, DEST, SPRT and more
|
||||
// Everything else, we'll just skip
|
||||
IFFChunk.skipData(imageInput, length, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedImage read(int pIndex, ImageReadParam pParam) throws IOException {
|
||||
init(pIndex);
|
||||
|
||||
@@ -314,16 +323,26 @@ public class IFFImageReader extends ImageReaderBase {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth(int pIndex) throws IOException {
|
||||
init(pIndex);
|
||||
return header.width;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight(int pIndex) throws IOException {
|
||||
init(pIndex);
|
||||
return header.height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IIOMetadata getImageMetadata(int imageIndex) throws IOException {
|
||||
init(imageIndex);
|
||||
|
||||
return new IFFImageMetadata(formType, header, colorMap != null ? colorMap.getIndexColorModel(header, isEHB()) : null, viewPort, meta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<ImageTypeSpecifier> getImageTypes(int pIndex) throws IOException {
|
||||
init(pIndex);
|
||||
|
||||
@@ -363,12 +382,11 @@ public class IFFImageReader extends ImageReaderBase {
|
||||
if (colorMap != null) {
|
||||
IndexColorModel cm = colorMap.getIndexColorModel(header, isEHB());
|
||||
specifier = ImageTypeSpecifiers.createFromIndexColorModel(cm);
|
||||
break;
|
||||
}
|
||||
else {
|
||||
specifier = ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// NOTE: HAM modes falls through, as they are converted to RGB
|
||||
case 24:
|
||||
@@ -786,7 +804,7 @@ public class IFFImageReader extends ImageReaderBase {
|
||||
}
|
||||
|
||||
public static void main(String[] pArgs) throws IOException {
|
||||
ImageReader reader = new IFFImageReader();
|
||||
ImageReader reader = new IFFImageReader(new IFFImageReaderSpi());
|
||||
|
||||
boolean scale = false;
|
||||
for (String arg : pArgs) {
|
||||
@@ -800,8 +818,7 @@ public class IFFImageReader extends ImageReaderBase {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
ImageInputStream input = new BufferedImageInputStream(ImageIO.createImageInputStream(file));
|
||||
try (ImageInputStream input = ImageIO.createImageInputStream(file)) {
|
||||
boolean canRead = reader.getOriginatingProvider().canDecodeInput(input);
|
||||
|
||||
if (canRead) {
|
||||
|
||||
+7
-3
@@ -30,12 +30,13 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.iff;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
|
||||
|
||||
/**
|
||||
* IFFImageReaderSpi
|
||||
@@ -52,6 +53,7 @@ public final class IFFImageReaderSpi extends ImageReaderSpiBase {
|
||||
super(new IFFProviderInfo());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canDecodeInput(Object pSource) throws IOException {
|
||||
return pSource instanceof ImageInputStream && canDecode((ImageInputStream) pSource);
|
||||
}
|
||||
@@ -80,10 +82,12 @@ public final class IFFImageReaderSpi extends ImageReaderSpiBase {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageReader createReaderInstance(Object pExtension) throws IOException {
|
||||
return new IFFImageReader(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription(Locale pLocale) {
|
||||
return "Commodore Amiga/Electronic Arts Image Interchange Format (IFF) image reader";
|
||||
}
|
||||
|
||||
+26
-22
@@ -30,22 +30,31 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.iff;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ColorModel;
|
||||
import java.awt.image.IndexColorModel;
|
||||
import java.awt.image.Raster;
|
||||
import java.awt.image.RenderedImage;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import javax.imageio.IIOImage;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.ImageWriteParam;
|
||||
import javax.imageio.ImageWriter;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.spi.ImageWriterSpi;
|
||||
|
||||
import com.twelvemonkeys.imageio.ImageWriterBase;
|
||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||
import com.twelvemonkeys.io.FastByteArrayOutputStream;
|
||||
import com.twelvemonkeys.io.enc.EncoderStream;
|
||||
import com.twelvemonkeys.io.enc.PackBitsEncoder;
|
||||
|
||||
import javax.imageio.*;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.spi.ImageWriterSpi;
|
||||
import java.awt.*;
|
||||
import java.awt.image.*;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Writer for Commodore Amiga (Electronic Arts) IFF ILBM (InterLeaved BitMap) format.
|
||||
* The IFF format (Interchange File Format) is the standard file format
|
||||
@@ -57,24 +66,23 @@ import java.io.OutputStream;
|
||||
* @see <a href="http://en.wikipedia.org/wiki/Interchange_File_Format">Wikipedia: IFF</a>
|
||||
* @see <a href="http://en.wikipedia.org/wiki/ILBM">Wikipedia: IFF ILBM</a>
|
||||
*/
|
||||
public class IFFImageWriter extends ImageWriterBase {
|
||||
public final class IFFImageWriter extends ImageWriterBase {
|
||||
|
||||
public IFFImageWriter() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
protected IFFImageWriter(ImageWriterSpi pProvider) {
|
||||
IFFImageWriter(ImageWriterSpi pProvider) {
|
||||
super(pProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IIOMetadata getDefaultImageMetadata(ImageTypeSpecifier imageType, ImageWriteParam param) {
|
||||
throw new UnsupportedOperationException("Method getDefaultImageMetadata not implemented");// TODO: Implement
|
||||
}
|
||||
|
||||
@Override
|
||||
public IIOMetadata convertImageMetadata(IIOMetadata inData, ImageTypeSpecifier imageType, ImageWriteParam param) {
|
||||
throw new UnsupportedOperationException("Method convertImageMetadata not implemented");// TODO: Implement
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(IIOMetadata pStreamMetadata, IIOImage pImage, ImageWriteParam pParam) throws IOException {
|
||||
assertOutput();
|
||||
|
||||
@@ -105,13 +113,9 @@ public class IFFImageWriter extends ImageWriterBase {
|
||||
|
||||
// NOTE: This is much faster than imageOutput.write(pImageData.toByteArray())
|
||||
// as the data array is not duplicated
|
||||
OutputStream adapter = IIOUtil.createStreamAdapter(imageOutput);
|
||||
try {
|
||||
try (OutputStream adapter = IIOUtil.createStreamAdapter(imageOutput)) {
|
||||
pImageData.writeTo(adapter);
|
||||
}
|
||||
finally {
|
||||
adapter.close();
|
||||
}
|
||||
|
||||
if (pImageData.size() % 2 == 0) {
|
||||
imageOutput.writeByte(0); // PAD
|
||||
@@ -180,7 +184,7 @@ public class IFFImageWriter extends ImageWriterBase {
|
||||
|
||||
private void writeMeta(RenderedImage pImage, int pBodyLength) throws IOException {
|
||||
// Annotation ANNO chunk, 8 + annoData.length bytes
|
||||
String annotation = "Written by " + getOriginatingProvider().getDescription(null) + " by " + getOriginatingProvider().getVendorName();
|
||||
String annotation = String.format("Written by %s IFFImageWriter %s", getOriginatingProvider().getVendorName(), getOriginatingProvider().getVersion());
|
||||
GenericChunk anno = new GenericChunk(IFFUtil.toInt("ANNO".getBytes()), annotation.getBytes());
|
||||
|
||||
ColorModel cm = pImage.getColorModel();
|
||||
|
||||
+6
-3
@@ -30,12 +30,13 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.iff;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ImageWriterSpiBase;
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.ImageWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ImageWriterSpiBase;
|
||||
|
||||
/**
|
||||
* IFFImageWriterSpi
|
||||
@@ -58,10 +59,12 @@ public class IFFImageWriterSpi extends ImageWriterSpiBase {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageWriter createWriterInstance(Object pExtension) throws IOException {
|
||||
return new IFFImageWriter(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription(Locale pLocale) {
|
||||
return "Commodore Amiga/Electronic Arts Image Interchange Format (IFF) image writer";
|
||||
}
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ package com.twelvemonkeys.imageio.plugins.iff;
|
||||
* @author Harald Kuhr (Java port)
|
||||
* @version $Id: IFFUtil.java,v 1.0 06.mar.2006 13:31:35 haku Exp$
|
||||
*/
|
||||
class IFFUtil {
|
||||
final class IFFUtil {
|
||||
|
||||
/**
|
||||
* Creates a rotation table
|
||||
|
||||
+511
@@ -0,0 +1,511 @@
|
||||
package com.twelvemonkeys.imageio.plugins.iff;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.awt.image.IndexColorModel;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.imageio.metadata.IIOMetadataFormatImpl;
|
||||
import javax.imageio.metadata.IIOMetadataNode;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.function.ThrowingRunnable;
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
public class IFFImageMetadataTest {
|
||||
@Test
|
||||
public void testStandardFeatures() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
|
||||
final IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, null, Collections.<GenericChunk>emptyList());
|
||||
|
||||
// Standard metadata format
|
||||
assertTrue(metadata.isStandardMetadataFormatSupported());
|
||||
Node root = metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName);
|
||||
assertNotNull(root);
|
||||
assertTrue(root instanceof IIOMetadataNode);
|
||||
|
||||
// Other formats
|
||||
assertNull(metadata.getNativeMetadataFormatName());
|
||||
assertNull(metadata.getExtraMetadataFormatNames());
|
||||
assertThrows(IllegalArgumentException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
metadata.getAsTree("com_foo_bar_1.0");
|
||||
}
|
||||
});
|
||||
|
||||
// Read-only
|
||||
assertTrue(metadata.isReadOnly());
|
||||
assertThrows(IllegalStateException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
metadata.mergeTree(IIOMetadataFormatImpl.standardMetadataFormatName, new IIOMetadataNode(IIOMetadataFormatImpl.standardMetadataFormatName));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardChromaGray() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, null, Collections.<GenericChunk>emptyList());
|
||||
|
||||
IIOMetadataNode chroma = metadata.getStandardChromaNode();
|
||||
assertNotNull(chroma);
|
||||
assertEquals("Chroma", chroma.getNodeName());
|
||||
assertEquals(3, chroma.getLength());
|
||||
|
||||
IIOMetadataNode colorSpaceType = (IIOMetadataNode) chroma.getFirstChild();
|
||||
assertEquals("ColorSpaceType", colorSpaceType.getNodeName());
|
||||
assertEquals("GRAY", colorSpaceType.getAttribute("name"));
|
||||
|
||||
IIOMetadataNode numChannels = (IIOMetadataNode) colorSpaceType.getNextSibling();
|
||||
assertEquals("NumChannels", numChannels.getNodeName());
|
||||
assertEquals("1", numChannels.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode blackIsZero = (IIOMetadataNode) numChannels.getNextSibling();
|
||||
assertEquals("BlackIsZero", blackIsZero.getNodeName());
|
||||
assertEquals("TRUE", blackIsZero.getAttribute("value"));
|
||||
|
||||
assertNull(blackIsZero.getNextSibling()); // No more children
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardChromaRGB() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, null, Collections.<GenericChunk>emptyList());
|
||||
|
||||
IIOMetadataNode chroma = metadata.getStandardChromaNode();
|
||||
assertNotNull(chroma);
|
||||
assertEquals("Chroma", chroma.getNodeName());
|
||||
assertEquals(3, chroma.getLength());
|
||||
|
||||
IIOMetadataNode colorSpaceType = (IIOMetadataNode) chroma.getFirstChild();
|
||||
assertEquals("ColorSpaceType", colorSpaceType.getNodeName());
|
||||
assertEquals("RGB", colorSpaceType.getAttribute("name"));
|
||||
|
||||
IIOMetadataNode numChannels = (IIOMetadataNode) colorSpaceType.getNextSibling();
|
||||
assertEquals("NumChannels", numChannels.getNodeName());
|
||||
assertEquals("3", numChannels.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode blackIsZero = (IIOMetadataNode) numChannels.getNextSibling();
|
||||
assertEquals("BlackIsZero", blackIsZero.getNodeName());
|
||||
assertEquals("TRUE", blackIsZero.getAttribute("value"));
|
||||
|
||||
assertNull(blackIsZero.getNextSibling()); // No more children
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardChromaPalette() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 1, BMHDChunk.MASK_TRANSPARENT_COLOR, BMHDChunk.COMPRESSION_BYTE_RUN, 1);
|
||||
|
||||
byte[] bw = {0, (byte) 0xff};
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, new IndexColorModel(header.bitplanes, bw.length, bw, bw, bw, header.transparentIndex), null, Collections.<GenericChunk>emptyList());
|
||||
|
||||
IIOMetadataNode chroma = metadata.getStandardChromaNode();
|
||||
assertNotNull(chroma);
|
||||
assertEquals("Chroma", chroma.getNodeName());
|
||||
assertEquals(4, chroma.getLength());
|
||||
|
||||
IIOMetadataNode colorSpaceType = (IIOMetadataNode) chroma.getFirstChild();
|
||||
assertEquals("ColorSpaceType", colorSpaceType.getNodeName());
|
||||
assertEquals("RGB", colorSpaceType.getAttribute("name"));
|
||||
|
||||
IIOMetadataNode numChannels = (IIOMetadataNode) colorSpaceType.getNextSibling();
|
||||
assertEquals("NumChannels", numChannels.getNodeName());
|
||||
assertEquals("3", numChannels.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode blackIsZero = (IIOMetadataNode) numChannels.getNextSibling();
|
||||
assertEquals("BlackIsZero", blackIsZero.getNodeName());
|
||||
assertEquals("TRUE", blackIsZero.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode palette = (IIOMetadataNode) blackIsZero.getNextSibling();
|
||||
assertEquals("Palette", palette.getNodeName());
|
||||
assertEquals(bw.length, palette.getLength());
|
||||
|
||||
for (int i = 0; i < palette.getLength(); i++) {
|
||||
IIOMetadataNode item0 = (IIOMetadataNode) palette.item(i);
|
||||
assertEquals("PaletteEntry", item0.getNodeName());
|
||||
assertEquals(String.valueOf(i), item0.getAttribute("index"));
|
||||
String rgb = String.valueOf(bw[i] & 0xff);
|
||||
assertEquals(rgb, item0.getAttribute("red"));
|
||||
assertEquals(rgb, item0.getAttribute("green"));
|
||||
assertEquals(rgb, item0.getAttribute("blue"));
|
||||
}
|
||||
|
||||
// TODO: BackgroundIndex == 1??
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardCompressionRLE() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, null, Collections.<GenericChunk>emptyList());
|
||||
|
||||
IIOMetadataNode compression = metadata.getStandardCompressionNode();
|
||||
assertNotNull(compression);
|
||||
assertEquals("Compression", compression.getNodeName());
|
||||
assertEquals(2, compression.getLength());
|
||||
|
||||
IIOMetadataNode compressionTypeName = (IIOMetadataNode) compression.getFirstChild();
|
||||
assertEquals("CompressionTypeName", compressionTypeName.getNodeName());
|
||||
assertEquals("RLE", compressionTypeName.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode lossless = (IIOMetadataNode) compressionTypeName.getNextSibling();
|
||||
assertEquals("Lossless", lossless.getNodeName());
|
||||
assertEquals("TRUE", lossless.getAttribute("value"));
|
||||
|
||||
assertNull(lossless.getNextSibling()); // No more children
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardCompressionNone() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_NONE, 0);
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, null, Collections.<GenericChunk>emptyList());
|
||||
|
||||
assertNull(metadata.getStandardCompressionNode()); // No compression, all default...
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardDataILBM_Gray() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, null, Collections.<GenericChunk>emptyList());
|
||||
|
||||
IIOMetadataNode data = metadata.getStandardDataNode();
|
||||
assertNotNull(data);
|
||||
assertEquals("Data", data.getNodeName());
|
||||
assertEquals(3, data.getLength());
|
||||
|
||||
IIOMetadataNode planarConfiguration = (IIOMetadataNode) data.getFirstChild();
|
||||
assertEquals("PlanarConfiguration", planarConfiguration.getNodeName());
|
||||
assertEquals("PlaneInterleaved", planarConfiguration.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode sampleFomat = (IIOMetadataNode) planarConfiguration.getNextSibling();
|
||||
assertEquals("SampleFormat", sampleFomat.getNodeName());
|
||||
assertEquals("UnsignedIntegral", sampleFomat.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode bitsPerSample = (IIOMetadataNode) sampleFomat.getNextSibling();
|
||||
assertEquals("BitsPerSample", bitsPerSample.getNodeName());
|
||||
assertEquals("8", bitsPerSample.getAttribute("value"));
|
||||
|
||||
assertNull(bitsPerSample.getNextSibling()); // No more children
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardDataILBM_RGB() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, null, Collections.<GenericChunk>emptyList());
|
||||
|
||||
IIOMetadataNode data = metadata.getStandardDataNode();
|
||||
assertNotNull(data);
|
||||
assertEquals("Data", data.getNodeName());
|
||||
assertEquals(3, data.getLength());
|
||||
|
||||
IIOMetadataNode planarConfiguration = (IIOMetadataNode) data.getFirstChild();
|
||||
assertEquals("PlanarConfiguration", planarConfiguration.getNodeName());
|
||||
assertEquals("PlaneInterleaved", planarConfiguration.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode sampleFomat = (IIOMetadataNode) planarConfiguration.getNextSibling();
|
||||
assertEquals("SampleFormat", sampleFomat.getNodeName());
|
||||
assertEquals("UnsignedIntegral", sampleFomat.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode bitsPerSample = (IIOMetadataNode) sampleFomat.getNextSibling();
|
||||
assertEquals("BitsPerSample", bitsPerSample.getNodeName());
|
||||
assertEquals("8 8 8", bitsPerSample.getAttribute("value"));
|
||||
|
||||
assertNull(bitsPerSample.getNextSibling()); // No more children
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardDataILBM_RGBA() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 32, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, null, Collections.<GenericChunk>emptyList());
|
||||
|
||||
IIOMetadataNode data = metadata.getStandardDataNode();
|
||||
assertNotNull(data);
|
||||
assertEquals("Data", data.getNodeName());
|
||||
assertEquals(3, data.getLength());
|
||||
|
||||
IIOMetadataNode planarConfiguration = (IIOMetadataNode) data.getFirstChild();
|
||||
assertEquals("PlanarConfiguration", planarConfiguration.getNodeName());
|
||||
assertEquals("PlaneInterleaved", planarConfiguration.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode sampleFomat = (IIOMetadataNode) planarConfiguration.getNextSibling();
|
||||
assertEquals("SampleFormat", sampleFomat.getNodeName());
|
||||
assertEquals("UnsignedIntegral", sampleFomat.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode bitsPerSample = (IIOMetadataNode) sampleFomat.getNextSibling();
|
||||
assertEquals("BitsPerSample", bitsPerSample.getNodeName());
|
||||
assertEquals("8 8 8 8", bitsPerSample.getAttribute("value"));
|
||||
|
||||
assertNull(bitsPerSample.getNextSibling()); // No more children
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardDataILBM_Palette() {
|
||||
for (int i = 1; i <= 8; i++) {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, i, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
|
||||
byte[] rgb = new byte[2 << i]; // Colors doesn't really matter here
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, new IndexColorModel(header.bitplanes, rgb.length, rgb, rgb, rgb, 0), null, Collections.<GenericChunk>emptyList());
|
||||
|
||||
IIOMetadataNode data = metadata.getStandardDataNode();
|
||||
assertNotNull(data);
|
||||
assertEquals("Data", data.getNodeName());
|
||||
assertEquals(3, data.getLength());
|
||||
|
||||
IIOMetadataNode planarConfiguration = (IIOMetadataNode) data.getFirstChild();
|
||||
assertEquals("PlanarConfiguration", planarConfiguration.getNodeName());
|
||||
assertEquals("PlaneInterleaved", planarConfiguration.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode sampleFomat = (IIOMetadataNode) planarConfiguration.getNextSibling();
|
||||
assertEquals("SampleFormat", sampleFomat.getNodeName());
|
||||
assertEquals("Index", sampleFomat.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode bitsPerSample = (IIOMetadataNode) sampleFomat.getNextSibling();
|
||||
assertEquals("BitsPerSample", bitsPerSample.getNodeName());
|
||||
assertEquals(String.valueOf(i), bitsPerSample.getAttribute("value"));
|
||||
|
||||
assertNull(bitsPerSample.getNextSibling()); // No more children
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardDataPBM_Gray() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_PBM, header, null, null, Collections.<GenericChunk>emptyList());
|
||||
|
||||
IIOMetadataNode data = metadata.getStandardDataNode();
|
||||
assertNotNull(data);
|
||||
assertEquals("Data", data.getNodeName());
|
||||
assertEquals(3, data.getLength());
|
||||
|
||||
IIOMetadataNode planarConfiguration = (IIOMetadataNode) data.getFirstChild();
|
||||
assertEquals("PlanarConfiguration", planarConfiguration.getNodeName());
|
||||
assertEquals("PixelInterleaved", planarConfiguration.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode sampleFomat = (IIOMetadataNode) planarConfiguration.getNextSibling();
|
||||
assertEquals("SampleFormat", sampleFomat.getNodeName());
|
||||
assertEquals("UnsignedIntegral", sampleFomat.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode bitsPerSample = (IIOMetadataNode) sampleFomat.getNextSibling();
|
||||
assertEquals("BitsPerSample", bitsPerSample.getNodeName());
|
||||
assertEquals("8", bitsPerSample.getAttribute("value"));
|
||||
|
||||
assertNull(bitsPerSample.getNextSibling()); // No more children
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardDataPBM_RGB() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_PBM, header, null, null, Collections.<GenericChunk>emptyList());
|
||||
|
||||
IIOMetadataNode data = metadata.getStandardDataNode();
|
||||
assertNotNull(data);
|
||||
assertEquals("Data", data.getNodeName());
|
||||
assertEquals(3, data.getLength());
|
||||
|
||||
IIOMetadataNode planarConfiguration = (IIOMetadataNode) data.getFirstChild();
|
||||
assertEquals("PlanarConfiguration", planarConfiguration.getNodeName());
|
||||
assertEquals("PixelInterleaved", planarConfiguration.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode sampleFomat = (IIOMetadataNode) planarConfiguration.getNextSibling();
|
||||
assertEquals("SampleFormat", sampleFomat.getNodeName());
|
||||
assertEquals("UnsignedIntegral", sampleFomat.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode bitsPerSample = (IIOMetadataNode) sampleFomat.getNextSibling();
|
||||
assertEquals("BitsPerSample", bitsPerSample.getNodeName());
|
||||
assertEquals("8 8 8", bitsPerSample.getAttribute("value"));
|
||||
|
||||
assertNull(bitsPerSample.getNextSibling()); // No more children
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testStandardDimensionNoViewport() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, null, Collections.<GenericChunk>emptyList());
|
||||
|
||||
IIOMetadataNode dimension = metadata.getStandardDimensionNode();
|
||||
assertNull(dimension);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardDimensionNormal() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, new CAMGChunk(4), Collections.<GenericChunk>emptyList());
|
||||
|
||||
IIOMetadataNode dimension = metadata.getStandardDimensionNode();
|
||||
assertNotNull(dimension);
|
||||
assertEquals("Dimension", dimension.getNodeName());
|
||||
assertEquals(1, dimension.getLength());
|
||||
|
||||
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) dimension.getFirstChild();
|
||||
assertEquals("PixelAspectRatio", pixelAspectRatio.getNodeName());
|
||||
assertEquals("1.0", pixelAspectRatio.getAttribute("value"));
|
||||
|
||||
assertNull(pixelAspectRatio.getNextSibling()); // No more children
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardDimensionHires() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
CAMGChunk viewPort = new CAMGChunk(4);
|
||||
viewPort.camg = 0x8000;
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, viewPort, Collections.<GenericChunk>emptyList());
|
||||
|
||||
IIOMetadataNode dimension = metadata.getStandardDimensionNode();
|
||||
assertNotNull(dimension);
|
||||
assertEquals("Dimension", dimension.getNodeName());
|
||||
assertEquals(1, dimension.getLength());
|
||||
|
||||
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) dimension.getFirstChild();
|
||||
assertEquals("PixelAspectRatio", pixelAspectRatio.getNodeName());
|
||||
assertEquals("2.0", pixelAspectRatio.getAttribute("value"));
|
||||
|
||||
assertNull(pixelAspectRatio.getNextSibling()); // No more children
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardDimensionInterlaced() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
|
||||
CAMGChunk viewPort = new CAMGChunk(4);
|
||||
viewPort.camg = 0x4;
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, viewPort, Collections.<GenericChunk>emptyList());
|
||||
|
||||
IIOMetadataNode dimension = metadata.getStandardDimensionNode();
|
||||
assertNotNull(dimension);
|
||||
assertEquals("Dimension", dimension.getNodeName());
|
||||
assertEquals(1, dimension.getLength());
|
||||
|
||||
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) dimension.getFirstChild();
|
||||
assertEquals("PixelAspectRatio", pixelAspectRatio.getNodeName());
|
||||
assertEquals("0.5", pixelAspectRatio.getAttribute("value"));
|
||||
|
||||
assertNull(pixelAspectRatio.getNextSibling()); // No more children
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardDimensionHiresInterlaced() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
|
||||
CAMGChunk viewPort = new CAMGChunk(4);
|
||||
viewPort.camg = 0x8004;
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, viewPort, Collections.<GenericChunk>emptyList());
|
||||
|
||||
IIOMetadataNode dimension = metadata.getStandardDimensionNode();
|
||||
assertNotNull(dimension);
|
||||
assertEquals("Dimension", dimension.getNodeName());
|
||||
assertEquals(1, dimension.getLength());
|
||||
|
||||
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) dimension.getFirstChild();
|
||||
assertEquals("PixelAspectRatio", pixelAspectRatio.getNodeName());
|
||||
assertEquals("1.0", pixelAspectRatio.getAttribute("value"));
|
||||
|
||||
assertNull(pixelAspectRatio.getNextSibling()); // No more children
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardDocument() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, null, Collections.<GenericChunk>emptyList());
|
||||
|
||||
IIOMetadataNode document = metadata.getStandardDocumentNode();
|
||||
assertNotNull(document);
|
||||
assertEquals("Document", document.getNodeName());
|
||||
assertEquals(1, document.getLength());
|
||||
|
||||
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) document.getFirstChild();
|
||||
assertEquals("FormatVersion", pixelAspectRatio.getNodeName());
|
||||
assertEquals("1.0", pixelAspectRatio.getAttribute("value"));
|
||||
|
||||
assertNull(pixelAspectRatio.getNextSibling()); // No more children
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardText() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
|
||||
String[] texts = {"annotation", "äñnótâtïøñ"};
|
||||
List<GenericChunk> meta = Arrays.asList(new GenericChunk(IFF.CHUNK_ANNO, texts[0].getBytes(StandardCharsets.US_ASCII)),
|
||||
new GenericChunk(IFF.CHUNK_UTF8, texts[1].getBytes(StandardCharsets.UTF_8)));
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, null, meta);
|
||||
|
||||
IIOMetadataNode text = metadata.getStandardTextNode();
|
||||
assertNotNull(text);
|
||||
assertEquals("Text", text.getNodeName());
|
||||
assertEquals(texts.length, text.getLength());
|
||||
|
||||
for (int i = 0; i < texts.length; i++) {
|
||||
IIOMetadataNode textEntry = (IIOMetadataNode) text.item(i);
|
||||
assertEquals("TextEntry", textEntry.getNodeName());
|
||||
assertEquals(IFFUtil.toChunkStr(meta.get(i).chunkId), textEntry.getAttribute("keyword"));
|
||||
assertEquals(texts[i], textEntry.getAttribute("value"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardTransparencyRGB() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, null, Collections.<GenericChunk>emptyList());
|
||||
|
||||
IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
|
||||
assertNull(transparency); // No transparency, just defaults
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardTransparencyRGBA() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 32, BMHDChunk.MASK_HAS_MASK, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, null, Collections.<GenericChunk>emptyList());
|
||||
|
||||
IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
|
||||
assertNotNull(transparency);
|
||||
assertEquals("Transparency", transparency.getNodeName());
|
||||
assertEquals(1, transparency.getLength());
|
||||
|
||||
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) transparency.getFirstChild();
|
||||
assertEquals("Alpha", pixelAspectRatio.getNodeName());
|
||||
assertEquals("nonpremultiplied", pixelAspectRatio.getAttribute("value"));
|
||||
|
||||
assertNull(pixelAspectRatio.getNextSibling()); // No more children
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardTransparencyPalette() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 1, BMHDChunk.MASK_TRANSPARENT_COLOR, BMHDChunk.COMPRESSION_BYTE_RUN, 1);
|
||||
|
||||
byte[] bw = {0, (byte) 0xff};
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, new IndexColorModel(header.bitplanes, bw.length, bw, bw, bw, header.transparentIndex), null, Collections.<GenericChunk>emptyList());
|
||||
|
||||
IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
|
||||
assertNotNull(transparency);
|
||||
assertEquals("Transparency", transparency.getNodeName());
|
||||
assertEquals(1, transparency.getLength());
|
||||
|
||||
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) transparency.getFirstChild();
|
||||
assertEquals("TransparentIndex", pixelAspectRatio.getNodeName());
|
||||
assertEquals("1", pixelAspectRatio.getAttribute("value"));
|
||||
|
||||
assertNull(pixelAspectRatio.getNextSibling()); // No more children
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-jpeg-jai-interop</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: JPEG/JAI TIFF Interop</name>
|
||||
<description>
|
||||
Test JPEG plugin and JAI TIFF plugin interoperability
|
||||
</description>
|
||||
|
||||
<properties>
|
||||
<project.jpms.module.name>com.twelvemonkeys.imageio.jpeg.jaiinterop</project.jpms.module.name>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.github.jai-imageio</groupId>
|
||||
<artifactId>jai-imageio-core</artifactId>
|
||||
<version>1.4.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-metadata</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-jpeg</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (c) 2021, 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:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
* * 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.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.jpeg.jaiinterop;
|
||||
|
||||
import com.twelvemonkeys.imageio.plugins.jpeg.JPEGImageReaderSpi;
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.spi.IIORegistry;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.spi.ServiceRegistry;
|
||||
import java.awt.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* Tests the JAI TIFFImageReader delegating to our JPEGImageReader.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: JAITIFFImageReaderInteroperabilityTest.java,v 1.0 08.05.12 15:25 haraldk Exp$
|
||||
*/
|
||||
public class JAITIFFImageReaderInteroperabilityTest extends ImageReaderAbstractTest<ImageReader> {
|
||||
private static final String JAI_TIFF_PROVIDER_CLASS_NAME = "com.github.jaiimageio.impl.plugins.tiff.TIFFImageReaderSpi";
|
||||
|
||||
@Override
|
||||
protected ImageReaderSpi createProvider() {
|
||||
Iterator<ImageReaderSpi> providers = IIORegistry.getDefaultInstance().getServiceProviders(ImageReaderSpi.class, new ServiceRegistry.Filter() {
|
||||
@Override
|
||||
public boolean filter(final Object provider) {
|
||||
return JAI_TIFF_PROVIDER_CLASS_NAME.equals(provider.getClass().getName());
|
||||
}
|
||||
}, true);
|
||||
|
||||
if (providers.hasNext()) {
|
||||
return providers.next();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<TestData> getTestData() {
|
||||
return Arrays.asList(
|
||||
new TestData(getClassLoaderResource("/tiff/foto_0001.tif"), new Dimension(1663, 2338), new Dimension(1663, 2337)), // Little endian, Old JPEG
|
||||
new TestData(getClassLoaderResource("/tiff/cmyk_jpeg.tif"), new Dimension(100, 100)), // CMYK, JPEG compressed, with ICC profile
|
||||
new TestData(getClassLoaderResource("/tiff/jpeg-lossless-8bit-gray.tif"), new Dimension(512, 512)) // Lossless JPEG Gray, 8 bit/sample
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getFormatNames() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getSuffixes() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getMIMETypes() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Ignore("Fails in TIFFImageReader")
|
||||
@Override
|
||||
public void testSetDestinationIllegal() {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReallyUsingOurJPEGImageReader() {
|
||||
Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName("JPEG");
|
||||
|
||||
if (readers.hasNext()) {
|
||||
ImageReader reader = readers.next();
|
||||
|
||||
if ((reader.getOriginatingProvider() instanceof JPEGImageReaderSpi)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fail("Expected Spi not registered (dependency issue?): " + JPEGImageReaderSpi.class);
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-jpeg-jep262-interop</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: JPEG/JEP-262 Interop</name>
|
||||
<description>
|
||||
Test JPEG plugin and JEP-262 (JDK TIFF plugin) interoperability
|
||||
</description>
|
||||
|
||||
<properties>
|
||||
<project.jpms.module.name>com.twelvemonkeys.imageio.jpeg.jep262interop</project.jpms.module.name>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-metadata</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-jpeg</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (c) 2021, 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:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
* * 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.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.jpeg.jep262interop;
|
||||
|
||||
import com.twelvemonkeys.imageio.plugins.jpeg.JPEGImageReaderSpi;
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||
|
||||
import org.junit.AssumptionViolatedException;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.spi.IIORegistry;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.spi.ServiceRegistry;
|
||||
import java.awt.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* Tests the JEP 262 TIFFImageReader delegating to our JPEGImageReader.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: TIFFImageReaderTest.java,v 1.0 08.05.12 15:25 haraldk Exp$
|
||||
*/
|
||||
public class JEP262TIFFImageReaderInteroperabilityTest extends ImageReaderAbstractTest<ImageReader> {
|
||||
private static final String JEP_262_PROVIDER_CLASS_NAME = "com.sun.imageio.plugins.tiff.TIFFImageReaderSpi";
|
||||
|
||||
@Override
|
||||
protected ImageReaderSpi createProvider() {
|
||||
Iterator<ImageReaderSpi> providers = IIORegistry.getDefaultInstance().getServiceProviders(ImageReaderSpi.class, new ServiceRegistry.Filter() {
|
||||
@Override
|
||||
public boolean filter(final Object provider) {
|
||||
return JEP_262_PROVIDER_CLASS_NAME.equals(provider.getClass().getName()) && ((ImageReaderSpi) provider).getVendorName().startsWith("Oracle");
|
||||
}
|
||||
}, true);
|
||||
|
||||
if (providers.hasNext()) {
|
||||
return providers.next();
|
||||
}
|
||||
|
||||
// Skip tests if we have no Spi (ie. pre JDK 9)
|
||||
throw new AssumptionViolatedException("Provider " + JEP_262_PROVIDER_CLASS_NAME + " not found");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<TestData> getTestData() {
|
||||
return Arrays.asList(
|
||||
new TestData(getClassLoaderResource("/tiff/foto_0001.tif"), new Dimension(1663, 2338), new Dimension(1663, 2337)), // Little endian, Old JPEG
|
||||
new TestData(getClassLoaderResource("/tiff/cmyk_jpeg.tif"), new Dimension(100, 100)), // CMYK, JPEG compressed, with ICC profile
|
||||
new TestData(getClassLoaderResource("/tiff/jpeg-lossless-8bit-gray.tif"), new Dimension(512, 512)) // Lossless JPEG Gray, 8 bit/sample
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getFormatNames() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getSuffixes() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getMIMETypes() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Ignore("Fails in TIFFImageReader")
|
||||
@Override
|
||||
public void testSetDestinationIllegal() {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReallyUsingOurJPEGImageReader() {
|
||||
Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName("JPEG");
|
||||
|
||||
if (readers.hasNext()) {
|
||||
ImageReader reader = readers.next();
|
||||
|
||||
if ((reader.getOriginatingProvider() instanceof JPEGImageReaderSpi)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fail("Expected Spi not registered (dependency issue?): " + JPEGImageReaderSpi.class);
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.6.3</version>
|
||||
<version>3.7.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-jpeg</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: JPEG plugin</name>
|
||||
@@ -12,6 +12,10 @@
|
||||
ImageIO plugin for Joint Photographer Expert Group images (JPEG/JFIF).
|
||||
</description>
|
||||
|
||||
<properties>
|
||||
<project.jpms.module.name>com.twelvemonkeys.imageio.jpeg</project.jpms.module.name>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
|
||||
+4
-2
@@ -38,7 +38,7 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Application.
|
||||
* An application (APPn) segment in the JPEG stream.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: harald.kuhr$
|
||||
@@ -78,7 +78,9 @@ class Application extends Segment {
|
||||
if ("JFXX".equals(identifier)) {
|
||||
return JFXX.read(data, length);
|
||||
}
|
||||
// TODO: Exif?
|
||||
if ("Exif".equals(identifier)) {
|
||||
return EXIF.read(data, length);
|
||||
}
|
||||
case JPEG.APP2:
|
||||
// ICC_PROFILE
|
||||
if ("ICC_PROFILE".equals(identifier)) {
|
||||
|
||||
+27
-23
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012, Harald Kuhr
|
||||
* Copyright (c) 2021, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -30,41 +30,45 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.jpeg;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import com.twelvemonkeys.imageio.metadata.jpeg.JPEG;
|
||||
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
|
||||
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.DataInput;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* JFIFThumbnailReader
|
||||
* An EXIF segment.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: JFIFThumbnailReader.java,v 1.0 18.04.12 12:19 haraldk Exp$
|
||||
* @version $Id: JFIFSegment.java,v 1.0 23.04.12 16:52 haraldk Exp$
|
||||
*/
|
||||
final class JFIFThumbnailReader extends ThumbnailReader {
|
||||
private final JFIF segment;
|
||||
|
||||
JFIFThumbnailReader(final ThumbnailReadProgressListener progressListener, final int imageIndex, final int thumbnailIndex, final JFIF segment) {
|
||||
super(progressListener, imageIndex, thumbnailIndex);
|
||||
this.segment = segment;
|
||||
final class EXIF extends Application {
|
||||
EXIF(byte[] data) {
|
||||
super(JPEG.APP1, "Exif", data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedImage read() {
|
||||
processThumbnailStarted();
|
||||
BufferedImage thumbnail = readRawThumbnail(segment.thumbnail, segment.thumbnail.length, 0, segment.xThumbnail, segment.yThumbnail);
|
||||
processThumbnailProgress(100f);
|
||||
processThumbnailComplete();
|
||||
|
||||
return thumbnail;
|
||||
public String toString() {
|
||||
return String.format("APP1/Exif, length: %d", data.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth() throws IOException {
|
||||
return segment.xThumbnail;
|
||||
ImageInputStream exifData() {
|
||||
// Identifier is "Exif\0" + 1 byte pad
|
||||
int offset = identifier.length() + 2;
|
||||
return new ByteArrayImageInputStream(data, offset, data.length - offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() throws IOException {
|
||||
return segment.yThumbnail;
|
||||
public static EXIF read(final DataInput data, int length) throws IOException {
|
||||
if (length < 2 + 6) {
|
||||
throw new EOFException();
|
||||
}
|
||||
|
||||
byte[] bytes = new byte[length - 2];
|
||||
data.readFully(bytes);
|
||||
|
||||
return new EXIF(bytes);
|
||||
}
|
||||
}
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
* * 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.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.jpeg;
|
||||
|
||||
import com.twelvemonkeys.imageio.color.YCbCrConverter;
|
||||
import com.twelvemonkeys.imageio.metadata.CompoundDirectory;
|
||||
import com.twelvemonkeys.imageio.metadata.Directory;
|
||||
import com.twelvemonkeys.imageio.metadata.Entry;
|
||||
import com.twelvemonkeys.imageio.metadata.jpeg.JPEG;
|
||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
|
||||
import com.twelvemonkeys.imageio.plugins.jpeg.ThumbnailReader.JPEGThumbnailReader;
|
||||
import com.twelvemonkeys.imageio.plugins.jpeg.ThumbnailReader.UncompressedThumbnailReader;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* EXIFThumbnail
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: EXIFThumbnail.java,v 1.0 18.04.12 12:19 haraldk Exp$
|
||||
*/
|
||||
final class EXIFThumbnail {
|
||||
private EXIFThumbnail() {
|
||||
}
|
||||
|
||||
static ThumbnailReader from(final EXIF segment, final CompoundDirectory exif, final ImageReader jpegThumbnailReader) throws IOException {
|
||||
if (segment != null && exif != null && exif.directoryCount() >= 2) {
|
||||
ImageInputStream stream = segment.exifData(); // NOTE This is an in-memory stream and must not be closed...
|
||||
|
||||
Directory ifd1 = exif.getDirectory(1);
|
||||
|
||||
// Compression: 1 = no compression, 6 = JPEG compression (default)
|
||||
Entry compressionEntry = ifd1.getEntryById(TIFF.TAG_COMPRESSION);
|
||||
int compression = compressionEntry == null ? 6 : ((Number) compressionEntry.getValue()).intValue();
|
||||
|
||||
switch (compression) {
|
||||
case 1:
|
||||
return createUncompressedThumbnailReader(stream, ifd1);
|
||||
case 6:
|
||||
return createJPEGThumbnailReader(segment, jpegThumbnailReader, stream, ifd1);
|
||||
default:
|
||||
throw new IIOException("EXIF IFD with unknown thumbnail compression (expected 1 or 6): " + compression);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static UncompressedThumbnailReader createUncompressedThumbnailReader(ImageInputStream stream, Directory ifd1) throws IOException {
|
||||
Entry stripOffEntry = ifd1.getEntryById(TIFF.TAG_STRIP_OFFSETS);
|
||||
Entry width = ifd1.getEntryById(TIFF.TAG_IMAGE_WIDTH);
|
||||
Entry height = ifd1.getEntryById(TIFF.TAG_IMAGE_HEIGHT);
|
||||
|
||||
if (stripOffEntry != null && width != null && height != null) {
|
||||
Entry bitsPerSample = ifd1.getEntryById(TIFF.TAG_BITS_PER_SAMPLE);
|
||||
Entry samplesPerPixel = ifd1.getEntryById(TIFF.TAG_SAMPLES_PER_PIXEL);
|
||||
Entry photometricInterpretation = ifd1.getEntryById(TIFF.TAG_PHOTOMETRIC_INTERPRETATION);
|
||||
|
||||
// Required
|
||||
int w = ((Number) width.getValue()).intValue();
|
||||
int h = ((Number) height.getValue()).intValue();
|
||||
|
||||
if (bitsPerSample != null && !Arrays.equals((int[]) bitsPerSample.getValue(), new int[] {8, 8, 8})) {
|
||||
throw new IIOException("Unknown BitsPerSample value for uncompressed EXIF thumbnail (expected [8, 8, 8]): " + bitsPerSample.getValueAsString());
|
||||
}
|
||||
|
||||
if (samplesPerPixel != null && ((Number) samplesPerPixel.getValue()).intValue() != 3) {
|
||||
throw new IIOException("Unknown SamplesPerPixel value for uncompressed EXIF thumbnail (expected 3): " + samplesPerPixel.getValueAsString());
|
||||
}
|
||||
|
||||
int interpretation = photometricInterpretation != null ? ((Number) photometricInterpretation.getValue()).intValue() : 2;
|
||||
long stripOffset = ((Number) stripOffEntry.getValue()).longValue();
|
||||
|
||||
int thumbLength = w * h * 3;
|
||||
if (stripOffset >= 0 && stripOffset + thumbLength <= stream.length()) {
|
||||
// Read raw image data, either RGB or YCbCr
|
||||
stream.seek(stripOffset);
|
||||
byte[] thumbData = new byte[thumbLength];
|
||||
stream.readFully(thumbData);
|
||||
|
||||
switch (interpretation) {
|
||||
case 2:
|
||||
// RGB
|
||||
break;
|
||||
case 6:
|
||||
// YCbCr
|
||||
for (int i = 0; i < thumbLength; i += 3) {
|
||||
YCbCrConverter.convertJPEGYCbCr2RGB(thumbData, thumbData, i);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IIOException("Unknown PhotometricInterpretation value for uncompressed EXIF thumbnail (expected 2 or 6): " + interpretation);
|
||||
}
|
||||
|
||||
return new UncompressedThumbnailReader(w, h, thumbData);
|
||||
}
|
||||
}
|
||||
|
||||
throw new IIOException("EXIF IFD with empty or incomplete uncompressed thumbnail");
|
||||
}
|
||||
|
||||
private static JPEGThumbnailReader createJPEGThumbnailReader(EXIF exif, ImageReader jpegThumbnailReader, ImageInputStream stream, Directory ifd1) throws IOException {
|
||||
Entry jpegOffEntry = ifd1.getEntryById(TIFF.TAG_JPEG_INTERCHANGE_FORMAT);
|
||||
if (jpegOffEntry != null) {
|
||||
Entry jpegLenEntry = ifd1.getEntryById(TIFF.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
|
||||
|
||||
// Test if Exif thumbnail is contained within the Exif segment (offset + length <= segment.length)
|
||||
long jpegOffset = ((Number) jpegOffEntry.getValue()).longValue();
|
||||
long jpegLength = jpegLenEntry != null ? ((Number) jpegLenEntry.getValue()).longValue() : -1;
|
||||
|
||||
if (jpegLength > 0 && jpegOffset + jpegLength <= exif.data.length) {
|
||||
// Verify first bytes are FFD8
|
||||
stream.seek(jpegOffset);
|
||||
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
if (stream.readUnsignedShort() == JPEG.SOI) {
|
||||
return new JPEGThumbnailReader(jpegThumbnailReader, stream, jpegOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new IIOException("EXIF IFD with empty or incomplete JPEG thumbnail");
|
||||
}
|
||||
}
|
||||
-248
@@ -1,248 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * 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.
|
||||
*
|
||||
* * 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.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.jpeg;
|
||||
|
||||
import com.twelvemonkeys.imageio.color.YCbCrConverter;
|
||||
import com.twelvemonkeys.imageio.metadata.Directory;
|
||||
import com.twelvemonkeys.imageio.metadata.Entry;
|
||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
|
||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import javax.imageio.stream.MemoryCacheImageInputStream;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.SequenceInputStream;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* EXIFThumbnail
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: EXIFThumbnail.java,v 1.0 18.04.12 12:19 haraldk Exp$
|
||||
*/
|
||||
final class EXIFThumbnailReader extends ThumbnailReader {
|
||||
private final ImageReader reader;
|
||||
private final Directory ifd;
|
||||
private final ImageInputStream stream;
|
||||
private final int compression;
|
||||
|
||||
private transient SoftReference<BufferedImage> cachedThumbnail;
|
||||
|
||||
EXIFThumbnailReader(final ThumbnailReadProgressListener progressListener, final ImageReader jpegReader, final int imageIndex, final int thumbnailIndex, final Directory ifd, final ImageInputStream stream) {
|
||||
super(progressListener, imageIndex, thumbnailIndex);
|
||||
this.reader = Validate.notNull(jpegReader);
|
||||
this.ifd = ifd;
|
||||
this.stream = stream;
|
||||
|
||||
Entry compression = ifd.getEntryById(TIFF.TAG_COMPRESSION);
|
||||
|
||||
this.compression = compression != null ? ((Number) compression.getValue()).intValue() : 6;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedImage read() throws IOException {
|
||||
if (compression == 1) { // 1 = no compression
|
||||
processThumbnailStarted();
|
||||
BufferedImage thumbnail = readUncompressed();
|
||||
processThumbnailProgress(100f);
|
||||
processThumbnailComplete();
|
||||
|
||||
return thumbnail;
|
||||
}
|
||||
else if (compression == 6) { // 6 = JPEG compression
|
||||
processThumbnailStarted();
|
||||
BufferedImage thumbnail = readJPEGCached(true);
|
||||
processThumbnailProgress(100f);
|
||||
processThumbnailComplete();
|
||||
|
||||
return thumbnail;
|
||||
}
|
||||
else {
|
||||
throw new IIOException("Unsupported EXIF thumbnail compression: " + compression);
|
||||
}
|
||||
}
|
||||
|
||||
private BufferedImage readJPEGCached(final boolean pixelsExposed) throws IOException {
|
||||
BufferedImage thumbnail = cachedThumbnail != null ? cachedThumbnail.get() : null;
|
||||
|
||||
if (thumbnail == null) {
|
||||
thumbnail = readJPEG();
|
||||
}
|
||||
|
||||
cachedThumbnail = pixelsExposed ? null : new SoftReference<>(thumbnail);
|
||||
|
||||
return thumbnail;
|
||||
}
|
||||
|
||||
private BufferedImage readJPEG() throws IOException {
|
||||
// IFD1 should contain JPEG offset for JPEG thumbnail
|
||||
Entry jpegOffset = ifd.getEntryById(TIFF.TAG_JPEG_INTERCHANGE_FORMAT);
|
||||
|
||||
if (jpegOffset != null) {
|
||||
stream.seek(((Number) jpegOffset.getValue()).longValue());
|
||||
InputStream input = IIOUtil.createStreamAdapter(stream);
|
||||
|
||||
// For certain EXIF files (encoded with TIFF.TAG_YCBCR_POSITIONING = 2?), we need
|
||||
// EXIF information to read the thumbnail correctly (otherwise the colors are messed up).
|
||||
// Probably related to: http://bugs.sun.com/view_bug.do?bug_id=4881314
|
||||
|
||||
// HACK: Splice empty EXIF information into the thumbnail stream
|
||||
byte[] fakeEmptyExif = {
|
||||
// SOI (from original data)
|
||||
(byte) input.read(), (byte) input.read(),
|
||||
// APP1 + len (016) + 'Exif' + 0-term + pad
|
||||
(byte) 0xFF, (byte) 0xE1, 0, 16, 'E', 'x', 'i', 'f', 0, 0,
|
||||
// Big-endian BOM (MM), TIFF magic (042), offset (0000)
|
||||
'M', 'M', 0, 42, 0, 0, 0, 0,
|
||||
};
|
||||
|
||||
input = new SequenceInputStream(new ByteArrayInputStream(fakeEmptyExif), input);
|
||||
|
||||
try {
|
||||
|
||||
try (MemoryCacheImageInputStream stream = new MemoryCacheImageInputStream(input)) {
|
||||
return readJPEGThumbnail(reader, stream);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
input.close();
|
||||
}
|
||||
}
|
||||
|
||||
throw new IIOException("Missing JPEGInterchangeFormat tag for JPEG compressed EXIF thumbnail");
|
||||
}
|
||||
|
||||
private BufferedImage readUncompressed() throws IOException {
|
||||
// Read ImageWidth, ImageLength (height) and BitsPerSample (=8 8 8, always)
|
||||
// PhotometricInterpretation (2=RGB, 6=YCbCr), SamplesPerPixel (=3, always),
|
||||
Entry width = ifd.getEntryById(TIFF.TAG_IMAGE_WIDTH);
|
||||
Entry height = ifd.getEntryById(TIFF.TAG_IMAGE_HEIGHT);
|
||||
|
||||
if (width == null || height == null) {
|
||||
throw new IIOException("Missing dimensions for uncompressed EXIF thumbnail");
|
||||
}
|
||||
|
||||
Entry bitsPerSample = ifd.getEntryById(TIFF.TAG_BITS_PER_SAMPLE);
|
||||
Entry samplesPerPixel = ifd.getEntryById(TIFF.TAG_SAMPLES_PER_PIXEL);
|
||||
Entry photometricInterpretation = ifd.getEntryById(TIFF.TAG_PHOTOMETRIC_INTERPRETATION);
|
||||
|
||||
// Required
|
||||
int w = ((Number) width.getValue()).intValue();
|
||||
int h = ((Number) height.getValue()).intValue();
|
||||
|
||||
if (bitsPerSample != null) {
|
||||
int[] bpp = (int[]) bitsPerSample.getValue();
|
||||
if (!Arrays.equals(bpp, new int[] {8, 8, 8})) {
|
||||
throw new IIOException("Unknown BitsPerSample value for uncompressed EXIF thumbnail (expected [8, 8, 8]): " + bitsPerSample.getValueAsString());
|
||||
}
|
||||
}
|
||||
|
||||
if (samplesPerPixel != null && (Integer) samplesPerPixel.getValue() != 3) {
|
||||
throw new IIOException("Unknown SamplesPerPixel value for uncompressed EXIF thumbnail (expected 3): " + samplesPerPixel.getValueAsString());
|
||||
}
|
||||
|
||||
int interpretation = photometricInterpretation != null ? ((Number) photometricInterpretation.getValue()).intValue() : 2;
|
||||
|
||||
// IFD1 should contain strip offsets for uncompressed images
|
||||
Entry offset = ifd.getEntryById(TIFF.TAG_STRIP_OFFSETS);
|
||||
if (offset != null) {
|
||||
stream.seek(((Number) offset.getValue()).longValue());
|
||||
|
||||
// Read raw image data, either RGB or YCbCr
|
||||
int thumbSize = w * h * 3;
|
||||
byte[] thumbData = JPEGImageReader.readFully(stream, thumbSize);
|
||||
|
||||
switch (interpretation) {
|
||||
case 2:
|
||||
// RGB
|
||||
break;
|
||||
case 6:
|
||||
// YCbCr
|
||||
for (int i = 0; i < thumbSize; i += 3) {
|
||||
YCbCrConverter.convertYCbCr2RGB(thumbData, thumbData, i);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IIOException("Unknown PhotometricInterpretation value for uncompressed EXIF thumbnail (expected 2 or 6): " + interpretation);
|
||||
}
|
||||
|
||||
return ThumbnailReader.readRawThumbnail(thumbData, thumbSize, 0, w, h);
|
||||
}
|
||||
|
||||
throw new IIOException("Missing StripOffsets tag for uncompressed EXIF thumbnail");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth() throws IOException {
|
||||
if (compression == 1) { // 1 = no compression
|
||||
Entry width = ifd.getEntryById(TIFF.TAG_IMAGE_WIDTH);
|
||||
|
||||
if (width == null) {
|
||||
throw new IIOException("Missing dimensions for uncompressed EXIF thumbnail");
|
||||
}
|
||||
|
||||
return ((Number) width.getValue()).intValue();
|
||||
}
|
||||
else if (compression == 6) { // 6 = JPEG compression
|
||||
return readJPEGCached(false).getWidth();
|
||||
}
|
||||
else {
|
||||
throw new IIOException("Unsupported EXIF thumbnail compression (expected 1 or 6): " + compression);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() throws IOException {
|
||||
if (compression == 1) { // 1 = no compression
|
||||
Entry height = ifd.getEntryById(TIFF.TAG_IMAGE_HEIGHT);
|
||||
|
||||
if (height == null) {
|
||||
throw new IIOException("Missing dimensions for uncompressed EXIF thumbnail");
|
||||
}
|
||||
|
||||
return ((Number) height.getValue()).intValue();
|
||||
}
|
||||
else if (compression == 6) { // 6 = JPEG compression
|
||||
return readJPEGCached(false).getHeight();
|
||||
}
|
||||
else {
|
||||
throw new IIOException("Unsupported EXIF thumbnail compression (expected 1 or 6): " + compression);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user