Compare commits

...

56 Commits

Author SHA1 Message Date
Harald Kuhr c61e47b3e7 [maven-release-plugin] prepare for next development iteration 2021-12-11 22:54:21 +01:00
Harald Kuhr 1d47d2ef90 [maven-release-plugin] prepare release twelvemonkeys-3.7.1 2021-12-11 22:54:18 +01:00
Harald Kuhr 94554b0660 #631 New way of forcing profile activation + guarding all invocations of ICC_Profile.getInstance()
(cherry picked from commit b2c5915db8)
2021-12-11 18:59:19 +01:00
Harald Kuhr a39bca4d2f #645 AAIOBE in CCITTFaxDecoderStream now wrapped in IOException
(cherry picked from commit 3911191b04)
2021-12-11 18:59:18 +01:00
Harald Kuhr fdbbcc54a8 Preparing for next release. 2021-12-10 17:02:41 +01:00
Harald Kuhr 00cd9471dd #483: Rollback 2021-12-10 17:00:58 +01:00
Snyk bot eb6a3bde39 fix: upgrade commons-fileupload:commons-fileupload from 1.3.3 to 1.4 (#642)
Snyk has created this PR to upgrade commons-fileupload:commons-fileupload from 1.3.3 to 1.4.

See this package in Maven Repository:
https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload/

See this project in Snyk:
https://app.snyk.io/org/haraldk/project/3eb50c00-e586-4f4c-a39c-90c80f89cc60?utm_source=github&utm_medium=referral&page=upgrade-pr

(cherry picked from commit bc328419ac)
2021-12-10 16:57:00 +01:00
Harald Kuhr d3dd4c29d5 Avoid fetching external resources in XMPReader.
(cherry picked from commit da4efe98bf)
2021-12-10 16:57:00 +01:00
Harald Kuhr 3355acfa03 Minor clean-up.
(cherry picked from commit 6653f4a85d)
2021-12-10 16:56:58 +01:00
Harald Kuhr b70e52561a Minor AffineTransformOp clean-up + removed test output.
(cherry picked from commit 511a29beb9)
2021-12-10 16:56:56 +01:00
Gauthier 49ddbfa85e Make tests pass on JDK 16 and 17 (#635)
* make tests pass on JDK 16 and 17
replace deprecated mockito-all by mockito-core, and updated to latest 3.x
replace deprecated org.mockito.Matchers

* code cleanup from IDE suggestions

* add oracle jdk 16 and 17 to Travis

(cherry picked from commit 5617b4323c)
2021-12-10 16:56:56 +01:00
Harald Kuhr 215db21569 #629: Fixed build
(cherry picked from commit 16d0af357d)
2021-12-10 16:56:54 +01:00
Harald Kuhr 0ac8011053 #629: Preliminary WebP animation (ANIM/ANMF) support
(cherry picked from commit 74927d5396)
2021-12-10 16:56:52 +01:00
Harald Kuhr abbbca9be3 Readme improvements
(cherry picked from commit 7e809dd834)
2021-12-10 16:56:52 +01:00
Harald Kuhr bc5473aec6 Fixed Travis link + bonus project summary updates
(cherry picked from commit 7aa95a08bc)
2021-12-10 16:56:52 +01:00
Harald Kuhr aca8b1256a Minor servlet clean-up.
(cherry picked from commit c28963ae49)
2021-12-10 16:56:51 +01:00
Harald Kuhr 18c1edf0e5 Servlet deprecation
(cherry picked from commit 0327f5fc1a)
2021-12-10 16:56:51 +01:00
Harald Kuhr 6a01466ebb #628: Stabilized build + better dependency scopes and module names in interop modules
(cherry picked from commit 1c59057c30)
2021-12-10 16:56:31 +01:00
Harald Kuhr b19df1640b #626 Clean up + fill order support for all compression types
(cherry picked from commit 3e1f85c4dc)
2021-12-10 16:53:27 +01:00
Harald Kuhr e8e3e0f1d6 #628 TIFF metadata fix, now always outputs denominator for rationals.
+ Bonus: Added JAI TIFF interop module with test and other minor fixes.

(cherry picked from commit 11227a68a0)
2021-12-10 16:53:23 +01:00
Oliver Schmidtmer c249a21c8c #626: Handle fillOrder in TIFFImageReader, not in CCITTFaxDecoderStream (#627)
(cherry picked from commit 62ba73a30e)
2021-12-10 16:53:23 +01:00
Harald Kuhr 182b2fdfa9 Fixed NullPointerException due to missing PhotometricInterpretation, now uses fallback as we do when reading.
(cherry picked from commit 1f33afb5a1)
2021-12-10 16:53:22 +01:00
Harald Kuhr df068e350d #626 TIFF CCITT detection only once per IFD
(cherry picked from commit 9d3f271867)
2021-12-10 16:53:20 +01:00
Harald Kuhr b55c623b87 #623: TGAImageReader, PCXImageReader and SGIImageReader now return more standard image types as default, for better AffineTransformOp compatibility. Added tests.
Bonus: Subsampling fix for TGAImageReader and BMPImageReader.

(cherry picked from commit 812e12acb0)
2021-12-10 16:53:16 +01:00
Harald Kuhr 09573b52ac #624: Added metadata support for 16 bit USHORT gray.
(cherry picked from commit 060b6cf852)
2021-12-10 16:53:15 +01:00
Koen De Groote ff7edbd223 Certain pixeldepth-16 TGA files fail to process, classcast exception (#624)
* Added fixed for monochrome tga16 bit. Uncertain if that description is complete.
Test files added. Without the changed code, the tests fail.

* Fix suggested by HaraldK

Co-authored-by: Koen De Groote <koen.degroote@limecraft.com>
(cherry picked from commit e68ce7ffd1)
2021-12-10 16:53:15 +01:00
Harald Kuhr d8f0cd97a2 Fix typo in TIFFImageMetadataFormat mk II.
(cherry picked from commit 778cdef69c)
2021-12-10 16:52:21 +01:00
Harald Kuhr f6971a9bce Fix typo in TIFFImageMetadataFormat.
(cherry picked from commit d46a76fca8)
2021-12-10 16:52:20 +01:00
Harald Kuhr 821965df0d #621 Don't add ICC profile for default gray images
(cherry picked from commit 105a1ee466)
2021-12-10 16:52:12 +01:00
Harald Kuhr ed46305d31 #619: Fix WebP Y'CbCr->RGB conversion (now uses rec 601)
(cherry picked from commit 976e5d6210)
2021-12-10 16:51:49 +01:00
Harald Kuhr ca3adb7c45 Minor clean-up.
(cherry picked from commit 6daca00fcd)
2021-12-10 16:51:45 +01:00
Harald Kuhr 59f76209bc Some more minor clean-up.
(cherry picked from commit ce997a6951)
2021-12-10 16:51:31 +01:00
Harald Kuhr 2076235313 Minor clean-up.
(cherry picked from commit 23bf5cb7b2)
2021-12-10 16:51:31 +01:00
Harald Kuhr 88bebf31f8 #616: Remove dependency on old xmlgraphics-commons (no longer needed)
(cherry picked from commit 564778f415)
2021-12-10 16:51:21 +01:00
Harald Kuhr 37beb21c29 Fix WebP ICC handling for images with alpha.
(cherry picked from commit e28bf8fb44)
2021-12-10 16:51:20 +01:00
Harald Kuhr 88c0d27516 Add WebP to BOM.
(cherry picked from commit cf8d630d01)
2021-12-10 16:51:20 +01:00
Harald Kuhr dcd4ffccf4 Switch build from travis.ci.org to com
(cherry picked from commit 0ff7224912)
2021-12-10 16:51:12 +01:00
Koen De Groote f35f5c6b24 Documentation cleanup (#612)
* Added the `@Deprecated` tag to instances where is was mentioned in documentation, but not for the actual code itself.

Changed one documentation link pointing at a non-existing item.

* As per PR suggestion.

(cherry picked from commit 196081a317)
2021-12-10 16:51:12 +01:00
Harald Kuhr 9dcf53d985 #609 Fixed ICC Profile handling in WebP.
(cherry picked from commit ff50180d86)
2021-12-10 16:51:11 +01:00
Harald Kuhr 44a2066b79 Minor code clean-up.
(cherry picked from commit 8f2c482167)
2021-12-10 16:51:06 +01:00
Oliver Schmidtmer 382c51db7c Invert EOF check
(cherry picked from commit cd42d81817)
2021-12-10 16:50:59 +01:00
Oliver Schmidtmer 660f3f7e86 #579 Deeper EOL search in the CCITT stream
(cherry picked from commit ba5c667b6c)
2021-12-10 16:50:59 +01:00
Harald Kuhr b4c30872c4 XXX: Remove another old servlet class.
(cherry picked from commit 94eac2d6e5)
2021-12-10 16:50:49 +01:00
Harald Kuhr 402d4d466d HTTPS links in README.md
(cherry picked from commit f63a33d541)
2021-12-10 16:50:49 +01:00
Harald Kuhr 7860bf7e17 XXX: Remove old servlet class.
(cherry picked from commit 2f9768a1d4)
2021-12-10 16:50:49 +01:00
Harald Kuhr d52522fb80 #483 Minor optimization
(cherry picked from commit 06bcf22242)
2021-12-10 16:41:24 +01:00
Harald Kuhr 8a8c6b1931 #483 Add license headers.
(cherry picked from commit 20c7f8e60e)
2021-12-10 16:41:24 +01:00
Harald Kuhr 2930708a54 #483 Initial PSD Write support
(cherry picked from commit 15a9ad0a9b)
2021-12-10 16:41:23 +01:00
Harald Kuhr 5661e7459c #606: Fix bug introduced by more aggressive readDirect.
(cherry picked from commit 4e2bf131d2)
2021-12-10 16:39:20 +01:00
Harald Kuhr 4d45ea4966 #606: Workaround for broken JDK WBMPImageReader
(cherry picked from commit d0c4a07556)
2021-12-10 16:39:19 +01:00
Harald Kuhr 34852f7be5 Cleaner tests for Java 6 or later... A little late. :-)
(cherry picked from commit 21059c8d5a)
2021-12-10 16:39:17 +01:00
Harald Kuhr abc929a531 Adding GitHub sponsors link.
(cherry picked from commit fa7b530809)
2021-12-10 16:39:16 +01:00
Harald Kuhr 247a09ca61 Test clean-up.
(cherry picked from commit 790cf3b32e)
2021-12-10 16:39:15 +01:00
Harald Kuhr c2880fe793 Rename file, add missing file extension.
(cherry picked from commit b1baaad23b)
2021-12-10 16:39:15 +01:00
Harald Kuhr c9e522475b Bump plugins and stop deploying useless (internal) artifacts.
(cherry picked from commit 7fa704ace5)
2021-12-10 16:39:15 +01:00
Harald Kuhr 1da16e4d7a Updated versions to 3.7.0.
(cherry picked from commit 8d07f4fe90)
2021-12-10 16:33:02 +01:00
204 changed files with 3213 additions and 2245 deletions
+1
View File
@@ -0,0 +1 @@
github: haraldk
+5 -3
View File
@@ -1,9 +1,11 @@
dist: trusty dist: trusty
language: java language: java
jdk: jdk:
- oraclejdk8 # Legacy - oraclejdk8 # LTS until Mar 2022
- oraclejdk11 # LTS - oraclejdk11 # LTS until Sep 2023
- oraclejdk15 # Latest - oraclejdk15 # out of support
- oraclejdk16 # out of support
- oraclejdk17 # LTS until Sep 2026
jobs: jobs:
include: include:
# Extra job, testing legacy CMM option # Extra job, testing legacy CMM option
+62 -63
View File
@@ -1,19 +1,16 @@
[![Build Status](https://travis-ci.org/haraldk/TwelveMonkeys.svg?branch=master)](https://travis-ci.org/haraldk/TwelveMonkeys) [![Build Status](https://travis-ci.com/haraldk/TwelveMonkeys.svg?branch=master)](https://travis-ci.com/github/haraldk/TwelveMonkeys)
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.twelvemonkeys.imageio/imageio/badge.svg?color=slateblue)](https://maven-badges.herokuapp.com/maven-central/com.twelvemonkeys.imageio/imageio) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.twelvemonkeys.imageio/imageio/badge.svg?color=slateblue)](https://maven-badges.herokuapp.com/maven-central/com.twelvemonkeys.imageio/imageio)
[![StackOverflow](https://img.shields.io/badge/stack_overflow-twelvemonkeys-orange.svg)](https://stackoverflow.com/questions/tagged/twelvemonkeys) [![StackOverflow](https://img.shields.io/badge/stack_overflow-twelvemonkeys-orange.svg)](https://stackoverflow.com/questions/tagged/twelvemonkeys)
[![Donate](https://img.shields.io/badge/donate-PayPal-blue.svg)](https://paypal.me/haraldk76/100) [![Donate](https://img.shields.io/badge/donate-PayPal-blue.svg)](https://paypal.me/haraldk76/100)
## About ## 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 goal of this project is to provide support for formats not covered by the JRE itself.
The main purpose 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
Support for formats is important, both to be able to read data found
"in the wild", as well as to maintain access to data in legacy formats. "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. As 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.
---- ----
@@ -47,11 +44,11 @@ The goal is to create a set of efficient and robust ImageIO plug-ins, that can b
|ThumbsDB| Thumbs.db| MS Windows Thumbs DB | âś” | - | - | OLE2 Compound Document based format only |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](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) | | [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) | | | 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) | In progress | [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) | | 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), **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.* 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 Note that GIF, PNG and WBMP formats are already supported through the ImageIO API, using the
@@ -166,7 +163,7 @@ finally {
``` ```
For more advanced usage, and information on how to use the ImageIO API, I suggest you read the 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. from Oracle.
#### Adobe Clipping Path support #### Adobe Clipping Path support
@@ -220,14 +217,14 @@ BufferedImage output = ditherer.filter(input, null);
## Building ## 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 $ git clone git@github.com:haraldk/TwelveMonkeys.git
This should create a folder named `TwelveMonkeys` in your current directory. Change directory to the `TwelveMonkeys` This should create a folder named `TwelveMonkeys` in your current directory. Change directory to the `TwelveMonkeys`
folder, and issue the command below to build. 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 $ mvn package
@@ -274,12 +271,12 @@ To depend on the JPEG and TIFF plugin using Maven, add the following to your POM
<dependency> <dependency>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-jpeg</artifactId> <artifactId>imageio-jpeg</artifactId>
<version>3.6.4</version> <version>3.7.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-tiff</artifactId> <artifactId>imageio-tiff</artifactId>
<version>3.6.4</version> <version>3.7.0</version>
</dependency> </dependency>
<!-- <!--
@@ -289,7 +286,7 @@ To depend on the JPEG and TIFF plugin using Maven, add the following to your POM
<dependency> <dependency>
<groupId>com.twelvemonkeys.servlet</groupId> <groupId>com.twelvemonkeys.servlet</groupId>
<artifactId>servlet</artifactId> <artifactId>servlet</artifactId>
<version>3.6.4</version> <version>3.7.0</version>
</dependency> </dependency>
</dependencies> </dependencies>
``` ```
@@ -298,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: 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.4.jar twelvemonkeys-common-lang-3.7.0.jar
twelvemonkeys-common-io-3.6.4.jar twelvemonkeys-common-io-3.7.0.jar
twelvemonkeys-common-image-3.6.4.jar twelvemonkeys-common-image-3.7.0.jar
twelvemonkeys-imageio-core-3.6.4.jar twelvemonkeys-imageio-core-3.7.0.jar
twelvemonkeys-imageio-metadata-3.6.4.jar twelvemonkeys-imageio-metadata-3.7.0.jar
twelvemonkeys-imageio-jpeg-3.6.4.jar twelvemonkeys-imageio-jpeg-3.7.0.jar
twelvemonkeys-imageio-tiff-3.6.4.jar twelvemonkeys-imageio-tiff-3.7.0.jar
#### Deploying the plugins in a web app #### Deploying the plugins in a web app
@@ -370,77 +367,79 @@ Other "fat" JAR bundlers will probably have similar mechanisms to merge entries
### Links to prebuilt binaries ### Links to prebuilt binaries
##### Latest version (3.6.4) ##### Latest version (3.7.0)
Requires Java 7 or later. Requires Java 7 or later.
Common dependencies Common dependencies
* [common-lang-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-lang/3.6.4/common-lang-3.6.4.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.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-io/3.6.4/common-io-3.6.4.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.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-image/3.6.4/common-image-3.6.4.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 dependencies
* [imageio-core-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-core/3.6.4/imageio-core-3.6.4.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.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-metadata/3.6.4/imageio-metadata-3.6.4.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 plugins
* [imageio-bmp-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-bmp/3.6.4/imageio-bmp-3.6.4.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.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-hdr/3.6.4/imageio-hdr-3.6.4.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.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-icns/3.6.4/imageio-icns-3.6.4.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.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-iff/3.6.4/imageio-iff-3.6.4.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.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jpeg/3.6.4/imageio-jpeg-3.6.4.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.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pcx/3.6.4/imageio-pcx-3.6.4.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.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pict/3.6.4/imageio-pict-3.6.4.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.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pnm/3.6.4/imageio-pnm-3.6.4.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.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-psd/3.6.4/imageio-psd-3.6.4.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.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-sgi/3.6.4/imageio-sgi-3.6.4.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.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tga/3.6.4/imageio-tga-3.6.4.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.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-thumbsdb/3.6.4/imageio-thumbsdb-3.6.4.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.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tiff/3.6.4/imageio-tiff-3.6.4.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 plugins requiring 3rd party libs
* [imageio-batik-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-batik/3.6.4/imageio-batik-3.6.4.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 Photoshop Path support for ImageIO
* [imageio-clippath-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-clippath/3.6.4/imageio-clippath-3.6.4.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 support
* [servlet-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/servlet/servlet/3.6.4/servlet-3.6.4.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) ##### 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*. Use this version for projects that requires Java 6 or need the JMagick support. *Does not support Java 8 or later*.
Common dependencies 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-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](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-io/3.0.2/common-io-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](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-image/3.0.2/common-image-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 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-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](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-metadata/3.0.2/imageio-metadata-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 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-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](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tiff/3.0.2/imageio-tiff-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](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-psd/3.0.2/imageio-psd-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](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pict/3.0.2/imageio-pict-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](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-iff/3.0.2/imageio-iff-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](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-icns/3.0.2/imageio-icns-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](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-ico/3.0.2/imageio-ico-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](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-thumbsdb/3.0.2/imageio-thumbsdb-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 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-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](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jmagick/3.0.2/imageio-jmagick-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 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 ## License
This project is provided 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 Copyright (c) 2008-2020, Harald Kuhr
All rights reserved. All rights reserved.
@@ -495,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. 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 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 utilizes the pairwise partial ordering mechanism of the `IIOServiceRegistry` to make sure it is installed before
+6 -1
View File
@@ -5,7 +5,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys</groupId> <groupId>com.twelvemonkeys</groupId>
<artifactId>twelvemonkeys</artifactId> <artifactId>twelvemonkeys</artifactId>
<version>3.7.0</version> <version>3.7.2-SNAPSHOT</version>
</parent> </parent>
<groupId>com.twelvemonkeys.bom</groupId> <groupId>com.twelvemonkeys.bom</groupId>
@@ -123,6 +123,11 @@
<artifactId>imageio-tiff</artifactId> <artifactId>imageio-tiff</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-webp</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-xwd</artifactId> <artifactId>imageio-xwd</artifactId>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.common</groupId> <groupId>com.twelvemonkeys.common</groupId>
<artifactId>common</artifactId> <artifactId>common</artifactId>
<version>3.7.0</version> <version>3.7.2-SNAPSHOT</version>
</parent> </parent>
<artifactId>common-image</artifactId> <artifactId>common-image</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
@@ -34,7 +34,13 @@ import java.awt.*;
import java.awt.geom.AffineTransform; import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D; import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D; 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}. * 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); delegate = new java.awt.image.AffineTransformOp(xform, interpolationType);
} }
@SuppressWarnings("ConstantConditions")
@Override @Override
public BufferedImage filter(final BufferedImage src, BufferedImage dst) { public BufferedImage filter(final BufferedImage src, BufferedImage dst) {
try { try {
@@ -80,10 +87,9 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp {
dst = createCompatibleDestImage(src, src.getColorModel()); dst = createCompatibleDestImage(src, src.getColorModel());
} }
Graphics2D g2d = null; Graphics2D g2d = dst.createGraphics();
try { try {
g2d = dst.createGraphics();
int interpolationType = delegate.getInterpolationType(); int interpolationType = delegate.getInterpolationType();
if (interpolationType > 0) { if (interpolationType > 0) {
@@ -109,9 +115,7 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp {
return dst; return dst;
} }
finally { finally {
if (g2d != null) { g2d.dispose();
g2d.dispose();
}
} }
} }
} }
@@ -45,8 +45,8 @@ import java.awt.image.BufferedImage;
*/ */
public class BufferedImageIcon implements Icon { public class BufferedImageIcon implements Icon {
private final BufferedImage image; private final BufferedImage image;
private int width; private final int width;
private int height; private final int height;
private final boolean fast; private final boolean fast;
public BufferedImageIcon(BufferedImage pImage) { public BufferedImageIcon(BufferedImage pImage) {
@@ -81,11 +81,10 @@ public class BufferedImageIcon implements Icon {
else { else {
//System.out.println("Scaling using interpolation"); //System.out.println("Scaling using interpolation");
Graphics2D g2 = (Graphics2D) g; Graphics2D g2 = (Graphics2D) g;
AffineTransform xform = AffineTransform.getTranslateInstance(x, y); AffineTransform transform = AffineTransform.getTranslateInstance(x, y);
xform.scale(width / (double) image.getWidth(), height / (double) image.getHeight()); transform.scale(width / (double) image.getWidth(), height / (double) image.getHeight());
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
RenderingHints.VALUE_INTERPOLATION_BILINEAR); g2.drawImage(image, transform, null);
g2.drawImage(image, xform, null);
} }
} }
} }
@@ -587,6 +587,7 @@ class IndexImage {
* @deprecated Use {@link #getIndexColorModel(Image,int,int)} instead! * @deprecated Use {@link #getIndexColorModel(Image,int,int)} instead!
* This version will be removed in a later version of the API. * This version will be removed in a later version of the API.
*/ */
@Deprecated
public static IndexColorModel getIndexColorModel(Image pImage, int pNumberOfColors, boolean pFast) { public static IndexColorModel getIndexColorModel(Image pImage, int pNumberOfColors, boolean pFast) {
return getIndexColorModel(pImage, pNumberOfColors, pFast ? COLOR_SELECTION_FAST : COLOR_SELECTION_QUALITY); return getIndexColorModel(pImage, pNumberOfColors, pFast ? COLOR_SELECTION_FAST : COLOR_SELECTION_QUALITY);
} }
@@ -30,17 +30,26 @@
package com.twelvemonkeys.image; 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.color.ColorSpace;
import java.awt.geom.AffineTransform; import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D; 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.Arrays;
import java.util.List; import java.util.List;
import static org.junit.Assert.*; import javax.imageio.ImageTypeSpecifier;
import org.junit.Test;
/** /**
* AffineTransformOpTest. * AffineTransformOpTest.
@@ -101,6 +110,7 @@ public class AffineTransformOpTest {
private final int width = 30; private final int width = 30;
private final int height = 20; private final int height = 20;
private final double anchor = min(width, height) / 2.0;
@Test @Test
public void testGetPoint2D() { public void testGetPoint2D() {
@@ -128,8 +138,8 @@ public class AffineTransformOpTest {
@Test @Test
public void testFilterRotateBIStandard() { 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 jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), null);
BufferedImageOp tmOp = new com.twelvemonkeys.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, anchor, anchor), null);
for (Integer type : TYPES) { for (Integer type : TYPES) {
BufferedImage image = new BufferedImage(width, height, type); BufferedImage image = new BufferedImage(width, height, type);
@@ -147,8 +157,8 @@ public class AffineTransformOpTest {
@Test @Test
public void testFilterRotateBICustom() { 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 jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), null);
BufferedImageOp tmOp = new com.twelvemonkeys.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, anchor, anchor), null);
for (ImageTypeSpecifier spec : SPECS) { for (ImageTypeSpecifier spec : SPECS) {
BufferedImage image = spec.createBufferedImage(width, height); BufferedImage image = spec.createBufferedImage(width, height);
@@ -197,8 +207,8 @@ public class AffineTransformOpTest {
@Test @Test
public void testFilterRotateRasterStandard() { 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 jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), null);
RasterOp tmOp = new com.twelvemonkeys.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, anchor, anchor), null);
for (Integer type : TYPES) { for (Integer type : TYPES) {
Raster raster = new BufferedImage(width, height, type).getRaster(); Raster raster = new BufferedImage(width, height, type).getRaster();
@@ -221,8 +231,6 @@ public class AffineTransformOpTest {
fail("No result!"); fail("No result!");
} }
else { else {
System.err.println("AffineTransformOpTest.testFilterRotateRasterStandard");
System.err.println("type: " + type);
continue; continue;
} }
} }
@@ -240,8 +248,8 @@ public class AffineTransformOpTest {
@Test @Test
public void testFilterRotateRasterCustom() { 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 jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), null);
RasterOp tmOp = new com.twelvemonkeys.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, anchor, anchor), null);
for (ImageTypeSpecifier spec : SPECS) { for (ImageTypeSpecifier spec : SPECS) {
Raster raster = spec.createBufferedImage(width, height).getRaster(); Raster raster = spec.createBufferedImage(width, height).getRaster();
@@ -264,8 +272,6 @@ public class AffineTransformOpTest {
fail("No result!"); fail("No result!");
} }
else { else {
System.err.println("AffineTransformOpTest.testFilterRotateRasterCustom");
System.err.println("spec: " + spec);
continue; continue;
} }
} }
+1 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.common</groupId> <groupId>com.twelvemonkeys.common</groupId>
<artifactId>common</artifactId> <artifactId>common</artifactId>
<version>3.7.0</version> <version>3.7.2-SNAPSHOT</version>
</parent> </parent>
<artifactId>common-io</artifactId> <artifactId>common-io</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
@@ -65,6 +65,7 @@ import java.io.FilenameFilter;
* @see WildcardStringParser * @see WildcardStringParser
* @deprecated * @deprecated
*/ */
@Deprecated
public class FilenameMaskFilter implements FilenameFilter { public class FilenameMaskFilter implements FilenameFilter {
// TODO: Rewrite to use regexp, or create new class // 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.BufferedReader#readLine()
* @see java.io.DataInputStream#readLine() * @see java.io.DataInputStream#readLine()
*/ */
@Deprecated
public String readLine() throws IOException { public String readLine() throws IOException {
DataInputStream ds = new DataInputStream(in); DataInputStream ds = new DataInputStream(in);
return ds.readLine(); return ds.readLine();
@@ -29,6 +29,9 @@
package com.twelvemonkeys.xml; package com.twelvemonkeys.xml;
import java.io.OutputStream;
import java.io.Writer;
import org.w3c.dom.DOMConfiguration; import org.w3c.dom.DOMConfiguration;
import org.w3c.dom.DOMImplementationList; import org.w3c.dom.DOMImplementationList;
import org.w3c.dom.Document; 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.LSOutput;
import org.w3c.dom.ls.LSSerializer; import org.w3c.dom.ls.LSSerializer;
import java.io.OutputStream;
import java.io.Writer;
/** /**
* {@code DOMImplementationLS} backed implementation. * {@code DOMImplementationLS} backed implementation.
* *
@@ -88,17 +88,6 @@ public final class DOMSerializer {
output.setCharacterStream(pStream); 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 * Specifies wether the serializer should use indentation and optimize for
* readability. * readability.
@@ -169,13 +158,7 @@ public final class DOMSerializer {
try { try {
return DOMImplementationRegistry.newInstance(); return DOMImplementationRegistry.newInstance();
} }
catch (ClassNotFoundException e) { catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
throw new IllegalStateException(e);
}
catch (InstantiationException e) {
throw new IllegalStateException(e);
}
catch (IllegalAccessException e) {
throw new IllegalStateException(e); throw new IllegalStateException(e);
} }
} }
@@ -30,16 +30,23 @@
package com.twelvemonkeys.xml; package com.twelvemonkeys.xml;
import com.twelvemonkeys.lang.StringUtil; import java.io.ByteArrayInputStream;
import org.w3c.dom.*; import java.io.ByteArrayOutputStream;
import org.xml.sax.SAXException; 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.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
import java.io.*;
import java.nio.charset.Charset; import org.w3c.dom.*;
import java.util.Date; import org.xml.sax.SAXException;
import com.twelvemonkeys.lang.StringUtil;
/** /**
* XMLSerializer * 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) { 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); pBuilder.append(pEntity);
return pEnd + 1; return pEnd + 1;
} }
@@ -527,8 +534,7 @@ public class XMLSerializer {
builder = factory.newDocumentBuilder(); builder = factory.newDocumentBuilder();
} }
catch (ParserConfigurationException e) { catch (ParserConfigurationException e) {
//noinspection ThrowableInstanceNeverThrown BOGUS throw new IOException(e);
throw (IOException) new IOException(e.getMessage()).initCause(e);
} }
DOMImplementation dom = builder.getDOMImplementation(); DOMImplementation dom = builder.getDOMImplementation();
+1 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.common</groupId> <groupId>com.twelvemonkeys.common</groupId>
<artifactId>common</artifactId> <artifactId>common</artifactId>
<version>3.7.0</version> <version>3.7.2-SNAPSHOT</version>
</parent> </parent>
<artifactId>common-lang</artifactId> <artifactId>common-lang</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
@@ -770,6 +770,7 @@ public final class StringUtil {
*/ */
/*public*/ /*public*/
@Deprecated
static String formatNumber(long pNum, int pLen) throws IllegalArgumentException { static String formatNumber(long pNum, int pLen) throws IllegalArgumentException {
StringBuilder result = new StringBuilder(); StringBuilder result = new StringBuilder();
@@ -1464,6 +1465,7 @@ public final class StringUtil {
*/ */
/*public*/ /*public*/
@Deprecated
static String removeSubstring(final String pSource, final char pBeginBoundaryChar, final char pEndBoundaryChar, final int pOffset) { static String removeSubstring(final String pSource, final char pBeginBoundaryChar, final char pEndBoundaryChar, final int pOffset) {
StringBuilder filteredString = new StringBuilder(); StringBuilder filteredString = new StringBuilder();
boolean insideDemarcatedArea = false; boolean insideDemarcatedArea = false;
@@ -157,6 +157,7 @@ public class Time {
* @see #parseTime(String) * @see #parseTime(String)
* @deprecated * @deprecated
*/ */
@Deprecated
public String toString(String pFormatStr) { public String toString(String pFormatStr) {
TimeFormat tf = new TimeFormat(pFormatStr); TimeFormat tf = new TimeFormat(pFormatStr);
@@ -174,6 +175,7 @@ public class Time {
* @see #toString(String) * @see #toString(String)
* @deprecated * @deprecated
*/ */
@Deprecated
public static Time parseTime(String pStr) { public static Time parseTime(String pStr) {
TimeFormat tf = TimeFormat.getInstance(); TimeFormat tf = TimeFormat.getInstance();
@@ -111,6 +111,7 @@ import java.io.PrintStream;
* @author <a href="mailto:eirik.torske@iconmedialab.no">Eirik Torske</a> * @author <a href="mailto:eirik.torske@iconmedialab.no">Eirik Torske</a>
* @deprecated Will probably be removed in the near future * @deprecated Will probably be removed in the near future
*/ */
@Deprecated
public class WildcardStringParser { public class WildcardStringParser {
// TODO: Get rid of this class // TODO: Get rid of this class
+1 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys</groupId> <groupId>com.twelvemonkeys</groupId>
<artifactId>twelvemonkeys</artifactId> <artifactId>twelvemonkeys</artifactId>
<version>3.7.0</version> <version>3.7.2-SNAPSHOT</version>
</parent> </parent>
<groupId>com.twelvemonkeys.common</groupId> <groupId>com.twelvemonkeys.common</groupId>
<artifactId>common</artifactId> <artifactId>common</artifactId>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys</groupId> <groupId>com.twelvemonkeys</groupId>
<artifactId>twelvemonkeys</artifactId> <artifactId>twelvemonkeys</artifactId>
<version>3.7.0</version> <version>3.7.2-SNAPSHOT</version>
</parent> </parent>
<groupId>com.twelvemonkeys.contrib</groupId> <groupId>com.twelvemonkeys.contrib</groupId>
<artifactId>contrib</artifactId> <artifactId>contrib</artifactId>
@@ -31,8 +31,9 @@
package com.twelvemonkeys.contrib.tiff; package com.twelvemonkeys.contrib.tiff;
import com.twelvemonkeys.contrib.tiff.TIFFUtilities.TIFFExtension; 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 com.twelvemonkeys.io.FileUtil;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.w3c.dom.Node; import org.w3c.dom.Node;
@@ -154,7 +155,7 @@ public class TIFFUtilitiesTest {
reader.setInput(checkTest1); reader.setInput(checkTest1);
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
Node metaData = reader.getImageMetadata(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(); short orientation = ((Number) expression.evaluate(metaData, XPathConstants.NUMBER)).shortValue();
Assert.assertEquals(orientation, TIFFExtension.ORIENTATION_RIGHTTOP); Assert.assertEquals(orientation, TIFFExtension.ORIENTATION_RIGHTTOP);
} }
@@ -171,7 +172,7 @@ public class TIFFUtilitiesTest {
reader.setInput(checkTest2); reader.setInput(checkTest2);
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
Node metaData = reader.getImageMetadata(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(); short orientation = ((Number) expression.evaluate(metaData, XPathConstants.NUMBER)).shortValue();
Assert.assertEquals(orientation, i == 1 Assert.assertEquals(orientation, i == 1
? TIFFExtension.ORIENTATION_BOTRIGHT ? TIFFExtension.ORIENTATION_BOTRIGHT
+2 -9
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId> <artifactId>imageio</artifactId>
<version>3.7.0</version> <version>3.7.2-SNAPSHOT</version>
</parent> </parent>
<artifactId>imageio-batik</artifactId> <artifactId>imageio-batik</artifactId>
<name>TwelveMonkeys :: ImageIO :: Batik Plugin</name> <name>TwelveMonkeys :: ImageIO :: Batik Plugin</name>
@@ -68,13 +68,6 @@
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>xmlgraphics-commons</artifactId>
<version>2.2</version>
<scope>provided</scope>
</dependency>
<dependency> <dependency>
<groupId>org.apache.xmlgraphics</groupId> <groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-anim</artifactId> <artifactId>batik-anim</artifactId>
@@ -98,7 +91,7 @@
<!-- <!--
There seems to be some weirdness in the There seems to be some weirdness in the
Batik/FOP poms (Batik depends on FOP 0.20-5) that screws things up, 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> <exclusions>
<exclusion> <exclusion>
@@ -30,37 +30,8 @@
package com.twelvemonkeys.imageio.plugins.svg; 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.*;
import java.awt.geom.AffineTransform; import java.awt.geom.AffineTransform;
import java.awt.geom.Dimension2D;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.IOException; import java.io.IOException;
@@ -70,6 +41,38 @@ import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; 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. * Image reader for SVG document fragments.
* *
@@ -132,6 +135,7 @@ public class SVGImageReader extends ImageReaderBase {
// Set ImageReadParams as hints // Set ImageReadParams as hints
// Note: The cast to Map invokes a different method that preserves // Note: The cast to Map invokes a different method that preserves
// unset defaults, DO NOT REMOVE! // unset defaults, DO NOT REMOVE!
//noinspection rawtypes
rasterizer.setTranscodingHints((Map) paramsToHints(svgParam)); 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(); 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 // 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 // Sets up root, curTxf & curAoi
// ---- // ----
if (document != null) { if (document != null) {
@@ -584,9 +588,7 @@ public class SVGImageReader extends ImageReaderBase {
return dest; return dest;
} }
catch (Exception ex) { catch (Exception ex) {
TranscoderException exception = new TranscoderException(ex.getMessage()); throw new TranscoderException(ex.getMessage(), ex);
exception.initCause(ex);
throw exception;
} }
finally { finally {
if (context != null) { if (context != null) {
@@ -54,8 +54,6 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
/** /**
@@ -225,7 +223,7 @@ public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader>
assertEquals(500, image.getHeight()); assertEquals(500, image.getHeight());
// CSS and embedded resources all go! // CSS and embedded resources all go!
verifyZeroInteractions(listener); verifyNoInteractions(listener);
} }
finally { finally {
reader.dispose(); reader.dispose();
@@ -266,7 +264,7 @@ public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader>
assertEquals(500, image.getHeight()); assertEquals(500, image.getHeight());
// No more warnings now that the base URI is set // No more warnings now that the base URI is set
verifyZeroInteractions(listener); verifyNoInteractions(listener);
} }
finally { finally {
reader.dispose(); reader.dispose();
+1 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId> <artifactId>imageio</artifactId>
<version>3.7.0</version> <version>3.7.2-SNAPSHOT</version>
</parent> </parent>
<artifactId>imageio-bmp</artifactId> <artifactId>imageio-bmp</artifactId>
<name>TwelveMonkeys :: ImageIO :: BMP plugin</name> <name>TwelveMonkeys :: ImageIO :: BMP plugin</name>
@@ -30,6 +30,22 @@
package com.twelvemonkeys.imageio.plugins.bmp; package com.twelvemonkeys.imageio.plugins.bmp;
import com.twelvemonkeys.imageio.ImageReaderBase;
import com.twelvemonkeys.imageio.stream.SubImageInputStream;
import com.twelvemonkeys.imageio.util.IIOUtil;
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
import com.twelvemonkeys.imageio.util.ProgressListenerBase;
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.*;
import java.awt.color.ColorSpace; import java.awt.color.ColorSpace;
import java.awt.image.*; import java.awt.image.*;
@@ -40,27 +56,6 @@ import java.nio.ByteOrder;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator; 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;
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
import com.twelvemonkeys.imageio.util.ProgressListenerBase;
import com.twelvemonkeys.io.LittleEndianDataInputStream;
import com.twelvemonkeys.io.enc.DecoderStream;
import com.twelvemonkeys.xml.XMLSerializer;
/** /**
* ImageReader for Microsoft Windows Bitmap (BMP) format. * ImageReader for Microsoft Windows Bitmap (BMP) format.
* *
@@ -482,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, 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 { 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 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); input.skipBytes(rowDataByte.length);
return; return;
@@ -498,19 +497,17 @@ public final class BMPImageReader extends ImageReaderBase {
} }
} }
if (header.topDown) { destChannel.setDataElements(0, dstY, srcChannel);
destChannel.setDataElements(0, y, srcChannel);
} else {
// Flip into position
int dstY = (height - 1 - y - srcRegion.y) / ySub;
destChannel.setDataElements(0, dstY, srcChannel);
}
} }
private void readRowUShort(final DataInput input, final int height, final Rectangle srcRegion, final int xSub, final int ySub, 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 { 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 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); input.skipBytes(rowDataUShort.length * 2 + (rowDataUShort.length % 2) * 2);
return; return;
@@ -530,19 +527,17 @@ public final class BMPImageReader extends ImageReaderBase {
} }
} }
if (header.topDown) { destChannel.setDataElements(0, dstY, srcChannel);
destChannel.setDataElements(0, y, srcChannel);
} else {
// Flip into position
int dstY = (height - 1 - y - srcRegion.y) / ySub;
destChannel.setDataElements(0, dstY, srcChannel);
}
} }
private void readRowInt(final DataInput input, final int height, final Rectangle srcRegion, final int xSub, final int ySub, 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 { 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 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); input.skipBytes(rowDataInt.length * 4);
return; return;
@@ -557,13 +552,7 @@ public final class BMPImageReader extends ImageReaderBase {
} }
} }
if (header.topDown) { destChannel.setDataElements(0, dstY, srcChannel);
destChannel.setDataElements(0, y, srcChannel);
} else {
// Flip into position
int dstY = (height - 1 - y - srcRegion.y) / ySub;
destChannel.setDataElements(0, dstY, srcChannel);
}
} }
// TODO: Candidate util method // TODO: Candidate util method
@@ -32,8 +32,8 @@ package com.twelvemonkeys.imageio.plugins.bmp;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.junit.Assume.assumeNoException; import static org.junit.Assume.assumeNoException;
import static org.mockito.Matchers.anyInt; import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.Matchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock; 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 // At least imageStarted and imageComplete, plus any number of imageProgress
InOrder ordered = inOrder(listener); InOrder ordered = inOrder(listener);
ordered.verify(listener).imageStarted(reader, 0); 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); 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 // At least imageStarted and imageComplete, plus any number of imageProgress
InOrder ordered = inOrder(listener); InOrder ordered = inOrder(listener);
ordered.verify(listener).imageStarted(reader, 0); 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); ordered.verify(listener).imageComplete(reader);
} }
+1 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId> <artifactId>imageio</artifactId>
<version>3.7.0</version> <version>3.7.2-SNAPSHOT</version>
</parent> </parent>
<artifactId>imageio-clippath</artifactId> <artifactId>imageio-clippath</artifactId>
<name>TwelveMonkeys :: ImageIO :: Photoshop Path Support</name> <name>TwelveMonkeys :: ImageIO :: Photoshop Path Support</name>
@@ -39,6 +39,7 @@ import java.io.IOException;
* *
* @deprecated Use {@link AdobePathReader} instead. This class will be removed in a future release. * @deprecated Use {@link AdobePathReader} instead. This class will be removed in a future release.
*/ */
@Deprecated
public final class AdobePathBuilder { public final class AdobePathBuilder {
private final AdobePathReader delegate; private final AdobePathReader delegate;
+1 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId> <artifactId>imageio</artifactId>
<version>3.7.0</version> <version>3.7.2-SNAPSHOT</version>
</parent> </parent>
<artifactId>imageio-core</artifactId> <artifactId>imageio-core</artifactId>
<name>TwelveMonkeys :: ImageIO :: Core</name> <name>TwelveMonkeys :: ImageIO :: Core</name>
@@ -39,6 +39,7 @@ import com.twelvemonkeys.util.LRUHashMap;
import java.awt.color.ColorSpace; import java.awt.color.ColorSpace;
import java.awt.color.ICC_ColorSpace; import java.awt.color.ICC_ColorSpace;
import java.awt.color.ICC_Profile; import java.awt.color.ICC_Profile;
import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
@@ -96,6 +97,8 @@ public final class ColorSpaces {
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
public static final int CS_GENERIC_CMYK = 5001; 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 // Weak references to hold the color spaces while cached
private static WeakReference<ICC_Profile> adobeRGB1998 = new WeakReference<>(null); private static WeakReference<ICC_Profile> adobeRGB1998 = new WeakReference<>(null);
private static WeakReference<ICC_Profile> genericCMYK = 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); private static final Map<Key, ICC_ColorSpace> cache = new LRUHashMap<>(10);
static { static {
try { // In case we didn't activate through SPI already
// Force invocation of ProfileDeferralMgr.activateProfiles() to avoid JDK-6986863 ProfileDeferralActivator.activateProfiles();
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();
}
} }
private ColorSpaces() {} private ColorSpaces() {}
@@ -148,9 +143,11 @@ public final class ColorSpaces {
private static byte[] getProfileHeaderWithProfileId(final ICC_Profile profile) { private static byte[] getProfileHeaderWithProfileId(final ICC_Profile profile) {
// Get *entire profile data*... :-/ // 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 // - LCMS updates CMM + creator to "lcms" and platform to current platform
// - KCMS keeps the values in the file... // - KCMS keeps the values in the file...
Arrays.fill(data, ICC_Profile.icHdrCmmId, ICC_Profile.icHdrCmmId + 4, (byte) 0); 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); System.arraycopy(md5, 0, data, ICC_Profile.icHdrProfileID, md5.length);
// ICC profile header is the first 128 bytes // 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) { private static byte[] computeMD5(byte[] data) {
@@ -203,25 +200,35 @@ public final class ColorSpaces {
Key key = new Key(profileHeader); Key key = new Key(profileHeader);
synchronized (cache) { synchronized (cache) {
ICC_ColorSpace cs = cache.get(key); ICC_ColorSpace cs = getCachedCS(key);
if (cs == null) { if (cs == null) {
cs = new ICC_ColorSpace(profile); cs = new ICC_ColorSpace(profile);
validateColorSpace(cs); 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); 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; 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) { private static void validateColorSpace(final ICC_ColorSpace cs) {
// Validate the color space, to avoid caching bad profiles/color spaces // Validate the color space, to avoid caching bad profiles/color spaces
// Will throw IllegalArgumentException or CMMException if the profile is bad // 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. * @return {@code true} if {@code profile} is equal to the default sRGB profile.
* @throws IllegalArgumentException if {@code profile} is {@code null} * @throws IllegalArgumentException if {@code profile} is {@code null}
* *
* @see java.awt.color.ColorSpace#CS_sRGB
* @see java.awt.color.ColorSpace#isCS_sRGB() * @see java.awt.color.ColorSpace#isCS_sRGB()
*/ */
public static boolean isCS_sRGB(final ICC_Profile profile) { 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); 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}. * Tests whether an ICC color profile is known to cause problems for {@link java.awt.image.ColorConvertOp}.
* <p> * <p>
@@ -302,6 +325,137 @@ public final class ColorSpaces {
return profile; 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. * Returns the color space specified by the given color space constant.
* <p> * <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 class sRGB {
private static final byte[] header = getProfileHeaderWithProfileId(ICC_Profile.getInstance(ColorSpace.CS_sRGB)); private static final byte[] header = getProfileHeaderWithProfileId(ICC_Profile.getInstance(ColorSpace.CS_sRGB));
} }
@@ -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();
}
}
}
@@ -45,36 +45,82 @@ public final class YCbCrConverter {
private final static int CENTERJSAMPLE = 128; private final static int CENTERJSAMPLE = 128;
private final static int ONE_HALF = 1 << (SCALEBITS - 1); private final static int ONE_HALF = 1 << (SCALEBITS - 1);
private final static int[] Cr_R_LUT = new int[MAXJSAMPLE + 1]; private final static class JPEG {
private final static int[] Cb_B_LUT = new int[MAXJSAMPLE + 1]; private final static int[] Cr_R_LUT = new int[MAXJSAMPLE + 1];
private final static int[] Cr_G_LUT = new int[MAXJSAMPLE + 1]; private final static int[] Cb_B_LUT = new int[MAXJSAMPLE + 1];
private final static int[] Cb_G_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. * Initializes tables for YCC->RGB color space conversion.
*/ */
private static void buildYCCtoRGBtable() { private static void buildYCCtoRGBtable() {
if (ColorSpaces.DEBUG) { if (ColorSpaces.DEBUG) {
System.err.println("Building YCC conversion table"); 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++) { static {
// i is the actual input pixel value, in the range 0..MAXJSAMPLE buildYCCtoRGBtable();
// 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 { private final static class ITU_R_601 {
buildYCCtoRGBtable(); 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) { 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); rgb[offset + 1] = clamp(green);
} }
public static void convertYCbCr2RGB(final byte[] yCbCr, final byte[] rgb, final int offset) { public static void convertJPEGYCbCr2RGB(final byte[] yCbCr, final byte[] rgb, final int offset) {
int y = yCbCr[offset] & 0xff; int y = yCbCr[offset ] & 0xff;
int cr = yCbCr[offset + 2] & 0xff;
int cb = yCbCr[offset + 1] & 0xff; int cb = yCbCr[offset + 1] & 0xff;
int cr = yCbCr[offset + 2] & 0xff;
rgb[offset] = clamp(y + Cr_R_LUT[cr]); rgb[offset ] = clamp(y + JPEG.Cr_R_LUT[cr]);
rgb[offset + 1] = clamp(y + (Cb_G_LUT[cb] + Cr_G_LUT[cr] >> SCALEBITS)); rgb[offset + 1] = clamp(y + (JPEG.Cb_G_LUT[cb] + JPEG.Cr_G_LUT[cr] >> SCALEBITS));
rgb[offset + 2] = clamp(y + Cb_B_LUT[cb]); 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)); return (byte) Math.max(0, Math.min(255, val));
} }
} }
@@ -93,8 +93,8 @@ public final class BufferedFileImageInputStream extends ImageInputStreamImpl {
@SuppressWarnings("BooleanMethodIsAlwaysInverted") @SuppressWarnings("BooleanMethodIsAlwaysInverted")
private boolean fillBuffer() throws IOException { private boolean fillBuffer() throws IOException {
bufferPos = 0;
int length = raf.read(buffer, 0, buffer.length); int length = raf.read(buffer, 0, buffer.length);
bufferPos = 0;
bufferLimit = max(length, 0); bufferLimit = max(length, 0);
return bufferLimit > 0; return bufferLimit > 0;
@@ -125,27 +125,35 @@ public final class BufferedFileImageInputStream extends ImageInputStreamImpl {
} }
@Override @Override
public int read(final byte[] pBuffer, final int pOffset, final int pLength) throws IOException { public int read(final byte[] bytes, final int offset, final int length) throws IOException {
checkClosed(); checkClosed();
bitOffset = 0; bitOffset = 0;
if (bufferEmpty()) { if (bufferEmpty()) {
// Bypass buffer if buffer is empty for reads longer than buffer // Bypass buffer if buffer is empty for reads longer than buffer
if (pLength >= buffer.length) { if (length >= buffer.length) {
return readDirect(pBuffer, pOffset, pLength); return readDirect(bytes, offset, length);
} }
else if (!fillBuffer()) { else if (!fillBuffer()) {
return -1; return -1;
} }
} }
return readBuffered(pBuffer, pOffset, pLength); 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[] pBuffer, final int pOffset, final int pLength) throws IOException { 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. // Invalidate the buffer, as its contents is no longer in sync with the stream's position.
bufferLimit = 0; bufferLimit = 0;
int read = raf.read(pBuffer, pOffset, pLength); int read = raf.read(bytes, offset, length);
if (read > 0) { if (read > 0) {
streamPos += read; streamPos += read;
@@ -154,17 +162,17 @@ public final class BufferedFileImageInputStream extends ImageInputStreamImpl {
return read; return read;
} }
private int readBuffered(final byte[] pBuffer, final int pOffset, final int pLength) { private int readBuffered(final byte[] bytes, final int offset, final int length) {
// Read as much as possible from buffer // Read as much as possible from buffer
int length = Math.min(bufferLimit - bufferPos, pLength); int available = Math.min(bufferLimit - bufferPos, length);
if (length > 0) { if (available > 0) {
System.arraycopy(buffer, bufferPos, pBuffer, pOffset, length); System.arraycopy(buffer, bufferPos, bytes, offset, available);
bufferPos += length; bufferPos += available;
streamPos += length; streamPos += available;
} }
return length; return available;
} }
public long length() { public long length() {
@@ -237,7 +245,7 @@ public final class BufferedFileImageInputStream extends ImageInputStreamImpl {
// Optimized to not invalidate buffer if new position is within current buffer // Optimized to not invalidate buffer if new position is within current buffer
long newBufferPos = bufferPos + position - streamPos; long newBufferPos = bufferPos + position - streamPos;
if (newBufferPos >= 0 && newBufferPos <= bufferLimit) { if (newBufferPos >= 0 && newBufferPos < bufferLimit) {
bufferPos = (int) newBufferPos; bufferPos = (int) newBufferPos;
} }
else { else {
@@ -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 registry the registry to unregister from.
* @param provider the provider to unregister. * @param provider the provider to unregister.
* @param category the category to unregister from. * @param category the category to unregister from.
*/ */
public static <T> void deregisterProvider(final ServiceRegistry registry, final IIOServiceProvider provider, final Class<T> category) { 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); 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 registry the registry to lookup from.
* @param providerClassName name of the provider class. * @param providerClassName name of the provider class.
@@ -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;
}
}
@@ -35,6 +35,7 @@ import org.junit.Test;
import java.awt.color.ColorSpace; import java.awt.color.ColorSpace;
import java.awt.color.ICC_ColorSpace; import java.awt.color.ICC_ColorSpace;
import java.awt.color.ICC_Profile; import java.awt.color.ICC_Profile;
import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import static org.junit.Assert.*; import static org.junit.Assert.*;
@@ -184,9 +185,27 @@ public class ColorSpacesTest {
ColorSpaces.isCS_sRGB(null); 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 @Test
public void testEqualHeadersDifferentProfile() throws IOException { 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 profile1 = ICC_Profile.getInstance(getClass().getResourceAsStream("/profiles/adobe_rgb_1998.icc"));
ICC_Profile profile2 = ICC_Profile.getInstance(getClass().getResourceAsStream("/profiles/color_match_rgb.icc")); ICC_Profile profile2 = ICC_Profile.getInstance(getClass().getResourceAsStream("/profiles/color_match_rgb.icc"));
@@ -197,4 +216,69 @@ public class ColorSpacesTest {
assertNotSame(cs1, cs2); 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);
}
} }
@@ -39,8 +39,6 @@ import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
public class KCMSSanitizerStrategyTest { public class KCMSSanitizerStrategyTest {
@@ -67,8 +67,9 @@ public class BufferedFileImageInputStreamTest {
@Test @Test
public void testCreate() throws IOException { public void testCreate() throws IOException {
BufferedFileImageInputStream stream = new BufferedFileImageInputStream(File.createTempFile("empty", ".tmp")); try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(File.createTempFile("empty", ".tmp"))) {
assertEquals("Data length should be same as stream length", 0, stream.length()); assertEquals("Data length should be same as stream length", 0, stream.length());
}
} }
@Test @Test
@@ -104,12 +105,12 @@ public class BufferedFileImageInputStreamTest {
byte[] data = new byte[1024 * 1024]; byte[] data = new byte[1024 * 1024];
File file = randomDataToFile(data); File file = randomDataToFile(data);
BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file); try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file)) {
assertEquals("File length should be same as stream length", file.length(), stream.length());
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());
for (byte value : data) { }
assertEquals("Wrong data read", value & 0xff, stream.read());
} }
} }
@@ -118,15 +119,15 @@ public class BufferedFileImageInputStreamTest {
byte[] data = new byte[1024 * 1024]; byte[] data = new byte[1024 * 1024];
File file = randomDataToFile(data); File file = randomDataToFile(data);
BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file); try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file)) {
assertEquals("File length should be same as stream length", file.length(), stream.length());
assertEquals("File length should be same as stream length", file.length(), stream.length()); byte[] result = new byte[1024];
byte[] result = new byte[1024]; for (int i = 0; i < data.length / result.length; i++) {
stream.readFully(result);
for (int i = 0; i < data.length / result.length; i++) { assertTrue("Wrong data read: " + i, rangeEquals(data, i * result.length, result, 0, result.length));
stream.readFully(result); }
assertTrue("Wrong data read: " + i, rangeEquals(data, i * result.length, result, 0, result.length));
} }
} }
@@ -135,16 +136,16 @@ public class BufferedFileImageInputStreamTest {
byte[] data = new byte[1024 * 14]; byte[] data = new byte[1024 * 14];
File file = randomDataToFile(data); File file = randomDataToFile(data);
BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file); try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file)) {
assertEquals("File length should be same as stream length", file.length(), stream.length());
assertEquals("File length should be same as stream length", file.length(), stream.length()); byte[] result = new byte[7];
byte[] result = new byte[7]; for (int i = 0; i < data.length / result.length; i += 2) {
stream.readFully(result);
for (int i = 0; i < data.length / result.length; i += 2) { stream.skipBytes(result.length);
stream.readFully(result); assertTrue("Wrong data read: " + i, rangeEquals(data, i * result.length, result, 0, result.length));
stream.skipBytes(result.length); }
assertTrue("Wrong data read: " + i, rangeEquals(data, i * result.length, result, 0, result.length));
} }
} }
@@ -153,19 +154,35 @@ public class BufferedFileImageInputStreamTest {
byte[] data = new byte[1024 * 18]; byte[] data = new byte[1024 * 18];
File file = randomDataToFile(data); File file = randomDataToFile(data);
BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file); try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file)) {
assertEquals("File length should be same as stream length", file.length(), stream.length());
assertEquals("File length should be same as stream length", file.length(), stream.length()); byte[] result = new byte[9];
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));
}
}
}
for (int i = 0; i < data.length / result.length; i++) { @Test
// Read backwards public void testReadOutsideDataSeek0Read() throws IOException {
long newPos = stream.length() - result.length - i * result.length; byte[] data = new byte[256];
stream.seek(newPos); File file = randomDataToFile(data);
assertEquals("Wrong stream position", newPos, stream.getStreamPosition());
stream.readFully(result); try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file)) {
assertTrue("Wrong data read: " + i, rangeEquals(data, (int) newPos, result, 0, result.length)); 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));
} }
} }
@@ -176,10 +193,10 @@ public class BufferedFileImageInputStreamTest {
long value = ByteBuffer.wrap(bytes).getLong(); long value = ByteBuffer.wrap(bytes).getLong();
// Create stream // Create stream
ImageInputStream stream = new BufferedFileImageInputStream(file); try (ImageInputStream stream = new BufferedFileImageInputStream(file)) {
for (int i = 1; i <= 64; i++) {
for (int i = 1; i <= 64; i++) { assertEquals(String.format("bit %d differ", i), (value << (i - 1L)) >>> 63L, stream.readBit());
assertEquals(String.format("bit %d differ", i), (value << (i - 1L)) >>> 63L, stream.readBit()); }
} }
} }
@@ -190,12 +207,12 @@ public class BufferedFileImageInputStreamTest {
long value = ByteBuffer.wrap(bytes).getLong(); long value = ByteBuffer.wrap(bytes).getLong();
// Create stream // Create stream
ImageInputStream stream = new BufferedFileImageInputStream(file); try (ImageInputStream stream = new BufferedFileImageInputStream(file)) {
for (int i = 1; i <= 64; i++) {
for (int i = 1; i <= 64; i++) { stream.seek(0);
stream.seek(0); assertEquals(String.format("bit %d differ", i), value >>> (64L - i), stream.readBits(i));
assertEquals(String.format("bit %d differ", i), value >>> (64L - i), stream.readBits(i)); assertEquals(i % 8, stream.getBitOffset());
assertEquals(i % 8, stream.getBitOffset()); }
} }
} }
@@ -206,13 +223,13 @@ public class BufferedFileImageInputStreamTest {
long value = ByteBuffer.wrap(bytes).getLong(); long value = ByteBuffer.wrap(bytes).getLong();
// Create stream // Create stream
ImageInputStream stream = new BufferedFileImageInputStream(file); try (ImageInputStream stream = new BufferedFileImageInputStream(file)) {
for (int i = 1; i <= 60; i++) {
for (int i = 1; i <= 60; i++) { stream.seek(0);
stream.seek(0); stream.setBitOffset(i % 8);
stream.setBitOffset(i % 8); assertEquals(String.format("bit %d differ", i), (value << (i % 8)) >>> (64L - i), stream.readBits(i));
assertEquals(String.format("bit %d differ", i), (value << (i % 8)) >>> (64L - i), stream.readBits(i)); assertEquals(i * 2 % 8, stream.getBitOffset());
assertEquals(i * 2 % 8, stream.getBitOffset()); }
} }
} }
@@ -221,35 +238,37 @@ public class BufferedFileImageInputStreamTest {
byte[] bytes = new byte[8743]; // Slightly more than one buffer size byte[] bytes = new byte[8743]; // Slightly more than one buffer size
File file = randomDataToFile(bytes); File file = randomDataToFile(bytes);
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN); ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
final ImageInputStream stream = new BufferedFileImageInputStream(file);
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
for (int i = 0; i < bytes.length / 2; i++) { try (final ImageInputStream stream = new BufferedFileImageInputStream(file)) {
assertEquals(buffer.getShort(), stream.readShort()); stream.setByteOrder(ByteOrder.BIG_ENDIAN);
}
assertThrows(EOFException.class, new ThrowingRunnable() { for (int i = 0; i < bytes.length / 2; i++) {
@Override assertEquals(buffer.getShort(), stream.readShort());
public void run() throws Throwable {
stream.readShort();
} }
});
stream.seek(0); assertThrows(EOFException.class, new ThrowingRunnable() {
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN); @Override
buffer.position(0); public void run() throws Throwable {
buffer.order(ByteOrder.LITTLE_ENDIAN); stream.readShort();
}
});
for (int i = 0; i < bytes.length / 2; i++) { stream.seek(0);
assertEquals(buffer.getShort(), stream.readShort()); stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
} buffer.position(0);
buffer.order(ByteOrder.LITTLE_ENDIAN);
assertThrows(EOFException.class, new ThrowingRunnable() { for (int i = 0; i < bytes.length / 2; i++) {
@Override assertEquals(buffer.getShort(), stream.readShort());
public void run() throws Throwable {
stream.readShort();
} }
});
assertThrows(EOFException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
stream.readShort();
}
});
}
} }
@Test @Test
@@ -257,35 +276,37 @@ public class BufferedFileImageInputStreamTest {
byte[] bytes = new byte[8743]; // Slightly more than one buffer size byte[] bytes = new byte[8743]; // Slightly more than one buffer size
File file = randomDataToFile(bytes); File file = randomDataToFile(bytes);
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN); ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
final ImageInputStream stream = new BufferedFileImageInputStream(file);
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
for (int i = 0; i < bytes.length / 4; i++) { try (final ImageInputStream stream = new BufferedFileImageInputStream(file)) {
assertEquals(buffer.getInt(), stream.readInt()); stream.setByteOrder(ByteOrder.BIG_ENDIAN);
}
assertThrows(EOFException.class, new ThrowingRunnable() { for (int i = 0; i < bytes.length / 4; i++) {
@Override assertEquals(buffer.getInt(), stream.readInt());
public void run() throws Throwable {
stream.readInt();
} }
});
stream.seek(0); assertThrows(EOFException.class, new ThrowingRunnable() {
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN); @Override
buffer.position(0); public void run() throws Throwable {
buffer.order(ByteOrder.LITTLE_ENDIAN); stream.readInt();
}
});
for (int i = 0; i < bytes.length / 4; i++) { stream.seek(0);
assertEquals(buffer.getInt(), stream.readInt()); stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
} buffer.position(0);
buffer.order(ByteOrder.LITTLE_ENDIAN);
assertThrows(EOFException.class, new ThrowingRunnable() { for (int i = 0; i < bytes.length / 4; i++) {
@Override assertEquals(buffer.getInt(), stream.readInt());
public void run() throws Throwable {
stream.readInt();
} }
});
assertThrows(EOFException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
stream.readInt();
}
});
}
} }
@Test @Test
@@ -293,35 +314,37 @@ public class BufferedFileImageInputStreamTest {
byte[] bytes = new byte[8743]; // Slightly more than one buffer size byte[] bytes = new byte[8743]; // Slightly more than one buffer size
File file = randomDataToFile(bytes); File file = randomDataToFile(bytes);
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN); ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
final ImageInputStream stream = new BufferedFileImageInputStream(file);
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
for (int i = 0; i < bytes.length / 8; i++) { try (final ImageInputStream stream = new BufferedFileImageInputStream(file)) {
assertEquals(buffer.getLong(), stream.readLong()); stream.setByteOrder(ByteOrder.BIG_ENDIAN);
}
assertThrows(EOFException.class, new ThrowingRunnable() { for (int i = 0; i < bytes.length / 8; i++) {
@Override assertEquals(buffer.getLong(), stream.readLong());
public void run() throws Throwable {
stream.readLong();
} }
});
stream.seek(0); assertThrows(EOFException.class, new ThrowingRunnable() {
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN); @Override
buffer.position(0); public void run() throws Throwable {
buffer.order(ByteOrder.LITTLE_ENDIAN); stream.readLong();
}
});
for (int i = 0; i < bytes.length / 8; i++) { stream.seek(0);
assertEquals(buffer.getLong(), stream.readLong()); stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
} buffer.position(0);
buffer.order(ByteOrder.LITTLE_ENDIAN);
assertThrows(EOFException.class, new ThrowingRunnable() { for (int i = 0; i < bytes.length / 8; i++) {
@Override assertEquals(buffer.getLong(), stream.readLong());
public void run() throws Throwable {
stream.readLong();
} }
});
assertThrows(EOFException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
stream.readLong();
}
});
}
} }
@Test @Test
@@ -329,49 +352,50 @@ public class BufferedFileImageInputStreamTest {
byte[] bytes = new byte[9]; byte[] bytes = new byte[9];
File file = randomDataToFile(bytes); File file = randomDataToFile(bytes);
final ImageInputStream stream = new BufferedFileImageInputStream(file); try (final ImageInputStream stream = new BufferedFileImageInputStream(file)) {
stream.seek(1000); stream.seek(1000);
assertEquals(-1, stream.read()); assertEquals(-1, stream.read());
assertEquals(-1, stream.read(new byte[1], 0, 1)); assertEquals(-1, stream.read(new byte[1], 0, 1));
assertThrows(EOFException.class, new ThrowingRunnable() { assertThrows(EOFException.class, new ThrowingRunnable() {
@Override @Override
public void run() throws Throwable { public void run() throws Throwable {
stream.readFully(new byte[1]); stream.readFully(new byte[1]);
} }
}); });
assertThrows(EOFException.class, new ThrowingRunnable() { assertThrows(EOFException.class, new ThrowingRunnable() {
@Override @Override
public void run() throws Throwable { public void run() throws Throwable {
stream.readByte(); stream.readByte();
} }
}); });
assertThrows(EOFException.class, new ThrowingRunnable() { assertThrows(EOFException.class, new ThrowingRunnable() {
@Override @Override
public void run() throws Throwable { public void run() throws Throwable {
stream.readShort(); stream.readShort();
} }
}); });
assertThrows(EOFException.class, new ThrowingRunnable() { assertThrows(EOFException.class, new ThrowingRunnable() {
@Override @Override
public void run() throws Throwable { public void run() throws Throwable {
stream.readInt(); stream.readInt();
} }
}); });
assertThrows(EOFException.class, new ThrowingRunnable() { assertThrows(EOFException.class, new ThrowingRunnable() {
@Override @Override
public void run() throws Throwable { public void run() throws Throwable {
stream.readLong(); stream.readLong();
} }
}); });
stream.seek(0); stream.seek(0);
for (byte value : bytes) { for (byte value : bytes) {
assertEquals(value, stream.readByte()); assertEquals(value, stream.readByte());
}
assertEquals(-1, stream.read());
} }
assertEquals(-1, stream.read());
} }
@Test @Test
@@ -383,4 +407,23 @@ public class BufferedFileImageInputStreamTest {
stream.close(); stream.close();
verify(mock, only()).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);
}
}
} }
@@ -45,9 +45,8 @@ import javax.imageio.spi.IIORegistry;
import javax.imageio.spi.ImageReaderSpi; import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.ImageInputStream;
import java.awt.*; import java.awt.*;
import java.awt.image.BufferedImage; import java.awt.geom.AffineTransform;
import java.awt.image.RenderedImage; import java.awt.image.*;
import java.awt.image.SampleModel;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
@@ -57,6 +56,7 @@ import java.util.Arrays;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import static java.lang.Math.min;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
@@ -1151,7 +1151,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
// At least imageStarted and imageComplete, plus any number of imageProgress // At least imageStarted and imageComplete, plus any number of imageProgress
InOrder ordered = inOrder(listener); InOrder ordered = inOrder(listener);
ordered.verify(listener).imageStarted(reader, 0); 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); ordered.verify(listener).imageComplete(reader);
reader.dispose(); reader.dispose();
} }
@@ -1184,9 +1184,9 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
ordered.verify(listenerToo).imageStarted(reader, 0); ordered.verify(listenerToo).imageStarted(reader, 0);
ordered.verify(listenerThree).imageStarted(reader, 0); ordered.verify(listenerThree).imageStarted(reader, 0);
ordered.verify(listener, atLeastOnce()).imageProgress(eq(reader), anyInt()); ordered.verify(listener, atLeastOnce()).imageProgress(eq(reader), anyFloat());
ordered.verify(listenerToo, atLeastOnce()).imageProgress(eq(reader), anyInt()); ordered.verify(listenerToo, atLeastOnce()).imageProgress(eq(reader), anyFloat());
ordered.verify(listenerThree, atLeastOnce()).imageProgress(eq(reader), anyInt()); ordered.verify(listenerThree, atLeastOnce()).imageProgress(eq(reader), anyFloat());
ordered.verify(listener).imageComplete(reader); ordered.verify(listener).imageComplete(reader);
ordered.verify(listenerToo).imageComplete(reader); ordered.verify(listenerToo).imageComplete(reader);
@@ -1226,7 +1226,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
// Should not have called any methods... // Should not have called any methods...
verifyZeroInteractions(listener); verifyNoInteractions(listener);
reader.dispose(); reader.dispose();
} }
@@ -1253,11 +1253,11 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
// Should not have called any methods on listener1... // Should not have called any methods on listener1...
verifyZeroInteractions(listener); verifyNoInteractions(listener);
InOrder ordered = inOrder(listenerToo); InOrder ordered = inOrder(listenerToo);
ordered.verify(listenerToo).imageStarted(reader, 0); 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); ordered.verify(listenerToo).imageComplete(reader);
reader.dispose(); reader.dispose();
} }
@@ -1281,7 +1281,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
// Should not have called any methods... // Should not have called any methods...
verifyZeroInteractions(listener); verifyNoInteractions(listener);
reader.dispose(); reader.dispose();
} }
@@ -1307,8 +1307,8 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
// Should not have called any methods... // Should not have called any methods...
verifyZeroInteractions(listener); verifyNoInteractions(listener);
verifyZeroInteractions(listenerToo); verifyNoInteractions(listenerToo);
reader.dispose(); reader.dispose();
} }
@@ -1333,7 +1333,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
}; };
doAnswer(abort).when(abortingListener).imageStarted(any(ImageReader.class), anyInt()); 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); reader.addIIOReadProgressListener(abortingListener);
@@ -1738,6 +1738,43 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
reader.dispose(); 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") @Ignore("TODO: Implement")
@Test @Test
public void testSetDestinationBands() { public void testSetDestinationBands() {
@@ -52,7 +52,6 @@ import java.net.URL;
import java.util.List; import java.util.List;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
/** /**
@@ -104,7 +103,7 @@ public abstract class ImageWriterAbstractTest<T extends ImageWriter> {
return image; return image;
} }
protected final RenderedImage getTestData(final int index) { protected final RenderedImage getTestData(final int index) {
return getTestData().get(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 // At least imageStarted and imageComplete, plus any number of imageProgress
InOrder ordered = inOrder(listener); InOrder ordered = inOrder(listener);
ordered.verify(listener).imageStarted(writer, 0); 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); ordered.verify(listener).imageComplete(writer);
} }
@@ -251,9 +250,9 @@ public abstract class ImageWriterAbstractTest<T extends ImageWriter> {
ordered.verify(listenerToo).imageStarted(writer, 0); ordered.verify(listenerToo).imageStarted(writer, 0);
ordered.verify(listenerThree).imageStarted(writer, 0); ordered.verify(listenerThree).imageStarted(writer, 0);
ordered.verify(listener, atLeastOnce()).imageProgress(eq(writer), anyInt()); ordered.verify(listener, atLeastOnce()).imageProgress(eq(writer), anyFloat());
ordered.verify(listenerToo, atLeastOnce()).imageProgress(eq(writer), anyInt()); ordered.verify(listenerToo, atLeastOnce()).imageProgress(eq(writer), anyFloat());
ordered.verify(listenerThree, atLeastOnce()).imageProgress(eq(writer), anyInt()); ordered.verify(listenerThree, atLeastOnce()).imageProgress(eq(writer), anyFloat());
ordered.verify(listener).imageComplete(writer); ordered.verify(listener).imageComplete(writer);
ordered.verify(listenerToo).imageComplete(writer); ordered.verify(listenerToo).imageComplete(writer);
@@ -290,7 +289,7 @@ public abstract class ImageWriterAbstractTest<T extends ImageWriter> {
} }
// Should not have called any methods... // Should not have called any methods...
verifyZeroInteractions(listener); verifyNoInteractions(listener);
} }
@Test @Test
@@ -315,12 +314,12 @@ public abstract class ImageWriterAbstractTest<T extends ImageWriter> {
} }
// Should not have called any methods... // Should not have called any methods...
verifyZeroInteractions(listener); verifyNoInteractions(listener);
// At least imageStarted and imageComplete, plus any number of imageProgress // At least imageStarted and imageComplete, plus any number of imageProgress
InOrder ordered = inOrder(listenerToo); InOrder ordered = inOrder(listenerToo);
ordered.verify(listenerToo).imageStarted(writer, 0); 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); ordered.verify(listenerToo).imageComplete(writer);
} }
@@ -345,7 +344,7 @@ public abstract class ImageWriterAbstractTest<T extends ImageWriter> {
} }
// Should not have called any methods... // Should not have called any methods...
verifyZeroInteractions(listener); verifyNoInteractions(listener);
} }
@Test @Test
@@ -371,7 +370,7 @@ public abstract class ImageWriterAbstractTest<T extends ImageWriter> {
} }
// Should not have called any methods... // Should not have called any methods...
verifyZeroInteractions(listener); verifyNoInteractions(listener);
verifyZeroInteractions(listenerToo); verifyNoInteractions(listenerToo);
} }
} }
@@ -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));
}
}
}
}
}
+1 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId> <artifactId>imageio</artifactId>
<version>3.7.0</version> <version>3.7.2-SNAPSHOT</version>
</parent> </parent>
<artifactId>imageio-hdr</artifactId> <artifactId>imageio-hdr</artifactId>
<name>TwelveMonkeys :: ImageIO :: HDR plugin</name> <name>TwelveMonkeys :: ImageIO :: HDR plugin</name>
@@ -38,12 +38,14 @@ import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import static java.util.Collections.emptyList;
/** /**
* TGAImageReaderTest * HDRImageReaderTest
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$ * @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> { public class HDRImageReaderTest extends ImageReaderAbstractTest<HDRImageReader> {
@Override @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 @Override
protected List<String> getFormatNames() { protected List<String> getFormatNames() {
return Arrays.asList("HDR", "hdr", "RGBE", "rgbe"); return Arrays.asList("HDR", "hdr", "RGBE", "rgbe");
+1 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId> <artifactId>imageio</artifactId>
<version>3.7.0</version> <version>3.7.2-SNAPSHOT</version>
</parent> </parent>
<artifactId>imageio-icns</artifactId> <artifactId>imageio-icns</artifactId>
<name>TwelveMonkeys :: ImageIO :: ICNS plugin</name> <name>TwelveMonkeys :: ImageIO :: ICNS plugin</name>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId> <artifactId>imageio</artifactId>
<version>3.7.0</version> <version>3.7.2-SNAPSHOT</version>
</parent> </parent>
<artifactId>imageio-iff</artifactId> <artifactId>imageio-iff</artifactId>
<name>TwelveMonkeys :: ImageIO :: IFF plugin</name> <name>TwelveMonkeys :: ImageIO :: IFF plugin</name>
+16 -3
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId> <artifactId>imageio</artifactId>
<version>3.7.0</version> <version>3.7.2-SNAPSHOT</version>
</parent> </parent>
<artifactId>imageio-jpeg-jai-interop</artifactId> <artifactId>imageio-jpeg-jai-interop</artifactId>
<name>TwelveMonkeys :: ImageIO :: JPEG/JAI TIFF Interop</name> <name>TwelveMonkeys :: ImageIO :: JPEG/JAI TIFF Interop</name>
@@ -13,14 +13,27 @@
</description> </description>
<properties> <properties>
<project.jpms.module.name>com.twelvemonkeys.imageio.jaiinterop</project.jpms.module.name> <project.jpms.module.name>com.twelvemonkeys.imageio.jpeg.jaiinterop</project.jpms.module.name>
</properties> </properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>com.github.jai-imageio</groupId> <groupId>com.github.jai-imageio</groupId>
<artifactId>jai-imageio-core</artifactId> <artifactId>jai-imageio-core</artifactId>
<version>1.3.0</version> <version>1.4.0</version>
<scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
@@ -78,7 +78,7 @@ public class JAITIFFImageReaderInteroperabilityTest extends ImageReaderAbstractT
@Override @Override
protected List<TestData> getTestData() { protected List<TestData> getTestData() {
return Arrays.asList( return Arrays.asList(
new TestData(getClassLoaderResource("/tiff/foto_0001.tif"), new Dimension(1663, 2338)), // Little endian, Old JPEG 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/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 new TestData(getClassLoaderResource("/tiff/jpeg-lossless-8bit-gray.tif"), new Dimension(512, 512)) // Lossless JPEG Gray, 8 bit/sample
); );
+14 -2
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId> <artifactId>imageio</artifactId>
<version>3.7.0</version> <version>3.7.2-SNAPSHOT</version>
</parent> </parent>
<artifactId>imageio-jpeg-jep262-interop</artifactId> <artifactId>imageio-jpeg-jep262-interop</artifactId>
<name>TwelveMonkeys :: ImageIO :: JPEG/JEP-262 Interop</name> <name>TwelveMonkeys :: ImageIO :: JPEG/JEP-262 Interop</name>
@@ -13,9 +13,21 @@
</description> </description>
<properties> <properties>
<project.jpms.module.name>com.twelvemonkeys.imageio.jep262interop</project.jpms.module.name> <project.jpms.module.name>com.twelvemonkeys.imageio.jpeg.jep262interop</project.jpms.module.name>
</properties> </properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
@@ -33,6 +33,7 @@ package com.twelvemonkeys.imageio.plugins.jpeg.jep262interop;
import com.twelvemonkeys.imageio.plugins.jpeg.JPEGImageReaderSpi; import com.twelvemonkeys.imageio.plugins.jpeg.JPEGImageReaderSpi;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest; import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
import org.junit.AssumptionViolatedException;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
@@ -48,7 +49,6 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
/** /**
* Tests the JEP 262 TIFFImageReader delegating to our JPEGImageReader. * Tests the JEP 262 TIFFImageReader delegating to our JPEGImageReader.
@@ -74,15 +74,13 @@ public class JEP262TIFFImageReaderInteroperabilityTest extends ImageReaderAbstra
} }
// Skip tests if we have no Spi (ie. pre JDK 9) // Skip tests if we have no Spi (ie. pre JDK 9)
assumeTrue("Provider " + JEP_262_PROVIDER_CLASS_NAME + " not found", false); throw new AssumptionViolatedException("Provider " + JEP_262_PROVIDER_CLASS_NAME + " not found");
return null;
} }
@Override @Override
protected List<TestData> getTestData() { protected List<TestData> getTestData() {
return Arrays.asList( return Arrays.asList(
new TestData(getClassLoaderResource("/tiff/foto_0001.tif"), new Dimension(1663, 2338)), // Little endian, Old JPEG 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/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 new TestData(getClassLoaderResource("/tiff/jpeg-lossless-8bit-gray.tif"), new Dimension(512, 512)) // Lossless JPEG Gray, 8 bit/sample
); );
+1 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId> <artifactId>imageio</artifactId>
<version>3.7.0</version> <version>3.7.2-SNAPSHOT</version>
</parent> </parent>
<artifactId>imageio-jpeg</artifactId> <artifactId>imageio-jpeg</artifactId>
<name>TwelveMonkeys :: ImageIO :: JPEG plugin</name> <name>TwelveMonkeys :: ImageIO :: JPEG plugin</name>
@@ -119,7 +119,7 @@ final class EXIFThumbnail {
case 6: case 6:
// YCbCr // YCbCr
for (int i = 0; i < thumbLength; i += 3) { for (int i = 0; i < thumbLength; i += 3) {
YCbCrConverter.convertYCbCr2RGB(thumbData, thumbData, i); YCbCrConverter.convertJPEGYCbCr2RGB(thumbData, thumbData, i);
} }
break; break;
default: default:
@@ -123,7 +123,7 @@ public final class JPEGImageReader extends ImageReaderBase {
private int currentStreamIndex = 0; private int currentStreamIndex = 0;
private final List<Long> streamOffsets = new ArrayList<>(); private final List<Long> streamOffsets = new ArrayList<>();
protected JPEGImageReader(final ImageReaderSpi provider, final ImageReader delegate) { JPEGImageReader(final ImageReaderSpi provider, final ImageReader delegate) {
super(provider); super(provider);
this.delegate = Validate.notNull(delegate); this.delegate = Validate.notNull(delegate);
@@ -614,7 +614,7 @@ public final class JPEGImageReader extends ImageReaderBase {
} }
} }
private ICC_Profile ensureDisplayProfile(final ICC_Profile profile) { private ICC_Profile ensureDisplayProfile(final ICC_Profile profile) throws IOException {
// NOTE: This is probably not the right way to do it... :-P // NOTE: This is probably not the right way to do it... :-P
// TODO: Consider moving method to ColorSpaces class or new class in imageio.color package // TODO: Consider moving method to ColorSpaces class or new class in imageio.color package
@@ -630,7 +630,7 @@ public final class JPEGImageReader extends ImageReaderBase {
intToBigEndian(ICC_Profile.icSigDisplayClass, profileData, ICC_Profile.icHdrDeviceClass); // Header is first intToBigEndian(ICC_Profile.icSigDisplayClass, profileData, ICC_Profile.icHdrDeviceClass); // Header is first
return ICC_Profile.getInstance(profileData); return ColorSpaces.createProfile(profileData);
} }
} }
@@ -954,7 +954,7 @@ public final class JPEGImageReader extends ImageReaderBase {
int iccChunkDataSize = segment.data.length - segmentDataStart; int iccChunkDataSize = segment.data.length - segmentDataStart;
int iccSize = segment.data.length < segmentDataStart + 4 ? 0 : intFromBigEndian(segment.data, segmentDataStart); int iccSize = segment.data.length < segmentDataStart + 4 ? 0 : intFromBigEndian(segment.data, segmentDataStart);
return readICCProfileSafe(stream, allowBadIndexes, iccSize, iccChunkDataSize); return readICCProfileSafe(stream, allowBadIndexes);
} }
else if (!segments.isEmpty()) { else if (!segments.isEmpty()) {
// NOTE: This is probably over-complicated, as I've never encountered ICC_PROFILE chunks out of order... // NOTE: This is probably over-complicated, as I've never encountered ICC_PROFILE chunks out of order...
@@ -1012,25 +1012,20 @@ public final class JPEGImageReader extends ImageReaderBase {
} }
} }
return readICCProfileSafe(new SequenceInputStream(Collections.enumeration(Arrays.asList(streams))), allowBadIndexes, iccSize, iccChunkDataSize); return readICCProfileSafe(new SequenceInputStream(Collections.enumeration(Arrays.asList(streams))), allowBadIndexes);
} }
return null; return null;
} }
private ICC_Profile readICCProfileSafe(final InputStream stream, final boolean allowBadProfile, final int iccSize, final int iccChunkDataSize) throws IOException { private ICC_Profile readICCProfileSafe(final InputStream stream, final boolean allowBadProfile) {
if (iccSize < 0 || iccSize > iccChunkDataSize) {
processWarningOccurred(String.format("Truncated 'ICC_PROFILE' chunk(s), size: %d. Ignoring ICC profile.", iccSize));
return null;
}
try { try {
ICC_Profile profile = ICC_Profile.getInstance(stream); ICC_Profile profile = ColorSpaces.readProfileRaw(stream);
// NOTE: Need to ensure we have a display profile *before* validating, for the caching to work // NOTE: Need to ensure we have a display profile *before* validating, for the caching to work
return allowBadProfile ? profile : ColorSpaces.validateProfile(ensureDisplayProfile(profile)); return allowBadProfile ? profile : ColorSpaces.validateProfile(ensureDisplayProfile(profile));
} }
catch (RuntimeException e) { catch (IOException | RuntimeException e) {
// NOTE: Throws either IllegalArgumentException or CMMException, depending on platform. // NOTE: Throws either IllegalArgumentException or CMMException, depending on platform.
// Usual reason: Broken tools store truncated ICC profiles in a single ICC_PROFILE chunk... // Usual reason: Broken tools store truncated ICC profiles in a single ICC_PROFILE chunk...
processWarningOccurred(String.format("Bad 'ICC_PROFILE' chunk(s): %s. Ignoring ICC profile.", e.getMessage())); processWarningOccurred(String.format("Bad 'ICC_PROFILE' chunk(s): %s. Ignoring ICC profile.", e.getMessage()));
@@ -1169,7 +1164,7 @@ public final class JPEGImageReader extends ImageReaderBase {
processThumbnailStarted(imageIndex, thumbnailIndex); processThumbnailStarted(imageIndex, thumbnailIndex);
processThumbnailProgress(0f); processThumbnailProgress(0f);
BufferedImage thumbnail = thumbnails.get(thumbnailIndex).read();; BufferedImage thumbnail = thumbnails.get(thumbnailIndex).read();
processThumbnailProgress(100f); processThumbnailProgress(100f);
processThumbnailComplete(); processThumbnailComplete();
@@ -1211,7 +1206,7 @@ public final class JPEGImageReader extends ImageReaderBase {
for (int y = 0; y < height; y++) { for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) { for (int x = 0; x < width; x++) {
YCbCrConverter.convertYCbCr2RGB(data, data, (x + y * width) * numComponents); YCbCrConverter.convertJPEGYCbCr2RGB(data, data, (x + y * width) * numComponents);
} }
} }
} }
@@ -1225,7 +1220,7 @@ public final class JPEGImageReader extends ImageReaderBase {
for (int x = 0; x < width; x++) { for (int x = 0; x < width; x++) {
int offset = (x + y * width) * 4; int offset = (x + y * width) * 4;
// YCC -> CMY // YCC -> CMY
YCbCrConverter.convertYCbCr2RGB(data, data, offset); YCbCrConverter.convertJPEGYCbCr2RGB(data, data, offset);
// Inverse K // Inverse K
data[offset + 3] = (byte) (0xff - data[offset + 3] & 0xff); data[offset + 3] = (byte) (0xff - data[offset + 3] & 0xff);
} }
@@ -36,7 +36,6 @@ import com.twelvemonkeys.lang.StringUtil;
import org.hamcrest.core.IsInstanceOf; import org.hamcrest.core.IsInstanceOf;
import org.junit.Test; import org.junit.Test;
import org.mockito.internal.matchers.GreaterThan;
import org.w3c.dom.Element; import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap; import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node; import org.w3c.dom.Node;
@@ -66,12 +65,11 @@ import static com.twelvemonkeys.imageio.util.IIOUtil.lookupProviderByName;
import static org.hamcrest.CoreMatchers.allOf; import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.greaterThan;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.junit.Assume.assumeNoException; import static org.junit.Assume.assumeNoException;
import static org.junit.Assume.assumeNotNull; import static org.junit.Assume.assumeNotNull;
import static org.mockito.AdditionalMatchers.and; import static org.mockito.AdditionalMatchers.and;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
/** /**
@@ -1456,7 +1454,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
assertTrue(markerSequences.getLength() == 1 || markerSequences.getLength() == 2); // In case of JPEG encoded thumbnail, there will be 2 assertTrue(markerSequences.getLength() == 1 || markerSequences.getLength() == 2); // In case of JPEG encoded thumbnail, there will be 2
IIOMetadataNode markerSequence = (IIOMetadataNode) markerSequences.item(markerSequences.getLength() - 1); // The last will be the "main" image IIOMetadataNode markerSequence = (IIOMetadataNode) markerSequences.item(markerSequences.getLength() - 1); // The last will be the "main" image
assertNotNull(markerSequence); assertNotNull(markerSequence);
assertThat(markerSequence.getChildNodes().getLength(), new GreaterThan<>(0)); assertThat(markerSequence.getChildNodes().getLength(), greaterThan(0));
NodeList unknowns = markerSequence.getElementsByTagName("unknown"); NodeList unknowns = markerSequence.getElementsByTagName("unknown");
for (int j = 0; j < unknowns.getLength(); j++) { for (int j = 0; j < unknowns.getLength(); j++) {
@@ -36,7 +36,6 @@ import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegmentUtil;
import com.twelvemonkeys.imageio.stream.URLImageInputStreamSpi; import com.twelvemonkeys.imageio.stream.URLImageInputStreamSpi;
import org.junit.Test; import org.junit.Test;
import org.mockito.internal.matchers.LessOrEqual;
import javax.imageio.IIOException; import javax.imageio.IIOException;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
@@ -48,6 +47,7 @@ import java.net.URL;
import java.util.List; import java.util.List;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.lessThanOrEqualTo;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@@ -124,7 +124,7 @@ public class JPEGSegmentImageInputStreamTest {
length++; length++;
} }
assertThat(length, new LessOrEqual<>(10203L)); // In no case should length increase assertThat(length, lessThanOrEqualTo(10203L)); // In no case should length increase
assertEquals(9607L, length); // May change, if more chunks are passed to reader... assertEquals(9607L, length); // May change, if more chunks are passed to reader...
} }
+1 -1
View File
@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId> <artifactId>imageio</artifactId>
<version>3.7.0</version> <version>3.7.2-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>imageio-metadata</artifactId> <artifactId>imageio-metadata</artifactId>
@@ -46,6 +46,7 @@ import java.io.IOException;
* *
* @deprecated Use com.twelvemonkeys.imageio.metadata.tiff.TIFFReader instead. * @deprecated Use com.twelvemonkeys.imageio.metadata.tiff.TIFFReader instead.
*/ */
@Deprecated
public final class EXIFReader extends MetadataReader { public final class EXIFReader extends MetadataReader {
private final TIFFReader delegate = new TIFFReader(); private final TIFFReader delegate = new TIFFReader();
@@ -48,6 +48,7 @@ import java.util.Collection;
* *
* @deprecated Use com.twelvemonkeys.imageio.metadata.tiff.TIFFWriter instead. * @deprecated Use com.twelvemonkeys.imageio.metadata.tiff.TIFFWriter instead.
*/ */
@Deprecated
public final class EXIFWriter extends MetadataWriter { public final class EXIFWriter extends MetadataWriter {
private final TIFFWriter delegate = new TIFFWriter(); private final TIFFWriter delegate = new TIFFWriter();
@@ -41,6 +41,7 @@ package com.twelvemonkeys.imageio.metadata.exif;
* *
* @deprecated Use com.twelvemonkeys.imageio.metadata.tiff.Rational instead. * @deprecated Use com.twelvemonkeys.imageio.metadata.tiff.Rational instead.
*/ */
@Deprecated
public final class Rational extends Number implements Comparable<Rational> { public final class Rational extends Number implements Comparable<Rational> {
private final com.twelvemonkeys.imageio.metadata.tiff.Rational delegate; private final com.twelvemonkeys.imageio.metadata.tiff.Rational delegate;
@@ -39,5 +39,6 @@ package com.twelvemonkeys.imageio.metadata.exif;
* *
* @deprecated Use com.twelvemonkeys.imageio.metadata.tiff.TIFF instead. * @deprecated Use com.twelvemonkeys.imageio.metadata.tiff.TIFF instead.
*/ */
@Deprecated
public interface TIFF extends com.twelvemonkeys.imageio.metadata.tiff.TIFF { public interface TIFF extends com.twelvemonkeys.imageio.metadata.tiff.TIFF {
} }
@@ -30,6 +30,7 @@
package com.twelvemonkeys.imageio.metadata.jpeg; package com.twelvemonkeys.imageio.metadata.jpeg;
import com.twelvemonkeys.imageio.color.ColorSpaces;
import com.twelvemonkeys.imageio.metadata.Directory; import com.twelvemonkeys.imageio.metadata.Directory;
import com.twelvemonkeys.imageio.metadata.Entry; import com.twelvemonkeys.imageio.metadata.Entry;
import com.twelvemonkeys.imageio.metadata.psd.PSD; import com.twelvemonkeys.imageio.metadata.psd.PSD;
@@ -42,7 +43,6 @@ import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
import javax.imageio.IIOException; import javax.imageio.IIOException;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.ImageInputStream;
import java.awt.color.ICC_ColorSpace;
import java.awt.color.ICC_Profile; import java.awt.color.ICC_Profile;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.EOFException; import java.io.EOFException;
@@ -353,8 +353,8 @@ public final class JPEGSegmentUtil {
Directory psd = new PSDReader().read(stream); Directory psd = new PSDReader().read(stream);
Entry iccEntry = psd.getEntryById(PSD.RES_ICC_PROFILE); Entry iccEntry = psd.getEntryById(PSD.RES_ICC_PROFILE);
if (iccEntry != null) { if (iccEntry != null) {
ICC_ColorSpace colorSpace = new ICC_ColorSpace(ICC_Profile.getInstance((byte[]) iccEntry.getValue())); ICC_Profile profile = ColorSpaces.createProfileRaw((byte[]) iccEntry.getValue());
System.err.println("colorSpace: " + colorSpace); System.err.println("ICC Profile: " + profile);
} }
System.err.println("PSD: " + psd); System.err.println("PSD: " + psd);
System.err.println(TIFFReader.HexDump.dump(segment.data)); System.err.println(TIFFReader.HexDump.dump(segment.data));
@@ -30,8 +30,15 @@
package com.twelvemonkeys.imageio.metadata.tiff; package com.twelvemonkeys.imageio.metadata.tiff;
import static com.twelvemonkeys.imageio.metadata.tiff.TIFFEntry.getValueLength; import com.twelvemonkeys.imageio.metadata.Directory;
import com.twelvemonkeys.imageio.metadata.Entry;
import com.twelvemonkeys.imageio.metadata.MetadataReader;
import com.twelvemonkeys.lang.StringUtil;
import com.twelvemonkeys.lang.Validate;
import javax.imageio.IIOException;
import javax.imageio.ImageIO;
import javax.imageio.stream.ImageInputStream;
import java.io.EOFException; import java.io.EOFException;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@@ -39,15 +46,7 @@ import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.*; import java.util.*;
import javax.imageio.IIOException; import static com.twelvemonkeys.imageio.metadata.tiff.TIFFEntry.getValueLength;
import javax.imageio.ImageIO;
import javax.imageio.stream.ImageInputStream;
import com.twelvemonkeys.imageio.metadata.Directory;
import com.twelvemonkeys.imageio.metadata.Entry;
import com.twelvemonkeys.imageio.metadata.MetadataReader;
import com.twelvemonkeys.lang.StringUtil;
import com.twelvemonkeys.lang.Validate;
/** /**
* TIFFReader * TIFFReader
@@ -73,16 +72,17 @@ public final class TIFFReader extends MetadataReader {
} }
}; };
map.put(TIFF.TAG_SUB_IFD, Collections.unmodifiableCollection(Collections.singleton(TIFF.TAG_SUB_IFD))); map.put(TIFF.TAG_SUB_IFD, Collections.singleton(TIFF.TAG_SUB_IFD));
map.put(TIFF.TAG_EXIF_IFD, Collections.unmodifiableCollection(Collections.singleton(TIFF.TAG_INTEROP_IFD))); map.put(TIFF.TAG_EXIF_IFD, Collections.singleton(TIFF.TAG_INTEROP_IFD));
return Collections.unmodifiableMap(map); return Collections.unmodifiableMap(map);
} }
private final Set<Long> parsedIFDs = new TreeSet<>();
private long length; private long length;
private boolean longOffsets; private boolean longOffsets;
private int offsetSize; private int offsetSize;
private Set<Long> parsedIFDs = new TreeSet<>();
@Override @Override
public Directory read(final ImageInputStream input) throws IOException { public Directory read(final ImageInputStream input) throws IOException {
@@ -30,23 +30,27 @@
package com.twelvemonkeys.imageio.metadata.tiff; package com.twelvemonkeys.imageio.metadata.tiff;
import static com.twelvemonkeys.imageio.metadata.tiff.TIFFEntry.getType;
import static com.twelvemonkeys.imageio.metadata.tiff.TIFFEntry.getValueLength;
import java.io.IOException;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import javax.imageio.IIOException;
import javax.imageio.stream.ImageOutputStream;
import com.twelvemonkeys.imageio.metadata.CompoundDirectory; import com.twelvemonkeys.imageio.metadata.CompoundDirectory;
import com.twelvemonkeys.imageio.metadata.Directory; import com.twelvemonkeys.imageio.metadata.Directory;
import com.twelvemonkeys.imageio.metadata.Entry; import com.twelvemonkeys.imageio.metadata.Entry;
import com.twelvemonkeys.imageio.metadata.MetadataWriter; import com.twelvemonkeys.imageio.metadata.MetadataWriter;
import com.twelvemonkeys.lang.Validate; import com.twelvemonkeys.lang.Validate;
import javax.imageio.IIOException;
import javax.imageio.stream.ImageOutputStream;
import java.io.IOException;
import java.nio.ByteOrder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;
import static com.twelvemonkeys.imageio.metadata.tiff.TIFFEntry.getType;
import static com.twelvemonkeys.imageio.metadata.tiff.TIFFEntry.getValueLength;
/** /**
* TIFFWriter * TIFFWriter
* *
@@ -58,9 +62,9 @@ public final class TIFFWriter extends MetadataWriter {
private static final int WORD_LENGTH = 2; private static final int WORD_LENGTH = 2;
private static final int LONGWORD_LENGTH = 4; private static final int LONGWORD_LENGTH = 4;
private static final int ENTRY_LENGTH = 12; private static final long ENTRY_LENGTH = 12;
public boolean write(final Collection<Entry> entries, final ImageOutputStream stream) throws IOException { public boolean write(final Collection<? extends Entry> entries, final ImageOutputStream stream) throws IOException {
return write(new IFD(entries), stream); return write(new IFD(entries), stream);
} }
@@ -248,7 +252,7 @@ public final class TIFFWriter extends MetadataWriter {
private int getCount(final Entry entry) { private int getCount(final Entry entry) {
Object value = entry.getValue(); Object value = entry.getValue();
return value instanceof String ? ((String) value).getBytes(Charset.forName("UTF-8")).length + 1 : entry.valueCount(); return value instanceof String ? ((String) value).getBytes(StandardCharsets.UTF_8).length + 1 : entry.valueCount();
} }
private void writeValueInline(final Object value, final short type, final ImageOutputStream stream) throws IOException { private void writeValueInline(final Object value, final short type, final ImageOutputStream stream) throws IOException {
@@ -30,11 +30,21 @@
package com.twelvemonkeys.imageio.metadata.xmp; package com.twelvemonkeys.imageio.metadata.xmp;
import com.twelvemonkeys.imageio.metadata.Directory; import java.io.IOException;
import com.twelvemonkeys.imageio.metadata.Entry; import java.util.ArrayList;
import com.twelvemonkeys.imageio.metadata.MetadataReader; import java.util.Collections;
import com.twelvemonkeys.imageio.util.IIOUtil; import java.util.Iterator;
import com.twelvemonkeys.lang.Validate; import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.imageio.IIOException;
import javax.imageio.stream.ImageInputStream;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap; import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node; import org.w3c.dom.Node;
@@ -43,13 +53,11 @@ import org.xml.sax.InputSource;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.helpers.DefaultHandler;
import javax.imageio.IIOException; import com.twelvemonkeys.imageio.metadata.Directory;
import javax.imageio.stream.ImageInputStream; import com.twelvemonkeys.imageio.metadata.Entry;
import javax.xml.parsers.DocumentBuilder; import com.twelvemonkeys.imageio.metadata.MetadataReader;
import javax.xml.parsers.DocumentBuilderFactory; import com.twelvemonkeys.imageio.util.IIOUtil;
import javax.xml.parsers.ParserConfigurationException; import com.twelvemonkeys.lang.Validate;
import java.io.IOException;
import java.util.*;
/** /**
* XMPReader * XMPReader
@@ -67,10 +75,9 @@ public final class XMPReader extends MetadataReader {
public Directory read(final ImageInputStream input) throws IOException { public Directory read(final ImageInputStream input) throws IOException {
Validate.notNull(input, "input"); Validate.notNull(input, "input");
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
try { try {
DocumentBuilderFactory factory = createDocumentBuilderFactory();
// TODO: Consider parsing using SAX? // TODO: Consider parsing using SAX?
// TODO: Determine encoding and parse using a Reader... // TODO: Determine encoding and parse using a Reader...
// TODO: Refactor scanner to return inputstream? // TODO: Refactor scanner to return inputstream?
@@ -79,9 +86,6 @@ public final class XMPReader extends MetadataReader {
builder.setErrorHandler(new DefaultHandler()); builder.setErrorHandler(new DefaultHandler());
Document document = builder.parse(new InputSource(IIOUtil.createStreamAdapter(input))); Document document = builder.parse(new InputSource(IIOUtil.createStreamAdapter(input)));
// XMLSerializer serializer = new XMLSerializer(System.err, System.getProperty("file.encoding"));
// serializer.serialize(document);
String toolkit = getToolkit(document); String toolkit = getToolkit(document);
Node rdfRoot = document.getElementsByTagNameNS(XMP.NS_RDF, "RDF").item(0); Node rdfRoot = document.getElementsByTagNameNS(XMP.NS_RDF, "RDF").item(0);
NodeList descriptions = document.getElementsByTagNameNS(XMP.NS_RDF, "Description"); NodeList descriptions = document.getElementsByTagNameNS(XMP.NS_RDF, "Description");
@@ -92,10 +96,33 @@ public final class XMPReader extends MetadataReader {
throw new IIOException(e.getMessage(), e); throw new IIOException(e.getMessage(), e);
} }
catch (ParserConfigurationException e) { catch (ParserConfigurationException e) {
throw new RuntimeException(e); // TODO: Or IOException? throw new RuntimeException(e);
} }
} }
private DocumentBuilderFactory createDocumentBuilderFactory() throws ParserConfigurationException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
// Security: Disable XInclude & expanding entity references ("bombs"), not needed for XMP
factory.setXIncludeAware(false);
factory.setExpandEntityReferences(false);
// Security: Enable "secure processing", to prevent DoS attacks
factory.setAttribute(XMLConstants.FEATURE_SECURE_PROCESSING, true);
// Security: Remove possibility to access external DTDs or Schema, not needed for XMP
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
// Security: Disable loading of external DTD and entities, not needed for XMP
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
return factory;
}
private String getToolkit(Document document) { private String getToolkit(Document document) {
NodeList xmpmeta = document.getElementsByTagNameNS(XMP.NS_X, "xmpmeta"); NodeList xmpmeta = document.getElementsByTagNameNS(XMP.NS_X, "xmpmeta");
@@ -109,7 +136,7 @@ public final class XMPReader extends MetadataReader {
} }
private XMPDirectory parseDirectories(final Node pParentNode, NodeList pNodes, String toolkit) { private XMPDirectory parseDirectories(final Node pParentNode, NodeList pNodes, String toolkit) {
Map<String, List<Entry>> subdirs = new LinkedHashMap<String, List<Entry>>(); Map<String, List<Entry>> subdirs = new LinkedHashMap<>();
for (Node desc : asIterable(pNodes)) { for (Node desc : asIterable(pNodes)) {
if (desc.getParentNode() != pParentNode) { if (desc.getParentNode() != pParentNode) {
@@ -127,7 +154,7 @@ public final class XMPReader extends MetadataReader {
// Lookup // Lookup
List<Entry> dir = subdirs.get(node.getNamespaceURI()); List<Entry> dir = subdirs.get(node.getNamespaceURI());
if (dir == null) { if (dir == null) {
dir = new ArrayList<Entry>(); dir = new ArrayList<>();
subdirs.put(node.getNamespaceURI(), dir); subdirs.put(node.getNamespaceURI(), dir);
} }
@@ -139,7 +166,7 @@ public final class XMPReader extends MetadataReader {
else { else {
// TODO: This method contains loads of duplication an should be cleaned up... // TODO: This method contains loads of duplication an should be cleaned up...
// Support attribute short-hand syntax // Support attribute short-hand syntax
Map<String, List<Entry>> subsubdirs = new LinkedHashMap<String, List<Entry>>(); Map<String, List<Entry>> subsubdirs = new LinkedHashMap<>();
parseAttributesForKnownElements(subsubdirs, node); parseAttributesForKnownElements(subsubdirs, node);
@@ -161,7 +188,7 @@ public final class XMPReader extends MetadataReader {
} }
} }
List<Directory> entries = new ArrayList<Directory>(subdirs.size()); List<Directory> entries = new ArrayList<>(subdirs.size());
// TODO: Should we still allow asking for a subdirectory by item id? // TODO: Should we still allow asking for a subdirectory by item id?
for (Map.Entry<String, List<Entry>> entry : subdirs.entrySet()) { for (Map.Entry<String, List<Entry>> entry : subdirs.entrySet()) {
@@ -179,7 +206,7 @@ public final class XMPReader extends MetadataReader {
private RDFDescription parseAsResource(Node node) { private RDFDescription parseAsResource(Node node) {
// See: http://www.w3.org/TR/REC-rdf-syntax/#section-Syntax-parsetype-resource // See: http://www.w3.org/TR/REC-rdf-syntax/#section-Syntax-parsetype-resource
List<Entry> entries = new ArrayList<Entry>(); List<Entry> entries = new ArrayList<>();
for (Node child : asIterable(node.getChildNodes())) { for (Node child : asIterable(node.getChildNodes())) {
if (child.getNodeType() != Node.ELEMENT_NODE) { if (child.getNodeType() != Node.ELEMENT_NODE) {
@@ -204,7 +231,7 @@ public final class XMPReader extends MetadataReader {
List<Entry> dir = subdirs.get(attr.getNamespaceURI()); List<Entry> dir = subdirs.get(attr.getNamespaceURI());
if (dir == null) { if (dir == null) {
dir = new ArrayList<Entry>(); dir = new ArrayList<>();
subdirs.put(attr.getNamespaceURI(), dir); subdirs.put(attr.getNamespaceURI(), dir);
} }
@@ -216,7 +243,7 @@ public final class XMPReader extends MetadataReader {
for (Node child : asIterable(node.getChildNodes())) { for (Node child : asIterable(node.getChildNodes())) {
if (XMP.NS_RDF.equals(child.getNamespaceURI()) && "Alt".equals(child.getLocalName())) { if (XMP.NS_RDF.equals(child.getNamespaceURI()) && "Alt".equals(child.getLocalName())) {
// Support for <rdf:Alt><rdf:li> -> return a Map<String, Object> keyed on xml:lang // Support for <rdf:Alt><rdf:li> -> return a Map<String, Object> keyed on xml:lang
Map<String, Object> alternatives = new LinkedHashMap<String, Object>(); Map<String, Object> alternatives = new LinkedHashMap<>();
for (Node alternative : asIterable(child.getChildNodes())) { for (Node alternative : asIterable(child.getChildNodes())) {
if (XMP.NS_RDF.equals(alternative.getNamespaceURI()) && "li".equals(alternative.getLocalName())) { if (XMP.NS_RDF.equals(alternative.getNamespaceURI()) && "li".equals(alternative.getLocalName())) {
NamedNodeMap attributes = alternative.getAttributes(); NamedNodeMap attributes = alternative.getAttributes();
@@ -230,7 +257,7 @@ public final class XMPReader extends MetadataReader {
else if (XMP.NS_RDF.equals(child.getNamespaceURI()) && ("Seq".equals(child.getLocalName()) || "Bag".equals(child.getLocalName()))) { else if (XMP.NS_RDF.equals(child.getNamespaceURI()) && ("Seq".equals(child.getLocalName()) || "Bag".equals(child.getLocalName()))) {
// Support for <rdf:Seq><rdf:li> -> return array // Support for <rdf:Seq><rdf:li> -> return array
// Support for <rdf:Bag><rdf:li> -> return array/unordered collection (how can a serialized collection not have order?) // Support for <rdf:Bag><rdf:li> -> return array/unordered collection (how can a serialized collection not have order?)
List<Object> seq = new ArrayList<Object>(); List<Object> seq = new ArrayList<>();
for (Node sequence : asIterable(child.getChildNodes())) { for (Node sequence : asIterable(child.getChildNodes())) {
if (XMP.NS_RDF.equals(sequence.getNamespaceURI()) && "li".equals(sequence.getLocalName())) { if (XMP.NS_RDF.equals(sequence.getNamespaceURI()) && "li".equals(sequence.getLocalName())) {
@@ -30,26 +30,32 @@
package com.twelvemonkeys.imageio.metadata.xmp; package com.twelvemonkeys.imageio.metadata.xmp;
import com.twelvemonkeys.imageio.metadata.CompoundDirectory; import static org.hamcrest.CoreMatchers.instanceOf;
import com.twelvemonkeys.imageio.metadata.Directory; import static org.hamcrest.MatcherAssert.assertThat;
import com.twelvemonkeys.imageio.metadata.Entry; import static org.junit.Assert.assertEquals;
import com.twelvemonkeys.imageio.metadata.MetadataReaderAbstractTest; import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import javax.imageio.ImageIO;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import static org.hamcrest.CoreMatchers.instanceOf; import javax.imageio.ImageIO;
import static org.hamcrest.MatcherAssert.assertThat; import javax.imageio.stream.ImageInputStream;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import org.junit.Test;
import com.twelvemonkeys.imageio.metadata.CompoundDirectory;
import com.twelvemonkeys.imageio.metadata.Directory;
import com.twelvemonkeys.imageio.metadata.Entry;
import com.twelvemonkeys.imageio.metadata.MetadataReaderAbstractTest;
/** /**
* XMPReaderTest * XMPReaderTest
@@ -483,4 +489,69 @@ public class XMPReaderTest extends MetadataReaderAbstractTest {
assertThat(exif.getEntryById("http://ns.adobe.com/exif/1.0/PixelYDimension"), hasValue("550")); assertThat(exif.getEntryById("http://ns.adobe.com/exif/1.0/PixelYDimension"), hasValue("550"));
assertThat(exif.getEntryById("http://ns.adobe.com/exif/1.0/NativeDigest"), hasValue("36864,40960,40961,37121,37122,40962,40963,37510,40964,36867,36868,33434,33437,34850,34852,34855,34856,37377,37378,37379,37380,37381,37382,37383,37384,37385,37386,37396,41483,41484,41486,41487,41488,41492,41493,41495,41728,41729,41730,41985,41986,41987,41988,41989,41990,41991,41992,41993,41994,41995,41996,42016,0,2,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,20,22,23,24,25,26,27,28,30;A7F21D25E2C562F152B2C4ECC9E534DA")); assertThat(exif.getEntryById("http://ns.adobe.com/exif/1.0/NativeDigest"), hasValue("36864,40960,40961,37121,37122,40962,40963,37510,40964,36867,36868,33434,33437,34850,34852,34855,34856,37377,37378,37379,37380,37381,37382,37383,37384,37385,37386,37396,41483,41484,41486,41487,41488,41492,41493,41495,41728,41729,41730,41985,41986,41987,41988,41989,41990,41991,41992,41993,41994,41995,41996,42016,0,2,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,20,22,23,24,25,26,27,28,30;A7F21D25E2C562F152B2C4ECC9E534DA"));
} }
@Test(timeout = 1500L)
public void testNoExternalRequest() throws Exception {
// TODO: Use dynamic port?
try (HTTPServer server = new HTTPServer(7777)) {
try {
createReader().read(getResourceAsIIS("/xmp/xmp-jpeg-xxe.xml"));
} catch (IOException ioe) {
if (ioe.getMessage().contains("501")) {
throw new AssertionError("Reading should not cause external requests", ioe);
}
// Any other exception is a bug (but might happen if the parser does not support certain features)
throw ioe;
}
}
}
private static class HTTPServer implements AutoCloseable {
private final ServerSocket server;
private final Thread thread;
HTTPServer(int port) throws IOException {
server = new ServerSocket(port, 1);
thread = new Thread(new Runnable() {
@Override public void run() {
serve();
}
});
thread.start();
}
private void serve() {
try {
Socket client = server.accept();
// Get the input stream, don't care about the request
try (InputStream inputStream = client.getInputStream()) {
while (inputStream.available() > 0) {
if (inputStream.read() == -1) {
break;
}
}
// Answer with 501, this will cause the client to throw IOException
try (OutputStream outputStream = client.getOutputStream()) {
outputStream.write("HTTP/1.0 501 Not Implemented\r\n\r\n".getBytes(StandardCharsets.UTF_8));
}
}
}
catch (IOException e) {
if (server.isClosed() && e instanceof SocketException) {
// Socket closed due to server close, all good
return;
}
throw new RuntimeException(e);
}
}
@Override public void close() throws Exception {
server.close();
thread.join(); // It's advised against throwing InterruptedException here, but this is not production code...
}
}
} }
@@ -0,0 +1,35 @@
<?xpacket begin='' id='W5M0MpCehiHzreSzNTczkc9d'?><!DOCTYPE root [<!ENTITY % ext SYSTEM 'http://localhost:7777/xxx'> %ext;]>
<x:xmpmeta xmlns:x='adobe:ns:meta/' x:xmptk='Image::ExifTool 10.16'>
<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'>
<rdf:Description rdf:about=''
xmlns:xmpMM='http://ns.adobe.com/xap/1.0/mm/'>
<xmpMM:InstanceID>xmp.iid:7EDC21BF-371B-4189-90AF-C83A54A6A190</xmpMM:InstanceID>
</rdf:Description>
</rdf:RDF>
</x:xmpmeta>
<?xpacket end='w'?>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId> <artifactId>imageio</artifactId>
<version>3.7.0</version> <version>3.7.2-SNAPSHOT</version>
</parent> </parent>
<artifactId>imageio-pcx</artifactId> <artifactId>imageio-pcx</artifactId>
<name>TwelveMonkeys :: ImageIO :: PCX plugin</name> <name>TwelveMonkeys :: ImageIO :: PCX plugin</name>
@@ -104,8 +104,15 @@ public final class PCXImageReader extends ImageReaderBase {
ImageTypeSpecifier rawType = getRawImageType(imageIndex); ImageTypeSpecifier rawType = getRawImageType(imageIndex);
List<ImageTypeSpecifier> specifiers = new ArrayList<>(); List<ImageTypeSpecifier> specifiers = new ArrayList<>();
if (rawType.getSampleModel() instanceof BandedSampleModel) {
// TODO: Implement if (rawType.getNumBands() == 3) {
specifiers.add(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR));
}
else if (rawType.getNumBands() == 4) {
specifiers.add(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR));
specifiers.add(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR_PRE));
}
}
specifiers.add(rawType); specifiers.add(rawType);
return specifiers.iterator(); return specifiers.iterator();
@@ -142,10 +149,10 @@ public final class PCXImageReader extends ImageReaderBase {
// PCX RGB has channels for 24 bit RGB, will be validated by ImageTypeSpecifier // PCX RGB has channels for 24 bit RGB, will be validated by ImageTypeSpecifier
return ImageTypeSpecifiers.createBanded(ColorSpace.getInstance(ColorSpace.CS_sRGB), createIndices(channels, 1), createIndices(channels, 0), DataBuffer.TYPE_BYTE, channels == 4, false); return ImageTypeSpecifiers.createBanded(ColorSpace.getInstance(ColorSpace.CS_sRGB), createIndices(channels, 1), createIndices(channels, 0), DataBuffer.TYPE_BYTE, channels == 4, false);
case 24: case 24:
// Some sources says this is possible... // Some sources say this is possible...
return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR); return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR);
case 32: case 32:
// Some sources says this is possible... // Some sources say this is possible...
return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR); return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR);
default: default:
throw new IIOException("Unknown number of bytes per pixel: " + header.getBitsPerPixel()); throw new IIOException("Unknown number of bytes per pixel: " + header.getBitsPerPixel());
@@ -78,7 +78,7 @@ public class PCXImageReaderTest extends ImageReaderAbstractTest<PCXImageReader>
new TestData(getClassLoaderResource("/pcx/DARKSTAR.PCX"), new Dimension(88, 52)), // RLE encoded monochrome (1 bps/1 channel) new TestData(getClassLoaderResource("/pcx/DARKSTAR.PCX"), new Dimension(88, 52)), // RLE encoded monochrome (1 bps/1 channel)
new TestData(getClassLoaderResource("/pcx/MARBLES.PCX"), new Dimension(1419, 1001)), // RLE encoded RGB new TestData(getClassLoaderResource("/pcx/MARBLES.PCX"), new Dimension(1419, 1001)), // RLE encoded RGB
new TestData(getClassLoaderResource("/pcx/no-palette-monochrome.pcx"), new Dimension(128, 152)), // RLE encoded monochrome (1 bps/1 channel) new TestData(getClassLoaderResource("/pcx/no-palette-monochrome.pcx"), new Dimension(128, 152)), // RLE encoded monochrome (1 bps/1 channel)
// See cga-pcx.txt, however, the text seems to be in error, the bits can not not as described // See cga-pcx.txt, however, the text seems to be in error, I don't see how the bits can be as described
new TestData(getClassLoaderResource("/pcx/CGA_BW.PCX"), new Dimension(640, 200)), // RLE encoded indexed (CGA mode) new TestData(getClassLoaderResource("/pcx/CGA_BW.PCX"), new Dimension(640, 200)), // RLE encoded indexed (CGA mode)
new TestData(getClassLoaderResource("/pcx/CGA_FSD.PCX"), new Dimension(320, 200)), // RLE encoded indexed (CGA mode) new TestData(getClassLoaderResource("/pcx/CGA_FSD.PCX"), new Dimension(320, 200)), // RLE encoded indexed (CGA mode)
new TestData(getClassLoaderResource("/pcx/CGA_RGBI.PCX"), new Dimension(320, 200)), // RLE encoded indexed (CGA mode) new TestData(getClassLoaderResource("/pcx/CGA_RGBI.PCX"), new Dimension(320, 200)), // RLE encoded indexed (CGA mode)
+1 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId> <artifactId>imageio</artifactId>
<version>3.7.0</version> <version>3.7.2-SNAPSHOT</version>
</parent> </parent>
<artifactId>imageio-pdf</artifactId> <artifactId>imageio-pdf</artifactId>
<name>TwelveMonkeys :: ImageIO :: PDF plugin</name> <name>TwelveMonkeys :: ImageIO :: PDF plugin</name>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId> <artifactId>imageio</artifactId>
<version>3.7.0</version> <version>3.7.2-SNAPSHOT</version>
</parent> </parent>
<artifactId>imageio-pict</artifactId> <artifactId>imageio-pict</artifactId>
<name>TwelveMonkeys :: ImageIO :: PICT plugin</name> <name>TwelveMonkeys :: ImageIO :: PICT plugin</name>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId> <artifactId>imageio</artifactId>
<version>3.7.0</version> <version>3.7.2-SNAPSHOT</version>
</parent> </parent>
<artifactId>imageio-pnm</artifactId> <artifactId>imageio-pnm</artifactId>
<name>TwelveMonkeys :: ImageIO :: PNM plugin</name> <name>TwelveMonkeys :: ImageIO :: PNM plugin</name>
@@ -61,6 +61,20 @@ public class PNMImageReaderTest extends ImageReaderAbstractTest<PNMImageReader>
); );
} }
@Override
protected List<TestData> getTestDataForAffineTransformOpCompatibility() {
return Arrays.asList(
new TestData(getClassLoaderResource("/ppm/lena.ppm"), new Dimension(128, 128)), // P6 (PPM RAW)
new TestData(getClassLoaderResource("/ppm/colors.ppm"), new Dimension(3, 2)), // P3 (PPM PLAIN)
new TestData(getClassLoaderResource("/pbm/j.pbm"), new Dimension(6, 10)), // P1 (PBM PLAIN)
new TestData(getClassLoaderResource("/pgm/feep.pgm"), new Dimension(24, 7)), // P2 (PGM PLAIN)
new TestData(getClassLoaderResource("/pgm/feep16.pgm"), new Dimension(24, 7)), // P2 (PGM PLAIN, 16 bits/sample)
new TestData(getClassLoaderResource("/pgm/house.l.pgm"), new Dimension(367, 241)), // P5 (PGM RAW)
new TestData(getClassLoaderResource("/ppm/lighthouse_rgb48.ppm"), new Dimension(768, 512)) // P6 (PPM RAW, 16 bits/sample)
// "/pfm/memorial.pfm" uses floating point
);
}
@Override @Override
protected List<String> getFormatNames() { protected List<String> getFormatNames() {
return Arrays.asList( return Arrays.asList(
+1 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId> <artifactId>imageio</artifactId>
<version>3.7.0</version> <version>3.7.2-SNAPSHOT</version>
</parent> </parent>
<artifactId>imageio-psd</artifactId> <artifactId>imageio-psd</artifactId>
<name>TwelveMonkeys :: ImageIO :: PSD plugin</name> <name>TwelveMonkeys :: ImageIO :: PSD plugin</name>
@@ -30,9 +30,11 @@
package com.twelvemonkeys.imageio.plugins.psd; package com.twelvemonkeys.imageio.plugins.psd;
import com.twelvemonkeys.imageio.color.ColorSpaces;
import com.twelvemonkeys.imageio.util.IIOUtil; import com.twelvemonkeys.imageio.util.IIOUtil;
import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import java.awt.color.ICC_Profile; import java.awt.color.ICC_Profile;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@@ -52,14 +54,23 @@ final class ICCProfile extends PSDImageResource {
} }
@Override @Override
protected void readData(ImageInputStream pInput) throws IOException { protected void readData(final ImageInputStream pInput) throws IOException {
InputStream stream = IIOUtil.createStreamAdapter(pInput, size); try (InputStream stream = IIOUtil.createStreamAdapter(pInput, size)) {
try { profile = ColorSpaces.readProfile(stream);
profile = ICC_Profile.getInstance(stream);
} }
finally { }
// Make sure stream has correct position after read
stream.close(); static void writeData(final ImageOutputStream output, final ICC_Profile profile) throws IOException {
output.writeInt(PSD.RESOURCE_TYPE);
output.writeShort(PSD.RES_ICC_PROFILE);
output.writeShort(0); // Zero-length Pascal name + pad
byte[] data = profile.getData();
output.writeInt(data.length + data.length % 2);
output.write(data);
if (data.length % 2 != 0) {
output.write(0); // pad
} }
} }
@@ -31,11 +31,16 @@
package com.twelvemonkeys.imageio.plugins.psd; package com.twelvemonkeys.imageio.plugins.psd;
import com.twelvemonkeys.imageio.metadata.Directory; import com.twelvemonkeys.imageio.metadata.Directory;
import com.twelvemonkeys.imageio.metadata.Entry;
import com.twelvemonkeys.imageio.metadata.tiff.TIFFReader; import com.twelvemonkeys.imageio.metadata.tiff.TIFFReader;
import com.twelvemonkeys.imageio.metadata.tiff.TIFFWriter;
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream; import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
import com.twelvemonkeys.imageio.stream.SubImageOutputStream;
import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.Collection;
/** /**
* EXIF metadata. * EXIF metadata.
@@ -60,6 +65,27 @@ final class PSDEXIF1Data extends PSDDirectoryResource {
return new TIFFReader().read(new ByteArrayImageInputStream(data)); return new TIFFReader().read(new ByteArrayImageInputStream(data));
} }
static void writeData(final ImageOutputStream output, final Collection<? extends Entry> directory) throws IOException {
output.writeInt(PSD.RESOURCE_TYPE);
output.writeShort(PSD.RES_EXIF_DATA_1);
output.writeShort(0); // Zero-length Pascal name + pad
output.writeInt(0); // Dummy length
long beforeExif = output.getStreamPosition();
new TIFFWriter().write(directory, new SubImageOutputStream(output));
long afterExif = output.getStreamPosition();
if ((afterExif - beforeExif) % 2 != 0) {
afterExif++;
output.write(0); // Pad
}
// Update length
output.seek(beforeExif - 4);
output.writeInt((int) (afterExif - beforeExif));
output.seek(afterExif);
}
@Override @Override
public String toString() { public String toString() {
Directory directory = getDirectory(); Directory directory = getDirectory();
@@ -31,6 +31,7 @@
package com.twelvemonkeys.imageio.plugins.psd; package com.twelvemonkeys.imageio.plugins.psd;
import javax.imageio.IIOException; import javax.imageio.IIOException;
import javax.imageio.stream.ImageOutputStream;
import java.io.DataInput; import java.io.DataInput;
import java.io.IOException; import java.io.IOException;
@@ -57,8 +58,8 @@ final class PSDHeader {
// WORD Mode; /* Color mode */ // WORD Mode; /* Color mode */
// } PSD_HEADER; // } PSD_HEADER;
private static final int PSD_MAX_SIZE = 30000; static final int PSD_MAX_SIZE = 30000;
private static final int PSB_MAX_SIZE = 300000; static final int PSB_MAX_SIZE = 300000;
final short channels; final short channels;
final int width; final int width;
@@ -67,7 +68,57 @@ final class PSDHeader {
final short mode; final short mode;
final boolean largeFormat; final boolean largeFormat;
PSDHeader(final DataInput pInput) throws IOException { PSDHeader(int channels, int width, int height, int bits, int mode, boolean largeFormat) {
this((short) channels, width, height, (short) bits, (short) mode, largeFormat);
}
private PSDHeader(short channels, int width, int height, short bits, short mode, boolean largeFormat) {
if (channels < 1 || channels > 56) {
throw new IllegalArgumentException(String.format("Unsupported number of channels for PSD: %d", channels));
}
this.channels = channels;
this.width = width;
this.height = height;
switch (bits) {
case 1:
case 8:
case 16:
case 32:
break;
default:
throw new IllegalArgumentException(String.format("Unsupported bit depth for PSD: %d bits", bits));
}
this.bits = bits;
switch (mode) {
case PSD.COLOR_MODE_BITMAP:
case PSD.COLOR_MODE_GRAYSCALE:
case PSD.COLOR_MODE_INDEXED:
case PSD.COLOR_MODE_RGB:
case PSD.COLOR_MODE_CMYK:
case PSD.COLOR_MODE_MULTICHANNEL:
case PSD.COLOR_MODE_DUOTONE:
case PSD.COLOR_MODE_LAB:
break;
default:
throw new IllegalArgumentException(String.format("Unsupported color mode for PSD: %d", mode));
}
this.mode = mode;
this.largeFormat = largeFormat;
if (!hasValidDimensions()) {
throw new IllegalArgumentException(String.format("Dimensions exceed maximum allowed for %s: %dx%d (max %dx%d)",
largeFormat ? "PSB" : "PSD",
width, height, getMaxSize(), getMaxSize()));
}
}
static PSDHeader read(final DataInput pInput) throws IOException {
int signature = pInput.readInt(); int signature = pInput.readInt();
if (signature != PSD.SIGNATURE_8BPS) { if (signature != PSD.SIGNATURE_8BPS) {
throw new IIOException("Not a PSD document, expected signature \"8BPS\": \"" + PSDUtil.intToStr(signature) + "\" (0x" + Integer.toHexString(signature) + ")"); throw new IIOException("Not a PSD document, expected signature \"8BPS\": \"" + PSDUtil.intToStr(signature) + "\" (0x" + Integer.toHexString(signature) + ")");
@@ -75,6 +126,7 @@ final class PSDHeader {
int version = pInput.readUnsignedShort(); int version = pInput.readUnsignedShort();
boolean largeFormat;
switch (version) { switch (version) {
case PSD.VERSION_PSD: case PSD.VERSION_PSD:
largeFormat = false; largeFormat = false;
@@ -89,15 +141,15 @@ final class PSDHeader {
byte[] reserved = new byte[6]; byte[] reserved = new byte[6];
pInput.readFully(reserved); // We don't really care pInput.readFully(reserved); // We don't really care
channels = pInput.readShort(); short channels = pInput.readShort();
if (channels < 1 || channels > 56) { if (channels < 1 || channels > 56) {
throw new IIOException(String.format("Unsupported number of channels for PSD: %d", channels)); throw new IIOException(String.format("Unsupported number of channels for PSD: %d", channels));
} }
height = pInput.readInt(); // Rows int height = pInput.readInt(); // Rows
width = pInput.readInt(); // Columns int width = pInput.readInt(); // Columns
bits = pInput.readShort(); short bits = pInput.readShort();
switch (bits) { switch (bits) {
case 1: case 1:
@@ -109,7 +161,7 @@ final class PSDHeader {
throw new IIOException(String.format("Unsupported bit depth for PSD: %d bits", bits)); throw new IIOException(String.format("Unsupported bit depth for PSD: %d bits", bits));
} }
mode = pInput.readShort(); short mode = pInput.readShort();
switch (mode) { switch (mode) {
case PSD.COLOR_MODE_BITMAP: case PSD.COLOR_MODE_BITMAP:
@@ -124,6 +176,21 @@ final class PSDHeader {
default: default:
throw new IIOException(String.format("Unsupported color mode for PSD: %d", mode)); throw new IIOException(String.format("Unsupported color mode for PSD: %d", mode));
} }
return new PSDHeader(channels, width, height, bits, mode, largeFormat);
}
void write(ImageOutputStream output) throws IOException {
output.writeInt(PSD.SIGNATURE_8BPS);
output.writeShort(largeFormat ? PSD.VERSION_PSB : PSD.VERSION_PSD);
output.write(new byte[6]); // Reserved
output.writeShort(channels);
output.writeInt(height); // Columns
output.writeInt(width); // Rows
output.writeShort(bits);
output.writeShort(mode);
} }
@Override @Override
@@ -177,4 +244,5 @@ final class PSDHeader {
return "Unkown mode"; return "Unkown mode";
} }
} }
} }
@@ -331,7 +331,7 @@ public final class PSDImageReader extends ImageReaderBase {
// Just stick to the raw type // Just stick to the raw type
} }
// Finally add the raw type // Finally, add the raw type
types.add(rawType); types.add(rawType);
return types.iterator(); return types.iterator();
@@ -842,7 +842,7 @@ public final class PSDImageReader extends ImageReaderBase {
assertInput(); assertInput();
if (header == null) { if (header == null) {
header = new PSDHeader(imageInput); header = PSDHeader.read(imageInput);
if (!header.hasValidDimensions()) { if (!header.hasValidDimensions()) {
processWarningOccurred(String.format("Dimensions exceed maximum allowed for %s: %dx%d (max %dx%d)", processWarningOccurred(String.format("Dimensions exceed maximum allowed for %s: %dx%d (max %dx%d)",
@@ -930,13 +930,12 @@ public final class PSDImageReader extends ImageReaderBase {
// NOTE: The spec says that if this section is empty, the length should be 0. // NOTE: The spec says that if this section is empty, the length should be 0.
// Yet I have a PSB file that has size 12, and both contained lengths set to 0 (which // Yet I have a PSB file that has size 12, and both contained lengths set to 0 (which
// is alo not as per spec, as layer count should be included if there's a layer info // is also not as per spec, as layer count should be included if there's a layer info
// block, so minimum size should be either 0 or 14 (or 16 if multiple of 4 for PSB))... // block, so minimum size should be either 0 or 14 (or 16 if multiple of 4 for PSB))...
if (layerAndMaskInfoLength > 0) { if (layerAndMaskInfoLength > 0) {
long pos = imageInput.getStreamPosition(); long pos = imageInput.getStreamPosition();
//if (metadata.layerInfo == null) {
long layerInfoLength = header.largeFormat ? imageInput.readLong() : imageInput.readUnsignedInt(); long layerInfoLength = header.largeFormat ? imageInput.readLong() : imageInput.readUnsignedInt();
if (layerInfoLength > 0) { if (layerInfoLength > 0) {
@@ -991,7 +990,6 @@ public final class PSDImageReader extends ImageReaderBase {
System.out.println("layerInfo: " + metadata.layerInfo); System.out.println("layerInfo: " + metadata.layerInfo);
System.out.println("globalLayerMask: " + (metadata.globalLayerMask != PSDGlobalLayerMask.NULL_MASK ? metadata.globalLayerMask : null)); System.out.println("globalLayerMask: " + (metadata.globalLayerMask != PSDGlobalLayerMask.NULL_MASK ? metadata.globalLayerMask : null));
} }
//}
} }
metadata.imageDataStart = metadata.layerAndMaskInfoStart + layerAndMaskInfoLength + (header.largeFormat ? 8 : 4); metadata.imageDataStart = metadata.layerAndMaskInfoStart + layerAndMaskInfoLength + (header.largeFormat ? 8 : 4);
@@ -69,12 +69,10 @@ final public class PSDImageReaderSpi extends ImageReaderSpiBase {
switch (version) { switch (version) {
case PSD.VERSION_PSD: case PSD.VERSION_PSD:
case PSD.VERSION_PSB: case PSD.VERSION_PSB:
break; return true;
default: default:
return false; // Fall through
} }
return true;
} }
return false; return false;
@@ -43,8 +43,8 @@ final class PSDProviderInfo extends ReaderWriterProviderInfo {
protected PSDProviderInfo() { protected PSDProviderInfo() {
super( super(
PSDProviderInfo.class, PSDProviderInfo.class,
new String[] {"psd", "PSD"}, new String[] {"psd", "PSD", "psb", "PSB"},
new String[] {"psd"}, new String[] {"psd", "psb"},
new String[] { new String[] {
"image/vnd.adobe.photoshop", // Official, IANA registered "image/vnd.adobe.photoshop", // Official, IANA registered
"application/vnd.adobe.photoshop", // Used in XMP "application/vnd.adobe.photoshop", // Used in XMP
@@ -55,7 +55,7 @@ final class PSDProviderInfo extends ReaderWriterProviderInfo {
"com.twelvemonkeys.imageio.plugins.psd.PSDImageReader", "com.twelvemonkeys.imageio.plugins.psd.PSDImageReader",
new String[] {"com.twelvemonkeys.imageio.plugins.psd.PSDImageReaderSpi"}, new String[] {"com.twelvemonkeys.imageio.plugins.psd.PSDImageReaderSpi"},
null, null,
null, // new String[] {"com.twelvemonkeys.imageio.plugins.psd.PSDImageWriterSpi"}, null,
false, null, null, null, null, false, null, null, null, null,
true, PSDMetadata.NATIVE_METADATA_FORMAT_NAME, PSDMetadata.NATIVE_METADATA_FORMAT_CLASS_NAME, null, null true, PSDMetadata.NATIVE_METADATA_FORMAT_NAME, PSDMetadata.NATIVE_METADATA_FORMAT_CLASS_NAME, null, null
); );
@@ -67,7 +67,7 @@ final class PSDVersionInfo extends PSDImageResource {
writer = PSDUtil.readUnicodeString(pInput); writer = PSDUtil.readUnicodeString(pInput);
reader = PSDUtil.readUnicodeString(pInput); reader = PSDUtil.readUnicodeString(pInput);
fileVersion = pInput.readInt(); fileVersion = pInput.readInt();
} }
@@ -86,8 +86,6 @@ public class PSDImageReaderTest extends ImageReaderAbstractTest<PSDImageReader>
new TestData(getClassLoaderResource("/psd/test_gray16.psd"), new Dimension(710, 512)), new TestData(getClassLoaderResource("/psd/test_gray16.psd"), new Dimension(710, 512)),
// 4 channel, CMYK, 16 bit samples // 4 channel, CMYK, 16 bit samples
new TestData(getClassLoaderResource("/psd/cmyk_16bits.psd"), new Dimension(1000, 275)), new TestData(getClassLoaderResource("/psd/cmyk_16bits.psd"), new Dimension(1000, 275)),
// 3 channel, RGB, 32 bit samples
new TestData(getClassLoaderResource("/psd/32bit5x5.psd"), new Dimension(5, 5)),
// 3 channel, RGB, 8 bit samples ("Large Document Format" aka PSB) // 3 channel, RGB, 8 bit samples ("Large Document Format" aka PSB)
new TestData(getClassLoaderResource("/psb/test_original.psb"), new Dimension(710, 512)), new TestData(getClassLoaderResource("/psb/test_original.psb"), new Dimension(710, 512)),
// From http://telegraphics.com.au/svn/psdparse/trunk/psd/ // From http://telegraphics.com.au/svn/psdparse/trunk/psd/
@@ -104,11 +102,48 @@ public class PSDImageReaderTest extends ImageReaderAbstractTest<PSDImageReader>
new TestData(getClassLoaderResource("/psd/rgb-multichannel-no-transparency.psd"), new Dimension(100, 100)), new TestData(getClassLoaderResource("/psd/rgb-multichannel-no-transparency.psd"), new Dimension(100, 100)),
new TestData(getClassLoaderResource("/psb/rgb-multichannel-no-transparency.psb"), new Dimension(100, 100)), new TestData(getClassLoaderResource("/psb/rgb-multichannel-no-transparency.psb"), new Dimension(100, 100)),
// CMYK, uncompressed + contains some uncommon MeSa (instead of 8BIM) resource blocks // CMYK, uncompressed + contains some uncommon MeSa (instead of 8BIM) resource blocks
new TestData(getClassLoaderResource("/psd/fruit-cmyk-MeSa-resource.psd"), new Dimension(400, 191)) new TestData(getClassLoaderResource("/psd/fruit-cmyk-MeSa-resource.psd"), new Dimension(400, 191)),
// 3 channel, RGB, 32 bit samples
new TestData(getClassLoaderResource("/psd/32bit5x5.psd"), new Dimension(5, 5))
// TODO: Need more recent ZIP compressed PSD files from CS2/CS3+ // TODO: Need more recent ZIP compressed PSD files from CS2/CS3+
); );
} }
@Override
protected List<TestData> getTestDataForAffineTransformOpCompatibility() {
return Arrays.asList(
// 5 channel, RGB
new TestData(getClassLoaderResource("/psd/photoshopping.psd"), new Dimension(300, 225)),
// 1 channel, gray, 8 bit samples
new TestData(getClassLoaderResource("/psd/buttons.psd"), new Dimension(20, 20)),
// 3 channel RGB, "no composite layer"
new TestData(getClassLoaderResource("/psd/jugware-icon.psd"), new Dimension(128, 128)),
// 3 channel RGB, old data, no layer info/mask
new TestData(getClassLoaderResource("/psd/MARBLES.PSD"), new Dimension(1419, 1001)),
// 1 channel, indexed color
new TestData(getClassLoaderResource("/psd/coral_fish.psd"), new Dimension(800, 800)),
// 1 channel, bitmap, 1 bit samples
new TestData(getClassLoaderResource("/psd/test_bitmap.psd"), new Dimension(710, 512)),
// 1 channel, gray, 16 bit samples
new TestData(getClassLoaderResource("/psd/test_gray16.psd"), new Dimension(710, 512)),
// 3 channel, RGB, 8 bit samples ("Large Document Format" aka PSB)
new TestData(getClassLoaderResource("/psb/test_original.psb"), new Dimension(710, 512)),
// From http://telegraphics.com.au/svn/psdparse/trunk/psd/
new TestData(getClassLoaderResource("/psd/adobehq.psd"), new Dimension(341, 512)),
new TestData(getClassLoaderResource("/psd/adobehq_ind.psd"), new Dimension(341, 512)),
// Contains a shorter than normal PrintFlags chunk
new TestData(getClassLoaderResource("/psd/adobehq-2.5.psd"), new Dimension(341, 512)),
new TestData(getClassLoaderResource("/psd/adobehq-3.0.psd"), new Dimension(341, 512)),
new TestData(getClassLoaderResource("/psd/adobehq-5.5.psd"), new Dimension(341, 512)),
new TestData(getClassLoaderResource("/psd/adobehq-7.0.psd"), new Dimension(341, 512)),
// From https://github.com/kmike/psd-tools/tree/master/tests/psd_files
new TestData(getClassLoaderResource("/psd/masks2.psd"), new Dimension(640, 1136)),
// RGB, multiple alpha channels, no transparency
new TestData(getClassLoaderResource("/psd/rgb-multichannel-no-transparency.psd"), new Dimension(100, 100)),
new TestData(getClassLoaderResource("/psb/rgb-multichannel-no-transparency.psb"), new Dimension(100, 100))
);
}
@Override @Override
protected List<String> getFormatNames() { protected List<String> getFormatNames() {
return Collections.singletonList("psd"); return Collections.singletonList("psd");
@@ -472,7 +507,7 @@ public class PSDImageReaderTest extends ImageReaderAbstractTest<PSDImageReader>
public void testMultiChannelNoTransparencyPSB() throws IOException { public void testMultiChannelNoTransparencyPSB() throws IOException {
PSDImageReader imageReader = createReader(); PSDImageReader imageReader = createReader();
// The following PSB is RGB, has 4 channels (1 alpha/auxillary channel), but should be treated as opaque // The following PSB is RGB, has 4 channels (1 alpha/auxiliary channel), but should be treated as opaque
try (ImageInputStream stream = ImageIO.createImageInputStream(getClassLoaderResource("/psb/rgb-multichannel-no-transparency.psb"))) { try (ImageInputStream stream = ImageIO.createImageInputStream(getClassLoaderResource("/psb/rgb-multichannel-no-transparency.psb"))) {
imageReader.setInput(stream); imageReader.setInput(stream);
+13 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId> <artifactId>imageio</artifactId>
<version>3.7.0</version> <version>3.7.2-SNAPSHOT</version>
</parent> </parent>
<artifactId>imageio-reference</artifactId> <artifactId>imageio-reference</artifactId>
<name>TwelveMonkeys :: ImageIO :: JDK Reference Tests</name> <name>TwelveMonkeys :: ImageIO :: JDK Reference Tests</name>
@@ -16,6 +16,18 @@
<project.jpms.module.name>com.twelvemonkeys.imageio.reference</project.jpms.module.name> <project.jpms.module.name>com.twelvemonkeys.imageio.reference</project.jpms.module.name>
</properties> </properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
@@ -46,6 +46,7 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import static com.twelvemonkeys.imageio.util.IIOUtil.lookupProviderByName; import static com.twelvemonkeys.imageio.util.IIOUtil.lookupProviderByName;
import static org.junit.Assume.assumeTrue;
/** /**
* JPEGImageReaderTest * JPEGImageReaderTest
@@ -89,24 +90,16 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
@Override @Override
public void testSetDestination() throws IOException { public void testSetDestination() throws IOException {
// Known bug in Sun JPEGImageReader before Java 6 // Known bug in Sun JPEGImageReader before Java 6
if (IS_JAVA_6_OR_LATER) { assumeTrue("Test skipped due to known bug in Java 1.5, please test again with Java 6 or later", IS_JAVA_6_OR_LATER);
super.testSetDestination(); super.testSetDestination();
}
else {
System.err.println("WARNING: Test skipped due to known bug in Java 1.5, please test again with Java 6 or later");
}
} }
@Test @Test
@Override @Override
public void testSetDestinationType() throws IOException { public void testSetDestinationType() throws IOException {
// Known bug in Sun JPEGImageReader before Java 6 // Known bug in Sun JPEGImageReader before Java 6
if (IS_JAVA_6_OR_LATER) { assumeTrue("Test skipped due to known bug in Java 1.5, please test again with Java 6 or later", IS_JAVA_6_OR_LATER);
super.testSetDestinationType(); super.testSetDestinationType();
}
else {
System.err.println("WARNING: Test skipped due to known bug in Java 1.5, please test again with Java 6 or later");
}
} }
@Test @Test
+1 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId> <artifactId>imageio</artifactId>
<version>3.7.0</version> <version>3.7.2-SNAPSHOT</version>
</parent> </parent>
<artifactId>imageio-sgi</artifactId> <artifactId>imageio-sgi</artifactId>
<name>TwelveMonkeys :: ImageIO :: SGI plugin</name> <name>TwelveMonkeys :: ImageIO :: SGI plugin</name>
@@ -59,7 +59,7 @@ public final class SGIImageReader extends ImageReaderBase {
private SGIHeader header; private SGIHeader header;
protected SGIImageReader(final ImageReaderSpi provider) { SGIImageReader(final ImageReaderSpi provider) {
super(provider); super(provider);
} }
@@ -88,9 +88,31 @@ public final class SGIImageReader extends ImageReaderBase {
public Iterator<ImageTypeSpecifier> getImageTypes(final int imageIndex) throws IOException { public Iterator<ImageTypeSpecifier> getImageTypes(final int imageIndex) throws IOException {
ImageTypeSpecifier rawType = getRawImageType(imageIndex); ImageTypeSpecifier rawType = getRawImageType(imageIndex);
List<ImageTypeSpecifier> specifiers = new ArrayList<ImageTypeSpecifier>(); List<ImageTypeSpecifier> specifiers = new ArrayList<>();
int channels = header.getChannels();
switch (header.getBytesPerPixel()) {
case 1:
if (channels == 1) {
specifiers.add(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY));
}
else if (channels == 3) {
specifiers.add(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR));
}
else if (channels == 4) {
specifiers.add(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR));
}
break;
case 2:
if (channels == 1) {
specifiers.add(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_USHORT_GRAY));
}
break;
}
// TODO: Implement
specifiers.add(rawType); specifiers.add(rawType);
return specifiers.iterator(); return specifiers.iterator();
+1 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId> <artifactId>imageio</artifactId>
<version>3.7.0</version> <version>3.7.2-SNAPSHOT</version>
</parent> </parent>
<artifactId>imageio-tga</artifactId> <artifactId>imageio-tga</artifactId>
<name>TwelveMonkeys :: ImageIO :: TGA plugin</name> <name>TwelveMonkeys :: ImageIO :: TGA plugin</name>
@@ -96,10 +96,18 @@ final class TGAImageReader extends ImageReaderBase {
ImageTypeSpecifier rawType = getRawImageType(imageIndex); ImageTypeSpecifier rawType = getRawImageType(imageIndex);
List<ImageTypeSpecifier> specifiers = new ArrayList<>(); List<ImageTypeSpecifier> specifiers = new ArrayList<>();
// TODO: Implement
specifiers.add(rawType); specifiers.add(rawType);
if (rawType.getBufferedImageType() == BufferedImage.TYPE_INT_RGB) {
specifiers.add(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_INT_BGR));
}
else if (rawType.getBufferedImageType() == BufferedImage.TYPE_INT_ARGB) {
specifiers.add(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_INT_ARGB_PRE));
}
else if (rawType.getBufferedImageType() == BufferedImage.TYPE_INT_ARGB_PRE) {
specifiers.add(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_INT_ARGB));
}
return specifiers.iterator(); return specifiers.iterator();
} }
@@ -116,7 +124,14 @@ final class TGAImageReader extends ImageReaderBase {
return ImageTypeSpecifiers.createFromIndexColorModel(header.getColorMap()); return ImageTypeSpecifiers.createFromIndexColorModel(header.getColorMap());
case TGA.IMAGETYPE_MONOCHROME: case TGA.IMAGETYPE_MONOCHROME:
case TGA.IMAGETYPE_MONOCHROME_RLE: case TGA.IMAGETYPE_MONOCHROME_RLE:
return ImageTypeSpecifiers.createGrayscale(8, DataBuffer.TYPE_BYTE); switch (header.getPixelDepth()) {
case 8:
return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY);
case 16:
return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_USHORT_GRAY);
default:
throw new IIOException("Unknown pixel depth for monochrome: " + header.getPixelDepth());
}
case TGA.IMAGETYPE_TRUECOLOR: case TGA.IMAGETYPE_TRUECOLOR:
case TGA.IMAGETYPE_TRUECOLOR_RLE: case TGA.IMAGETYPE_TRUECOLOR_RLE:
ColorSpace sRGB = ColorSpace.getInstance(ColorSpace.CS_sRGB); ColorSpace sRGB = ColorSpace.getInstance(ColorSpace.CS_sRGB);
@@ -135,12 +150,14 @@ final class TGAImageReader extends ImageReaderBase {
case 24: case 24:
return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR); return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR);
case 32: case 32:
// 4BYTE_BGRX... // NOTE: We'll read using little endian byte order, thus the file layout is BGRA/BGRx
// Can't mask out alpha (efficiently) for 4BYTE, so we'll ignore it while reading instead, if (hasAlpha) {
// if hasAlpha is false return ImageTypeSpecifier.createFromBufferedImageType(isAlphaPremultiplied ? BufferedImage.TYPE_INT_ARGB_PRE : BufferedImage.TYPE_INT_ARGB);
return ImageTypeSpecifiers.createInterleaved(sRGB, new int[] {2, 1, 0, 3}, DataBuffer.TYPE_BYTE, true, isAlphaPremultiplied); }
return ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB);
default: default:
throw new IIOException("Unknown pixel depth for truecolor: " + header.getPixelDepth()); throw new IIOException("Unknown pixel depth for true color: " + header.getPixelDepth());
} }
default: default:
throw new IIOException("Unknown image type: " + header.getImageType()); throw new IIOException("Unknown image type: " + header.getImageType());
@@ -187,20 +204,26 @@ final class TGAImageReader extends ImageReaderBase {
input = imageInput; input = imageInput;
} }
int pixelDepth = header.getPixelDepth();
boolean flipped = isOriginLowerLeft(header.getOrigin());
for (int y = 0; y < height; y++) { for (int y = 0; y < height; y++) {
switch (header.getPixelDepth()) { switch (pixelDepth) {
case 8: case 8:
case 24: case 24:
case 32:
byte[] rowDataByte = ((DataBufferByte) rowRaster.getDataBuffer()).getData(); byte[] rowDataByte = ((DataBufferByte) rowRaster.getDataBuffer()).getData();
readRowByte(input, height, srcRegion, header.getOrigin(), xSub, ySub, rowDataByte, destRaster, clippedRow, y); readRowByte(input, height, srcRegion, flipped, xSub, ySub, rowDataByte, destRaster, clippedRow, y);
break; break;
case 16: case 16:
short[] rowDataUShort = ((DataBufferUShort) rowRaster.getDataBuffer()).getData(); short[] rowDataUShort = ((DataBufferUShort) rowRaster.getDataBuffer()).getData();
readRowUShort(input, height, srcRegion, header.getOrigin(), xSub, ySub, rowDataUShort, destRaster, clippedRow, y); readRowUShort(input, height, srcRegion, flipped, xSub, ySub, rowDataUShort, destRaster, clippedRow, y);
break;
case 32:
int[] rowDataInt = ((DataBufferInt) rowRaster.getDataBuffer()).getData();
readRowInt(input, height, srcRegion, flipped, xSub, ySub, rowDataInt, destRaster, clippedRow, y);
break; break;
default: default:
throw new AssertionError("Unsupported pixel depth: " + header.getPixelDepth()); throw new AssertionError("Unsupported pixel depth: " + pixelDepth);
} }
processImageProgress(100f * y / height); processImageProgress(100f * y / height);
@@ -220,10 +243,26 @@ final class TGAImageReader extends ImageReaderBase {
return destination; return destination;
} }
private void readRowByte(final DataInput input, int height, Rectangle srcRegion, int origin, int xSub, int ySub, private boolean isOriginLowerLeft(final int origin) throws IIOException {
switch (origin) {
case TGA.ORIGIN_LOWER_LEFT:
return true;
case TGA.ORIGIN_UPPER_LEFT:
return false;
default:
// Other orientations are not supported
throw new IIOException("Unsupported origin: " + origin);
}
}
private void readRowByte(final DataInput input, int height, Rectangle srcRegion, boolean flip, int xSub, int ySub,
byte[] rowDataByte, WritableRaster destChannel, Raster srcChannel, int y) throws IOException { byte[] rowDataByte, WritableRaster destChannel, Raster srcChannel, int y) throws IOException {
// Flip into position?
int srcY = flip ? height - 1 - y : y;
int dstY = (srcY - srcRegion.y) / ySub;
// If subsampled or outside source region, skip entire row // 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 (y % ySub != 0 || srcY < srcRegion.y || srcY >= srcRegion.y + srcRegion.height) {
input.skipBytes(rowDataByte.length); input.skipBytes(rowDataByte.length);
return; return;
@@ -244,19 +283,7 @@ final class TGAImageReader extends ImageReaderBase {
} }
} }
switch (origin) { destChannel.setDataElements(0, dstY, srcChannel);
case TGA.ORIGIN_LOWER_LEFT:
// Flip into position
int dstY = (height - 1 - y - srcRegion.y) / ySub;
destChannel.setDataElements(0, dstY, srcChannel);
break;
case TGA.ORIGIN_UPPER_LEFT:
dstY = y / ySub;
destChannel.setDataElements(0, dstY, srcChannel);
break;
default:
throw new IIOException("Unsupported origin: " + origin);
}
} }
private void removeAlpha32(final byte[] rowData) { private void removeAlpha32(final byte[] rowData) {
@@ -265,10 +292,14 @@ final class TGAImageReader extends ImageReaderBase {
} }
} }
private void readRowUShort(final DataInput input, int height, Rectangle srcRegion, int origin, int xSub, int ySub, private void readRowUShort(final DataInput input, int height, Rectangle srcRegion, boolean flip, int xSub, int ySub,
short[] rowDataUShort, WritableRaster destChannel, Raster srcChannel, int y) throws IOException { short[] rowDataUShort, WritableRaster destChannel, Raster srcChannel, int y) throws IOException {
// Flip into position?
int srcY = flip ? height - 1 - y : y;
int dstY = (srcY - srcRegion.y) / ySub;
// If subsampled or outside source region, skip entire row // 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 (y % ySub != 0 || srcY < srcRegion.y || srcY >= srcRegion.y + srcRegion.height) {
input.skipBytes(rowDataUShort.length * 2); input.skipBytes(rowDataUShort.length * 2);
return; return;
@@ -283,19 +314,32 @@ final class TGAImageReader extends ImageReaderBase {
} }
} }
switch (origin) { destChannel.setDataElements(0, dstY, srcChannel);
case TGA.ORIGIN_LOWER_LEFT: }
// Flip into position
int dstY = (height - 1 - y - srcRegion.y) / ySub; private void readRowInt(final DataInput input, int height, Rectangle srcRegion, boolean flip, int xSub, int ySub,
destChannel.setDataElements(0, dstY, srcChannel); int[] rowDataInt, WritableRaster destChannel, Raster srcChannel, int y) throws IOException {
break; // Flip into position?
case TGA.ORIGIN_UPPER_LEFT: int srcY = flip ? height - 1 - y : y;
dstY = y / ySub; int dstY = (srcY - srcRegion.y) / ySub;
destChannel.setDataElements(0, dstY, srcChannel);
break; // If subsampled or outside source region, skip entire row
default: if (y % ySub != 0 || srcY < srcRegion.y || srcY >= srcRegion.y + srcRegion.height) {
throw new IIOException("Unsupported origin: " + origin); input.skipBytes(rowDataInt.length * 4);
return;
} }
readFully(input, rowDataInt);
// Subsample horizontal
if (xSub != 1) {
for (int x = srcRegion.x / xSub; x < ((srcRegion.x + srcRegion.width) / xSub); x++) {
rowDataInt[x] = rowDataInt[x * xSub];
}
}
destChannel.setDataElements(0, dstY, srcChannel);
} }
// TODO: Candidate util method // TODO: Candidate util method
@@ -311,6 +355,19 @@ final class TGAImageReader extends ImageReaderBase {
} }
} }
// TODO: Candidate util method
private static void readFully(final DataInput input, final int[] ints) throws IOException {
if (input instanceof ImageInputStream) {
// Optimization for ImageInputStreams, read all in one go
((ImageInputStream) input).readFully(ints, 0, ints.length);
}
else {
for (int i = 0; i < ints.length; i++) {
ints[i] = input.readInt();
}
}
}
private Raster clipRowToRect(final Raster raster, final Rectangle rect, final int[] bands, final int xSub) { private Raster clipRowToRect(final Raster raster, final Rectangle rect, final int[] bands, final int xSub) {
if (rect.contains(raster.getMinX(), 0, raster.getWidth(), 1) if (rect.contains(raster.getMinX(), 0, raster.getWidth(), 1)
&& xSub == 1 && xSub == 1
@@ -446,20 +503,26 @@ final class TGAImageReader extends ImageReaderBase {
// Thumbnail is always stored non-compressed, no need for RLE support // Thumbnail is always stored non-compressed, no need for RLE support
imageInput.seek(extensions.getThumbnailOffset() + 2); imageInput.seek(extensions.getThumbnailOffset() + 2);
int pixelDepth = header.getPixelDepth();
boolean flipped = isOriginLowerLeft(header.getOrigin());
for (int y = 0; y < height; y++) { for (int y = 0; y < height; y++) {
switch (header.getPixelDepth()) { switch (pixelDepth) {
case 8: case 8:
case 24: case 24:
case 32:
byte[] rowDataByte = ((DataBufferByte) rowRaster.getDataBuffer()).getData(); byte[] rowDataByte = ((DataBufferByte) rowRaster.getDataBuffer()).getData();
readRowByte(imageInput, height, srcRegion, header.getOrigin(), 1, 1, rowDataByte, destRaster, rowRaster, y); readRowByte(imageInput, height, srcRegion, flipped, 1, 1, rowDataByte, destRaster, rowRaster, y);
break; break;
case 16: case 16:
short[] rowDataUShort = ((DataBufferUShort) rowRaster.getDataBuffer()).getData(); short[] rowDataUShort = ((DataBufferUShort) rowRaster.getDataBuffer()).getData();
readRowUShort(imageInput, height, srcRegion, header.getOrigin(), 1, 1, rowDataUShort, destRaster, rowRaster, y); readRowUShort(imageInput, height, srcRegion, flipped, 1, 1, rowDataUShort, destRaster, rowRaster, y);
break;
case 32:
int[] rowDataInt = ((DataBufferInt) rowRaster.getDataBuffer()).getData();
readRowInt(imageInput, height, srcRegion, flipped, 1, 1, rowDataInt, destRaster, rowRaster, y);
break; break;
default: default:
throw new AssertionError("Unsupported pixel depth: " + header.getPixelDepth()); throw new AssertionError("Unsupported pixel depth: " + pixelDepth);
} }
processThumbnailProgress(100f * y / height); processThumbnailProgress(100f * y / height);
@@ -193,10 +193,13 @@ final class TGAMetadata extends AbstractMetadata {
switch (header.getPixelDepth()) { switch (header.getPixelDepth()) {
case 8: case 8:
bitsPerSample.setAttribute("value", createListValue(1, Integer.toString(header.getPixelDepth()))); bitsPerSample.setAttribute("value", createListValue(1, "8"));
break; break;
case 16: case 16:
if (header.getAttributeBits() > 0 && extensions != null && extensions.hasAlpha()) { if (header.getImageType() == TGA.IMAGETYPE_MONOCHROME || header.getImageType() == TGA.IMAGETYPE_MONOCHROME_RLE) {
bitsPerSample.setAttribute("value", "16");
}
else if (header.getAttributeBits() > 0 && extensions != null && extensions.hasAlpha()) {
bitsPerSample.setAttribute("value", "5, 5, 5, 1"); bitsPerSample.setAttribute("value", "5, 5, 5, 1");
} }
else { else {
@@ -91,7 +91,10 @@ public class TGAImageReaderTest extends ImageReaderAbstractTest<TGAImageReader>
new TestData(getClassLoaderResource("/tga/XING_T24.TGA"), new Dimension(240, 164)), // Uncompressed 24 bit BGR top/down new TestData(getClassLoaderResource("/tga/XING_T24.TGA"), new Dimension(240, 164)), // Uncompressed 24 bit BGR top/down
new TestData(getClassLoaderResource("/tga/XING_T32.TGA"), new Dimension(240, 164)), // Uncompressed 32 bit BGRA top/down new TestData(getClassLoaderResource("/tga/XING_T32.TGA"), new Dimension(240, 164)), // Uncompressed 32 bit BGRA top/down
new TestData(getClassLoaderResource("/tga/autodesk-3dsmax-extsize494.tga"), new Dimension(440, 200)) // RLE compressed 32 bit BGRA bottom/up new TestData(getClassLoaderResource("/tga/autodesk-3dsmax-extsize494.tga"), new Dimension(440, 200)), // RLE compressed 32 bit BGRA bottom/up
new TestData(getClassLoaderResource("/tga/monochrome16_top_left.tga"), new Dimension(64, 64)), // Uncompressed 16 bit monochrome
new TestData(getClassLoaderResource("/tga/monochrome16_top_left_rle.tga"), new Dimension(64, 64)) // RLE compressed 16 bit monochrome
); );
} }
+1 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId> <artifactId>imageio</artifactId>
<version>3.7.0</version> <version>3.7.2-SNAPSHOT</version>
</parent> </parent>
<artifactId>imageio-thumbsdb</artifactId> <artifactId>imageio-thumbsdb</artifactId>
<name>TwelveMonkeys :: ImageIO :: Thumbs.db plugin</name> <name>TwelveMonkeys :: ImageIO :: Thumbs.db plugin</name>
+57
View File
@@ -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-tiff-jai-interop</artifactId>
<name>TwelveMonkeys :: ImageIO :: TIFF/JAI Metadata Interop</name>
<description>
Test TIFF plugin and JAI TIFF plugin Metadata interoperability
</description>
<properties>
<project.jpms.module.name>com.twelvemonkeys.imageio.tiff.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-tiff</artifactId>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,101 @@
/*
* 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.tiff.jaiinterop;
import com.twelvemonkeys.imageio.metadata.tiff.Rational;
import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
import com.twelvemonkeys.imageio.metadata.tiff.TIFFEntry;
import com.twelvemonkeys.imageio.plugins.tiff.TIFFImageMetadata;
import org.junit.Test;
import javax.imageio.ImageIO;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriter;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataFormatImpl;
import javax.imageio.metadata.IIOMetadataNode;
import java.awt.image.BufferedImage;
import java.util.Arrays;
import java.util.Iterator;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/**
* Tests our TIFFImageMetadata works with JAI TIFFImageWriter.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: TIFFImageReaderJDKJPEGInteroperabilityTest.java,v 1.0 08.05.12 15:25 haraldk Exp$
*/
public class TIFFImageMetadataJAInteroperabilityTest {
private static final String JAI_TIFF_PROVIDER_CLASS_NAME = "com.github.jaiimageio.impl.plugins.tiff.TIFFImageWriterSpi";
private ImageWriter createImageWriter() {
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("TIFF");
while (writers.hasNext()) {
ImageWriter writer = writers.next();
if (JAI_TIFF_PROVIDER_CLASS_NAME.equals(writer.getOriginatingProvider().getClass().getName())) {
return writer;
}
}
throw new AssertionError("Expected Spi not found (dependency issue?): " + JAI_TIFF_PROVIDER_CLASS_NAME);
}
@Test
public void testRationalNeedsDenominator() {
// Set the resolution to 200 dpi
IIOMetadata ourMetadata = new TIFFImageMetadata(Arrays.asList(new TIFFEntry(TIFF.TAG_RESOLUTION_UNIT, 2), // Unit DPI (default)
new TIFFEntry(TIFF.TAG_X_RESOLUTION, new Rational(200)),
new TIFFEntry(TIFF.TAG_Y_RESOLUTION, new Rational(200))));
ImageTypeSpecifier type = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY);
ImageWriter writer = createImageWriter();
IIOMetadata converted = writer.convertImageMetadata(ourMetadata, type, null);
assertNotNull(converted);
// Make sure we have x/y resolution in converted metadata
IIOMetadataNode standardTree = (IIOMetadataNode) converted.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName);
String horizontalPixelSize = ((IIOMetadataNode) standardTree.getElementsByTagName("HorizontalPixelSize").item(0)).getAttribute("value");
String verticalPixelSize = ((IIOMetadataNode) standardTree.getElementsByTagName("VerticalPixelSize").item(0)).getAttribute("value");
// For some reason this is *pixel size* in *mm*...
String expected = String.valueOf(2.54 / 200 * 10);
assertEquals(expected, horizontalPixelSize);
assertEquals(expected, verticalPixelSize);
}
}
+15 -2
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId> <artifactId>imageio</artifactId>
<version>3.7.0</version> <version>3.7.2-SNAPSHOT</version>
</parent> </parent>
<artifactId>imageio-tiff-jdk-interop</artifactId> <artifactId>imageio-tiff-jdk-interop</artifactId>
<name>TwelveMonkeys :: ImageIO :: TIFF/JDK JPEG Interop</name> <name>TwelveMonkeys :: ImageIO :: TIFF/JDK JPEG Interop</name>
@@ -13,9 +13,22 @@
</description> </description>
<properties> <properties>
<project.jpms.module.name>com.twelvemonkeys.imageio.jdkinterop</project.jpms.module.name> <project.jpms.module.name>com.twelvemonkeys.imageio.tiff.jdkinterop</project.jpms.module.name>
</properties> </properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
@@ -28,25 +28,26 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.imageio.plugins.jpeg.jdkinterop; package com.twelvemonkeys.imageio.plugins.tiff.jdkinterop;
import com.twelvemonkeys.imageio.plugins.tiff.TIFFImageReader; import static org.junit.Assert.fail;
import com.twelvemonkeys.imageio.plugins.tiff.TIFFImageReaderSpi;
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.ImageReaderSpi;
import java.awt.*; import java.awt.*;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import static org.junit.Assert.fail; import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.spi.ImageReaderSpi;
import org.junit.Ignore;
import org.junit.Test;
import com.twelvemonkeys.imageio.plugins.tiff.TIFFImageReader;
import com.twelvemonkeys.imageio.plugins.tiff.TIFFImageReaderSpi;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
/** /**
* Tests our TIFFImageReader delegating to the JDK JPEGImageReader. * Tests our TIFFImageReader delegating to the JDK JPEGImageReader.
+1 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId> <artifactId>imageio</artifactId>
<version>3.7.0</version> <version>3.7.2-SNAPSHOT</version>
</parent> </parent>
<artifactId>imageio-tiff</artifactId> <artifactId>imageio-tiff</artifactId>
<name>TwelveMonkeys :: ImageIO :: TIFF plugin</name> <name>TwelveMonkeys :: ImageIO :: TIFF plugin</name>
@@ -53,7 +53,7 @@ final class BigTIFFProviderInfo extends ReaderWriterProviderInfo {
null, null,
null, null,
false, TIFFStreamMetadata.SUN_NATIVE_STREAM_METADATA_FORMAT_NAME, "com.twelvemonkeys.imageio.plugins.tiff.TIFFStreamMetadataFormat", null, null, false, TIFFStreamMetadata.SUN_NATIVE_STREAM_METADATA_FORMAT_NAME, "com.twelvemonkeys.imageio.plugins.tiff.TIFFStreamMetadataFormat", null, null,
true, TIFFMedataFormat.SUN_NATIVE_IMAGE_METADATA_FORMAT_NAME, "com.twelvemonkeys.imageio.plugins.tiff.TIFFMedataFormat", null, null true, TIFFImageMetadataFormat.SUN_NATIVE_IMAGE_METADATA_FORMAT_NAME, "com.twelvemonkeys.imageio.plugins.tiff.TIFFMedataFormat", null, null
); );
} }
} }
@@ -58,8 +58,6 @@ final class CCITTFaxDecoderStream extends FilterInputStream {
private final boolean optionUncompressed; private final boolean optionUncompressed;
private final boolean optionByteAligned; private final boolean optionByteAligned;
// Need to take fill order into account (?) (use flip table?)
private final int fillOrder;
private final int type; private final int type;
private int decodedLength; private int decodedLength;
@@ -81,12 +79,10 @@ final class CCITTFaxDecoderStream extends FilterInputStream {
* @param columns the number of columns in the stream. * @param columns the number of columns in the stream.
* @param type the type of stream, must be one of {@code COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE}, * @param type the type of stream, must be one of {@code COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE},
* {@code COMPRESSION_CCITT_T4} or {@code COMPRESSION_CCITT_T6}. * {@code COMPRESSION_CCITT_T4} or {@code COMPRESSION_CCITT_T6}.
* @param fillOrder fillOrder, must be {@code FILL_LEFT_TO_RIGHT} or
* {@code FILL_RIGHT_TO_LEFT}.
* @param options CCITT T.4 or T.6 options. * @param options CCITT T.4 or T.6 options.
* @param byteAligned enable byte alignment used in PDF files (EncodedByteAlign). * @param byteAligned enable byte alignment used in PDF files (EncodedByteAlign).
*/ */
public CCITTFaxDecoderStream(final InputStream stream, final int columns, final int type, final int fillOrder, public CCITTFaxDecoderStream(final InputStream stream, final int columns, final int type,
final long options, final boolean byteAligned) { final long options, final boolean byteAligned) {
super(Validate.notNull(stream, "stream")); super(Validate.notNull(stream, "stream"));
@@ -95,10 +91,6 @@ final class CCITTFaxDecoderStream extends FilterInputStream {
type == TIFFExtension.COMPRESSION_CCITT_T4 || type == TIFFExtension.COMPRESSION_CCITT_T4 ||
type == TIFFExtension.COMPRESSION_CCITT_T6, type == TIFFExtension.COMPRESSION_CCITT_T6,
type, "Only CCITT Modified Huffman RLE compression (2), CCITT T4 (3) or CCITT T6 (4) supported: %s"); type, "Only CCITT Modified Huffman RLE compression (2), CCITT T4 (3) or CCITT T6 (4) supported: %s");
this.fillOrder = Validate.isTrue(
fillOrder == TIFFBaseline.FILL_LEFT_TO_RIGHT || fillOrder == TIFFExtension.FILL_RIGHT_TO_LEFT,
fillOrder, "Expected fill order 1 or 2: %s"
);
// We know this is only used for b/w (1 bit) // We know this is only used for b/w (1 bit)
decodedRow = new byte[(columns + 7) / 8]; decodedRow = new byte[(columns + 7) / 8];
@@ -140,54 +132,63 @@ final class CCITTFaxDecoderStream extends FilterInputStream {
* @param columns the number of columns in the stream. * @param columns the number of columns in the stream.
* @param type the type of stream, must be one of {@code COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE}, * @param type the type of stream, must be one of {@code COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE},
* {@code COMPRESSION_CCITT_T4} or {@code COMPRESSION_CCITT_T6}. * {@code COMPRESSION_CCITT_T4} or {@code COMPRESSION_CCITT_T6}.
* @param fillOrder fillOrder, must be {@code FILL_LEFT_TO_RIGHT} or
* {@code FILL_RIGHT_TO_LEFT}.
* @param options CCITT T.4 or T.6 options. * @param options CCITT T.4 or T.6 options.
*/ */
public CCITTFaxDecoderStream(final InputStream stream, final int columns, final int type, final int fillOrder, public CCITTFaxDecoderStream(final InputStream stream, final int columns, final int type,
final long options) { final long options) {
this(stream, columns, type, fillOrder, options, type == TIFFBaseline.COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE); this(stream, columns, type, options, type == TIFFBaseline.COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE);
} }
static int findCompressionType(final int type, final InputStream in) throws IOException { static int findCompressionType(final int encodedType, final InputStream stream) throws IOException {
// Discover possible incorrect type, revert to RLE // Discover possible incorrect compression type, revert to RLE if no EOLs found
if (type == TIFFExtension.COMPRESSION_CCITT_T4 && in.markSupported()) { if (encodedType == TIFFExtension.COMPRESSION_CCITT_T4 && stream.markSupported()) {
byte[] streamData = new byte[32]; int limit = 512;
try { try {
in.mark(streamData.length); stream.mark(limit);
int first = stream.read();
int second = stream.read();
int offset = 0; if (second == -1) {
while (offset < streamData.length) { // stream to short
int read = in.read(streamData, offset, streamData.length - offset); return encodedType;
if (read <= 0) { }
break; else if (first == 0 && (((byte) second) >> 4 == 1 || ((byte) second) == 1)) {
// correct, starts with EOL or byte aligned EOL
return encodedType;
}
short b = (short) (((((byte) first) << 8) + ((byte) second)) >> 4);
int limitBits = limit * 8;
int read = second;
byte streamByte = (byte) read;
for (int i = 12; i < limitBits; i++) {
if (i % 8 == 0) {
read = stream.read();
if (read == -1) {
// no EOL before stream end
return TIFFBaseline.COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE;
}
} }
offset += read; b = (short) ((b << 1) + ((streamByte >> (7 - (i % 8))) & 0x01));
}
}
finally {
in.reset();
}
if (streamData[0] != 0 || (streamData[1] >> 4 != 1 && streamData[1] != 1)) {
// Leading EOL (0b000000000001) not found, search further and try RLE if not found
int numBits = streamData.length * 8;
short b = (short) (((streamData[0] << 8) + streamData[1]) >> 4);
for (int i = 12; i < numBits; i++) {
b = (short) ((b << 1) + ((streamData[(i / 8)] >> (7 - (i % 8))) & 0x01));
if ((b & 0xFFF) == 1) { if ((b & 0xFFF) == 1) {
// found EOL
return TIFFExtension.COMPRESSION_CCITT_T4; return TIFFExtension.COMPRESSION_CCITT_T4;
} }
} }
// no EOL till limit
return TIFFBaseline.COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE; return TIFFBaseline.COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE;
} }
finally {
stream.reset();
}
} }
return type; return encodedType;
} }
private void fetch() throws IOException { private void fetch() throws IOException {
@@ -197,13 +198,17 @@ final class CCITTFaxDecoderStream extends FilterInputStream {
try { try {
decodeRow(); decodeRow();
} }
catch (ArrayIndexOutOfBoundsException e) {
// Mask the AIOOBE as an IOException
throw new IOException("Malformed CCITT stream", e);
}
catch (EOFException e) { catch (EOFException e) {
// TODO: Rewrite to avoid throw/catch for normal flow... // TODO: Rewrite to avoid throw/catch for normal flow...
if (decodedLength != 0) { if (decodedLength != 0) {
throw e; throw e;
} }
// ..otherwise, just let client code try to read past the // ...otherwise, just let client code try to read past the
// end of stream // end of stream
decodedLength = -1; decodedLength = -1;
} }
@@ -468,7 +473,7 @@ final class CCITTFaxDecoderStream extends FilterInputStream {
int bufferPos = -1; int bufferPos = -1;
private boolean readBit() throws IOException { private boolean readBit() throws IOException {
if (bufferPos < 0 || bufferPos > 7) { if (bufferPos > 7 || bufferPos < 0) {
buffer = in.read(); buffer = in.read();
if (buffer == -1) { if (buffer == -1) {
@@ -478,21 +483,10 @@ final class CCITTFaxDecoderStream extends FilterInputStream {
bufferPos = 0; bufferPos = 0;
} }
boolean isSet; boolean isSet = (buffer & 0x80) != 0;
buffer <<= 1;
if (fillOrder == TIFFBaseline.FILL_LEFT_TO_RIGHT) {
isSet = ((buffer >> (7 - bufferPos)) & 1) == 1;
}
else {
isSet = ((buffer >> (bufferPos)) & 1) == 1;
}
bufferPos++; bufferPos++;
if (bufferPos > 7) {
bufferPos = -1;
}
return isSet; return isSet;
} }

Some files were not shown because too many files have changed in this diff Show More