Compare commits

...

62 Commits

Author SHA1 Message Date
snyk-bot b794d05035 fix: upgrade com.twelvemonkeys.common:common-image from 3.1-SNAPSHOT to 3.7.0
Snyk has created this PR to upgrade com.twelvemonkeys.common:common-image from 3.1-SNAPSHOT to 3.7.0.

See this package in Maven Repository:
https://mvnrepository.com/artifact/com.twelvemonkeys.common/common-image/

See this project in Snyk:
https://app.snyk.io/org/haraldk/project/b53d0a96-fb95-4707-a6b4-5e37b1b75566?utm_source=github&utm_medium=referral&page=upgrade-pr
2021-12-03 20:34:07 +00:00
Harald Kuhr 511a29beb9 Minor AffineTransformOp clean-up + removed test output. 2021-10-29 11:05:24 +02:00
Gauthier 5617b4323c 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
2021-10-26 18:38:33 +02:00
Harald Kuhr 16d0af357d #629: Fixed build 2021-10-15 16:04:53 +02:00
Harald Kuhr 74927d5396 #629: Preliminary WebP animation (ANIM/ANMF) support 2021-10-15 15:08:39 +02:00
Harald Kuhr 7e809dd834 Readme improvements 2021-10-15 14:48:39 +02:00
Harald Kuhr 7aa95a08bc Fixed Travis link + bonus project summary updates 2021-10-15 09:41:47 +02:00
Harald Kuhr c28963ae49 Minor servlet clean-up. 2021-09-21 14:23:59 +02:00
Harald Kuhr 0327f5fc1a Servlet deprecation 2021-09-21 14:22:16 +02:00
Harald Kuhr 1c59057c30 #628: Stabilized build + better dependency scopes and module names in interop modules 2021-09-17 20:32:25 +02:00
Harald Kuhr 3e1f85c4dc #626 Clean up + fill order support for all compression types 2021-09-17 19:37:28 +02:00
Harald Kuhr 11227a68a0 #628 TIFF metadata fix, now always outputs denominator for rationals.
+ Bonus: Added JAI TIFF interop module with test and other minor fixes.
2021-09-17 16:34:38 +02:00
Oliver Schmidtmer 62ba73a30e #626: Handle fillOrder in TIFFImageReader, not in CCITTFaxDecoderStream (#627) 2021-09-17 16:16:30 +02:00
Harald Kuhr 1f33afb5a1 Fixed NullPointerException due to missing PhotometricInterpretation, now uses fallback as we do when reading. 2021-09-16 22:56:13 +02:00
Harald Kuhr 9d3f271867 #626 TIFF CCITT detection only once per IFD 2021-09-16 22:25:06 +02:00
Harald Kuhr 812e12acb0 #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.
2021-09-07 09:29:13 +02:00
Harald Kuhr 060b6cf852 #624: Added metadata support for 16 bit USHORT gray. 2021-09-07 09:24:27 +02:00
Koen De Groote e68ce7ffd1 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>
2021-09-03 16:19:05 +02:00
Harald Kuhr 778cdef69c Fix typo in TIFFImageMetadataFormat mk II. 2021-08-31 22:34:47 +02:00
Harald Kuhr d46a76fca8 Fix typo in TIFFImageMetadataFormat. 2021-08-31 22:26:32 +02:00
Harald Kuhr 105a1ee466 #621 Don't add ICC profile for default gray images 2021-08-31 22:16:08 +02:00
Harald Kuhr aa030f526c #617 BigTIFF write clean-up. 2021-08-31 20:24:42 +02:00
Harald Kuhr 976e5d6210 #619: Fix WebP Y'CbCr->RGB conversion (now uses rec 601) 2021-08-26 16:47:51 +02:00
Harald Kuhr 6daca00fcd Minor clean-up. 2021-08-09 21:24:32 +02:00
Harald Kuhr ef05872934 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/tiff/TIFFWriter.java
2021-08-09 21:22:46 +02:00
Harald Kuhr 1ddab866fd #617 BigTIFF write support. 2021-08-09 21:11:40 +02:00
Harald Kuhr ce997a6951 Some more minor clean-up. 2021-08-06 10:10:34 +02:00
Harald Kuhr 23bf5cb7b2 Minor clean-up. 2021-08-06 09:59:07 +02:00
Harald Kuhr 564778f415 #616: Remove dependency on old xmlgraphics-commons (no longer needed) 2021-08-04 11:25:58 +02:00
Harald Kuhr e28bf8fb44 Fix WebP ICC handling for images with alpha. 2021-07-12 10:01:16 +02:00
Harald Kuhr cf8d630d01 Add WebP to BOM. 2021-07-12 09:38:20 +02:00
Harald Kuhr 0ff7224912 Switch build from travis.ci.org to com 2021-06-29 16:56:58 +02:00
Koen De Groote 196081a317 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.
2021-06-29 13:06:07 +02:00
Harald Kuhr ff50180d86 #609 Fixed ICC Profile handling in WebP. 2021-06-03 21:31:54 +02:00
Harald Kuhr 8f2c482167 Minor code clean-up. 2021-06-03 18:21:00 +02:00
Harald Kuhr eab24890ca Merge pull request #608 from Schmidor/ccitt_eol_search
#579 Deeper EOL search in the CCITT stream
2021-05-31 10:40:20 +02:00
Oliver Schmidtmer cd42d81817 Invert EOF check 2021-05-28 14:38:44 +02:00
Oliver Schmidtmer ba5c667b6c #579 Deeper EOL search in the CCITT stream 2021-05-27 22:11:13 +02:00
Harald Kuhr 4e9fa9442c PR template update. 2021-05-13 15:15:09 +02:00
Harald Kuhr 4d2326c18d Update issue templates 2021-05-09 18:12:34 +02:00
Harald Kuhr 94eac2d6e5 XXX: Remove another old servlet class. 2021-05-09 18:08:56 +02:00
Harald Kuhr f63a33d541 HTTPS links in README.md 2021-05-09 18:08:43 +02:00
Harald Kuhr 00f8d87f36 PR template + rename issue template. 2021-05-09 18:08:16 +02:00
Harald Kuhr 4c2ab6da7b Update issue templates 2021-05-08 20:41:13 +02:00
Harald Kuhr b5088312e2 Update issue templates 2021-05-07 19:39:41 +02:00
Harald Kuhr f04f968f12 Update issue templates 2021-05-07 19:35:02 +02:00
Harald Kuhr 8896092e31 Update issue templates 2021-05-07 19:29:52 +02:00
Harald Kuhr 2f9768a1d4 XXX: Remove old servlet class. 2021-05-06 00:14:21 +02:00
Harald Kuhr 06bcf22242 #483 Minor optimization 2021-05-06 00:12:19 +02:00
Harald Kuhr 20c7f8e60e #483 Add license headers. 2021-05-06 00:08:27 +02:00
Harald Kuhr 15a9ad0a9b #483 Initial PSD Write support 2021-05-06 00:01:54 +02:00
Harald Kuhr 7ae2d636dc Merge pull request #607 from haraldk/snyk-upgrade-b72dc0a35c77c27ccf914333993bfcb1
[Snyk] Upgrade com.github.jai-imageio:jai-imageio-core from 1.3.0 to 1.4.0
2021-04-30 08:45:03 +02:00
snyk-bot 12e756b23c fix: upgrade com.github.jai-imageio:jai-imageio-core from 1.3.0 to 1.4.0
Snyk has created this PR to upgrade com.github.jai-imageio:jai-imageio-core from 1.3.0 to 1.4.0.

See this package in Maven Repository:
https://mvnrepository.com/artifact/com.github.jai-imageio/jai-imageio-core/

See this project in Snyk:
https://app.snyk.io/org/haraldk/project/33153897-db54-44bf-a11a-efd834cecdae?utm_source=github&utm_medium=upgrade-pr
2021-04-30 00:42:19 +00:00
Harald Kuhr 4e2bf131d2 #606: Fix bug introduced by more aggressive readDirect. 2021-04-29 20:06:36 +02:00
Harald Kuhr d0c4a07556 #606: Workaround for broken JDK WBMPImageReader 2021-04-29 16:55:24 +02:00
Harald Kuhr 21059c8d5a Cleaner tests for Java 6 or later... A little late. :-) 2021-04-29 16:46:16 +02:00
Harald Kuhr fa7b530809 Adding GitHub sponsors link. 2021-04-28 14:21:37 +02:00
Harald Kuhr 790cf3b32e Test clean-up. 2021-04-26 22:42:44 +02:00
Harald Kuhr b1baaad23b Rename file, add missing file extension. 2021-04-25 19:55:33 +02:00
Harald Kuhr 7fa704ace5 Bump plugins and stop deploying useless (internal) artifacts. 2021-04-24 19:26:32 +02:00
Harald Kuhr 8d07f4fe90 Updated versions to 3.7.0. 2021-04-24 13:20:40 +02:00
Harald Kuhr 32bba6857b [maven-release-plugin] prepare for next development iteration 2021-04-24 12:31:07 +02:00
210 changed files with 4072 additions and 2199 deletions
+1
View File
@@ -0,0 +1 @@
github: haraldk
+53
View File
@@ -0,0 +1,53 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: Reported bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**Version information**
1. The version of the TwelveMonkeys ImageIO library in use.
For example: 4.0.0
2. The *exact* output of `java --version` (or `java -version` for older Java releases).
For example:
java version "1.8.0_271"
Java(TM) SE Runtime Environment (build 1.8.0_271-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.271-b09, mixed mode)
3. Extra information about OS version, server version, standalone program or web application packaging, executable wrapper, etc. Please state exact version numbers where applicable.
**To Reproduce**
Steps to reproduce the behavior:
1. Compile the below sample code
2. Download the sample image file
3. Run the code with the sample file
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Example code**
Preferably as a failing JUnit test, or a standalone program with a `main` method that showcases the problem.
Less is more. Don't add your entire project, only the code required to reproduce the problem. 😀
**Sample file(s)**
Attach any sample files needed to reproduce the problem. Use a ZIP-file if the format is not directly supported by GitHub.
**Stak trace**
Always include the stack trace you experience.
**Screenshots**
If applicable, add screenshots to help explain your problem.
Do not add screenshots of code or stack traces. 😀
**Additional context**
Add any other context about the problem here.
+20
View File
@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: New feature
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem or use case is.
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here, like links to specifications or sample files.
@@ -0,0 +1,13 @@
---
name: Trouble shooting and programming help
about: "General programming issues will reach a wider audience at StackOverflow. Tag
questions with javax-imageio and/or twelvemonkeys \U0001F600 "
title: ''
labels: Trouble-shooting
assignees: ''
---
General programming issues and problems will reach a much wider audience at StackOverflow, we suggest you ask them there. This will offload our work with maintaining the library, and make sure you get better help sooner.
Tag the question with `javax-imageio` and/or `twelvemonkeys` and we'll find them there.
@@ -0,0 +1,11 @@
**What is fixed** Add link to the issue this PR fixes.
Example: Fixes #42.
**Why is this change proposed** If this change does *not* fix an open issue, briefly describe the rationale for this PR.
**What is changed** Briefly describe the changes proposed in this pull request:
* Fixed rare exception happening in `x >= 42` case
* Small optimization of `decompress()` method
* Corrected API doc for `compress()` method to reflect current implementation
+5 -3
View File
@@ -1,9 +1,11 @@
dist: trusty
language: java
jdk:
- oraclejdk8 # Legacy
- oraclejdk11 # LTS
- oraclejdk15 # Latest
- oraclejdk8 # LTS until Mar 2022
- oraclejdk11 # LTS until Sep 2023
- oraclejdk15 # out of support
- oraclejdk16 # out of support
- oraclejdk17 # LTS until Sep 2026
jobs:
include:
# Extra job, testing legacy CMM option
+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)
[![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)
## About
TwelveMonkeys ImageIO is a collection of plugins and extensions for Java's ImageIO.
TwelveMonkeys ImageIO provides extended image file format support for the Java platform, through plugins for the `javax.imageio.*` package.
These plugins extend the number of image file formats supported in Java, using the `javax.imageio.*` package.
The main purpose of this project is to provide support for formats not covered by the JRE itself.
Support for formats is important, both to be able to read data found
The main goal of this project is to provide support for formats not covered by the JRE itself.
Support for these formats is important, to be able to read data found
"in the wild", as well as to maintain access to data in legacy formats.
Because there is lots of legacy data out there, we see the need for open implementations of readers for popular formats.
The goal is to create a set of efficient and robust ImageIO plug-ins, that can be distributed independently.
As there is lots of legacy data out there, we see the need for open implementations of readers for popular formats.
----
@@ -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
| [TIFF](https://github.com/haraldk/TwelveMonkeys/wiki/TIFF-Plugin) | **TIFF** | Aldus/Adobe Tagged Image File Format | ✔ | ✔ | [Native](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/tiff_metadata.html#ImageMetadata) & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| | BigTIFF | | ✔ | - | [Native](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/tiff_metadata.html#ImageMetadata) & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| [WebP](https://github.com/haraldk/TwelveMonkeys/wiki/WebP-Plugin) | **WebP** | Google WebP Format | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) | 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) |
**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.*
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
[Java Image I/O API Guide](http://docs.oracle.com/javase/7/docs/technotes/guides/imageio/spec/imageio_guideTOC.fm.html)
[Java Image I/O API Guide](https://docs.oracle.com/javase/7/docs/technotes/guides/imageio/spec/imageio_guideTOC.fm.html)
from Oracle.
#### Adobe Clipping Path support
@@ -220,14 +217,14 @@ BufferedImage output = ditherer.filter(input, null);
## Building
Download the project (using [Git](http://git-scm.com/downloads)):
Download the project (using [Git](https://git-scm.com/downloads)):
$ git clone git@github.com:haraldk/TwelveMonkeys.git
This should create a folder named `TwelveMonkeys` in your current directory. Change directory to the `TwelveMonkeys`
folder, and issue the command below to build.
Build the project (using [Maven](http://maven.apache.org/download.cgi)):
Build the project (using [Maven](https://maven.apache.org/download.cgi)):
$ mvn package
@@ -274,12 +271,12 @@ To depend on the JPEG and TIFF plugin using Maven, add the following to your POM
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-jpeg</artifactId>
<version>3.6.4</version>
<version>3.7.0</version>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-tiff</artifactId>
<version>3.6.4</version>
<version>3.7.0</version>
</dependency>
<!--
@@ -289,7 +286,7 @@ To depend on the JPEG and TIFF plugin using Maven, add the following to your POM
<dependency>
<groupId>com.twelvemonkeys.servlet</groupId>
<artifactId>servlet</artifactId>
<version>3.6.4</version>
<version>3.7.0</version>
</dependency>
</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:
twelvemonkeys-common-lang-3.6.4.jar
twelvemonkeys-common-io-3.6.4.jar
twelvemonkeys-common-image-3.6.4.jar
twelvemonkeys-imageio-core-3.6.4.jar
twelvemonkeys-imageio-metadata-3.6.4.jar
twelvemonkeys-imageio-jpeg-3.6.4.jar
twelvemonkeys-imageio-tiff-3.6.4.jar
twelvemonkeys-common-lang-3.7.0.jar
twelvemonkeys-common-io-3.7.0.jar
twelvemonkeys-common-image-3.7.0.jar
twelvemonkeys-imageio-core-3.7.0.jar
twelvemonkeys-imageio-metadata-3.7.0.jar
twelvemonkeys-imageio-jpeg-3.7.0.jar
twelvemonkeys-imageio-tiff-3.7.0.jar
#### Deploying the plugins in a web app
@@ -370,77 +367,79 @@ Other "fat" JAR bundlers will probably have similar mechanisms to merge entries
### Links to prebuilt binaries
##### Latest version (3.6.4)
##### Latest version (3.7.0)
Requires Java 7 or later.
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-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-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-lang-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-lang/3.7.0/common-lang-3.7.0.jar)
* [common-io-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-io/3.7.0/common-io-3.7.0.jar)
* [common-image-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-image/3.7.0/common-image-3.7.0.jar)
ImageIO dependencies
* [imageio-core-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-core/3.6.4/imageio-core-3.6.4.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-core-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-core/3.7.0/imageio-core-3.7.0.jar)
* [imageio-metadata-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-metadata/3.7.0/imageio-metadata-3.7.0.jar)
ImageIO plugins
* [imageio-bmp-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-bmp/3.6.4/imageio-bmp-3.6.4.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-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-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-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-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-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-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-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-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-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-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-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-bmp-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-bmp/3.7.0/imageio-bmp-3.7.0.jar)
* [imageio-hdr-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-hdr/3.7.0/imageio-hdr-3.7.0.jar)
* [imageio-icns-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-icns/3.7.0/imageio-icns-3.7.0.jar)
* [imageio-iff-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-iff/3.7.0/imageio-iff-3.7.0.jar)
* [imageio-jpeg-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jpeg/3.7.0/imageio-jpeg-3.7.0.jar)
* [imageio-pcx-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pcx/3.7.0/imageio-pcx-3.7.0.jar)
* [imageio-pict-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pict/3.7.0/imageio-pict-3.7.0.jar)
* [imageio-pnm-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pnm/3.7.0/imageio-pnm-3.7.0.jar)
* [imageio-psd-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-psd/3.7.0/imageio-psd-3.7.0.jar)
* [imageio-sgi-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-sgi/3.7.0/imageio-sgi-3.7.0.jar)
* [imageio-tga-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tga/3.7.0/imageio-tga-3.7.0.jar)
* [imageio-thumbsdb-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-thumbsdb/3.7.0/imageio-thumbsdb-3.7.0.jar)
* [imageio-tiff-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tiff/3.7.0/imageio-tiff-3.7.0.jar)
* [imageio-webp-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-webp/3.7.0/imageio-webp-3.7.0.jar)
* [imageio-xwd-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-xwd/3.7.0/imageio-xwd-3.7.0.jar)
ImageIO plugins requiring 3rd party libs
* [imageio-batik-3.6.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
* [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-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)
Use this version for projects that requires Java 6 or need the JMagick support. *Does not support Java 8 or later*.
Common dependencies
* [common-lang-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-lang/3.0.2/common-lang-3.0.2.jar)
* [common-io-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-io/3.0.2/common-io-3.0.2.jar)
* [common-image-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-image/3.0.2/common-image-3.0.2.jar)
* [common-lang-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-lang/3.0.2/common-lang-3.0.2.jar)
* [common-io-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-io/3.0.2/common-io-3.0.2.jar)
* [common-image-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-image/3.0.2/common-image-3.0.2.jar)
ImageIO dependencies
* [imageio-core-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-core/3.0.2/imageio-core-3.0.2.jar)
* [imageio-metadata-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-metadata/3.0.2/imageio-metadata-3.0.2.jar)
* [imageio-core-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-core/3.0.2/imageio-core-3.0.2.jar)
* [imageio-metadata-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-metadata/3.0.2/imageio-metadata-3.0.2.jar)
ImageIO plugins
* [imageio-jpeg-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jpeg/3.0.2/imageio-jpeg-3.0.2.jar)
* [imageio-tiff-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tiff/3.0.2/imageio-tiff-3.0.2.jar)
* [imageio-psd-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-psd/3.0.2/imageio-psd-3.0.2.jar)
* [imageio-pict-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pict/3.0.2/imageio-pict-3.0.2.jar)
* [imageio-iff-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-iff/3.0.2/imageio-iff-3.0.2.jar)
* [imageio-icns-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-icns/3.0.2/imageio-icns-3.0.2.jar)
* [imageio-ico-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-ico/3.0.2/imageio-ico-3.0.2.jar)
* [imageio-thumbsdb-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-thumbsdb/3.0.2/imageio-thumbsdb-3.0.2.jar)
* [imageio-jpeg-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jpeg/3.0.2/imageio-jpeg-3.0.2.jar)
* [imageio-tiff-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tiff/3.0.2/imageio-tiff-3.0.2.jar)
* [imageio-psd-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-psd/3.0.2/imageio-psd-3.0.2.jar)
* [imageio-pict-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pict/3.0.2/imageio-pict-3.0.2.jar)
* [imageio-iff-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-iff/3.0.2/imageio-iff-3.0.2.jar)
* [imageio-icns-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-icns/3.0.2/imageio-icns-3.0.2.jar)
* [imageio-ico-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-ico/3.0.2/imageio-ico-3.0.2.jar)
* [imageio-thumbsdb-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-thumbsdb/3.0.2/imageio-thumbsdb-3.0.2.jar)
ImageIO plugins requiring 3rd party libs
* [imageio-batik-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-batik/3.0.2/imageio-batik-3.0.2.jar)
* [imageio-jmagick-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jmagick/3.0.2/imageio-jmagick-3.0.2.jar)
* [imageio-batik-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-batik/3.0.2/imageio-batik-3.0.2.jar)
* [imageio-jmagick-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jmagick/3.0.2/imageio-jmagick-3.0.2.jar)
Servlet support
* [servlet-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/servlet/servlet/3.0.2/servlet-3.0.2.jar)
* [servlet-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/servlet/servlet/3.0.2/servlet-3.0.2.jar)
## License
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
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.
You can read more about the registry and the lookup mechanism in the [IIORegistry API doc](http://docs.oracle.com/javase/7/docs/api/javax/imageio/spi/IIORegistry.html).
You can read more about the registry and the lookup mechanism in the [IIORegistry API doc](https://docs.oracle.com/javase/7/docs/api/javax/imageio/spi/IIORegistry.html).
The fine print: The TwelveMonkeys service providers for JPEG, BMP and TIFF, overrides the onRegistration method, and
utilizes the pairwise partial ordering mechanism of the `IIOServiceRegistry` to make sure it is installed before
+6 -1
View File
@@ -5,7 +5,7 @@
<parent>
<groupId>com.twelvemonkeys</groupId>
<artifactId>twelvemonkeys</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<groupId>com.twelvemonkeys.bom</groupId>
@@ -123,6 +123,11 @@
<artifactId>imageio-tiff</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-webp</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-xwd</artifactId>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.common</groupId>
<artifactId>common</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<artifactId>common-image</artifactId>
<packaging>jar</packaging>
@@ -34,7 +34,13 @@ import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.*;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.ColorModel;
import java.awt.image.ImagingOpException;
import java.awt.image.Raster;
import java.awt.image.RasterOp;
import java.awt.image.WritableRaster;
/**
* This is a drop-in replacement for {@link java.awt.image.AffineTransformOp}.
@@ -70,6 +76,7 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp {
delegate = new java.awt.image.AffineTransformOp(xform, interpolationType);
}
@SuppressWarnings("ConstantConditions")
@Override
public BufferedImage filter(final BufferedImage src, BufferedImage dst) {
try {
@@ -80,10 +87,9 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp {
dst = createCompatibleDestImage(src, src.getColorModel());
}
Graphics2D g2d = null;
Graphics2D g2d = dst.createGraphics();
try {
g2d = dst.createGraphics();
int interpolationType = delegate.getInterpolationType();
if (interpolationType > 0) {
@@ -109,9 +115,7 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp {
return dst;
}
finally {
if (g2d != null) {
g2d.dispose();
}
g2d.dispose();
}
}
}
@@ -45,8 +45,8 @@ import java.awt.image.BufferedImage;
*/
public class BufferedImageIcon implements Icon {
private final BufferedImage image;
private int width;
private int height;
private final int width;
private final int height;
private final boolean fast;
public BufferedImageIcon(BufferedImage pImage) {
@@ -81,11 +81,10 @@ public class BufferedImageIcon implements Icon {
else {
//System.out.println("Scaling using interpolation");
Graphics2D g2 = (Graphics2D) g;
AffineTransform xform = AffineTransform.getTranslateInstance(x, y);
xform.scale(width / (double) image.getWidth(), height / (double) image.getHeight());
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2.drawImage(image, xform, null);
AffineTransform transform = AffineTransform.getTranslateInstance(x, y);
transform.scale(width / (double) image.getWidth(), height / (double) image.getHeight());
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2.drawImage(image, transform, null);
}
}
}
@@ -587,6 +587,7 @@ class IndexImage {
* @deprecated Use {@link #getIndexColorModel(Image,int,int)} instead!
* This version will be removed in a later version of the API.
*/
@Deprecated
public static IndexColorModel getIndexColorModel(Image pImage, int pNumberOfColors, boolean pFast) {
return getIndexColorModel(pImage, pNumberOfColors, pFast ? COLOR_SELECTION_FAST : COLOR_SELECTION_QUALITY);
}
@@ -30,17 +30,26 @@
package com.twelvemonkeys.image;
import org.junit.Test;
import static java.lang.Math.min;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import javax.imageio.ImageTypeSpecifier;
import java.awt.color.ColorSpace;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.image.*;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.DataBuffer;
import java.awt.image.ImagingOpException;
import java.awt.image.Raster;
import java.awt.image.RasterOp;
import java.util.Arrays;
import java.util.List;
import static org.junit.Assert.*;
import javax.imageio.ImageTypeSpecifier;
import org.junit.Test;
/**
* AffineTransformOpTest.
@@ -101,6 +110,7 @@ public class AffineTransformOpTest {
private final int width = 30;
private final int height = 20;
private final double anchor = min(width, height) / 2.0;
@Test
public void testGetPoint2D() {
@@ -128,8 +138,8 @@ public class AffineTransformOpTest {
@Test
public void testFilterRotateBIStandard() {
BufferedImageOp jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, Math.min(width, height) / 2, Math.min(width, height) / 2), null);
BufferedImageOp tmOp = new com.twelvemonkeys.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, Math.min(width, height) / 2, Math.min(width, height) / 2), null);
BufferedImageOp jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), null);
BufferedImageOp tmOp = new com.twelvemonkeys.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), null);
for (Integer type : TYPES) {
BufferedImage image = new BufferedImage(width, height, type);
@@ -147,8 +157,8 @@ public class AffineTransformOpTest {
@Test
public void testFilterRotateBICustom() {
BufferedImageOp jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, Math.min(width, height) / 2, Math.min(width, height) / 2), null);
BufferedImageOp tmOp = new com.twelvemonkeys.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, Math.min(width, height) / 2, Math.min(width, height) / 2), null);
BufferedImageOp jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), null);
BufferedImageOp tmOp = new com.twelvemonkeys.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), null);
for (ImageTypeSpecifier spec : SPECS) {
BufferedImage image = spec.createBufferedImage(width, height);
@@ -197,8 +207,8 @@ public class AffineTransformOpTest {
@Test
public void testFilterRotateRasterStandard() {
RasterOp jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, Math.min(width, height) / 2, Math.min(width, height) / 2), null);
RasterOp tmOp = new com.twelvemonkeys.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, Math.min(width, height) / 2, Math.min(width, height) / 2), null);
RasterOp jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), null);
RasterOp tmOp = new com.twelvemonkeys.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), null);
for (Integer type : TYPES) {
Raster raster = new BufferedImage(width, height, type).getRaster();
@@ -221,8 +231,6 @@ public class AffineTransformOpTest {
fail("No result!");
}
else {
System.err.println("AffineTransformOpTest.testFilterRotateRasterStandard");
System.err.println("type: " + type);
continue;
}
}
@@ -240,8 +248,8 @@ public class AffineTransformOpTest {
@Test
public void testFilterRotateRasterCustom() {
RasterOp jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, Math.min(width, height) / 2, Math.min(width, height) / 2), null);
RasterOp tmOp = new com.twelvemonkeys.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, Math.min(width, height) / 2, Math.min(width, height) / 2), null);
RasterOp jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), null);
RasterOp tmOp = new com.twelvemonkeys.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), null);
for (ImageTypeSpecifier spec : SPECS) {
Raster raster = spec.createBufferedImage(width, height).getRaster();
@@ -264,8 +272,6 @@ public class AffineTransformOpTest {
fail("No result!");
}
else {
System.err.println("AffineTransformOpTest.testFilterRotateRasterCustom");
System.err.println("spec: " + spec);
continue;
}
}
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.common</groupId>
<artifactId>common</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<artifactId>common-io</artifactId>
<packaging>jar</packaging>
@@ -65,6 +65,7 @@ import java.io.FilenameFilter;
* @see WildcardStringParser
* @deprecated
*/
@Deprecated
public class FilenameMaskFilter implements FilenameFilter {
// TODO: Rewrite to use regexp, or create new class
@@ -442,6 +442,7 @@ public class LittleEndianDataInputStream extends FilterInputStream implements Da
* @see java.io.BufferedReader#readLine()
* @see java.io.DataInputStream#readLine()
*/
@Deprecated
public String readLine() throws IOException {
DataInputStream ds = new DataInputStream(in);
return ds.readLine();
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.common</groupId>
<artifactId>common</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<artifactId>common-lang</artifactId>
<packaging>jar</packaging>
@@ -770,6 +770,7 @@ public final class StringUtil {
*/
/*public*/
@Deprecated
static String formatNumber(long pNum, int pLen) throws IllegalArgumentException {
StringBuilder result = new StringBuilder();
@@ -1464,6 +1465,7 @@ public final class StringUtil {
*/
/*public*/
@Deprecated
static String removeSubstring(final String pSource, final char pBeginBoundaryChar, final char pEndBoundaryChar, final int pOffset) {
StringBuilder filteredString = new StringBuilder();
boolean insideDemarcatedArea = false;
@@ -157,6 +157,7 @@ public class Time {
* @see #parseTime(String)
* @deprecated
*/
@Deprecated
public String toString(String pFormatStr) {
TimeFormat tf = new TimeFormat(pFormatStr);
@@ -174,6 +175,7 @@ public class Time {
* @see #toString(String)
* @deprecated
*/
@Deprecated
public static Time parseTime(String pStr) {
TimeFormat tf = TimeFormat.getInstance();
@@ -111,6 +111,7 @@ import java.io.PrintStream;
* @author <a href="mailto:eirik.torske@iconmedialab.no">Eirik Torske</a>
* @deprecated Will probably be removed in the near future
*/
@Deprecated
public class WildcardStringParser {
// TODO: Get rid of this class
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys</groupId>
<artifactId>twelvemonkeys</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<groupId>com.twelvemonkeys.common</groupId>
<artifactId>common</artifactId>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys</groupId>
<artifactId>twelvemonkeys</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<groupId>com.twelvemonkeys.contrib</groupId>
<artifactId>contrib</artifactId>
@@ -31,8 +31,9 @@
package com.twelvemonkeys.contrib.tiff;
import com.twelvemonkeys.contrib.tiff.TIFFUtilities.TIFFExtension;
import com.twelvemonkeys.imageio.plugins.tiff.TIFFMedataFormat;
import com.twelvemonkeys.imageio.plugins.tiff.TIFFImageMetadataFormat;
import com.twelvemonkeys.io.FileUtil;
import org.junit.Assert;
import org.junit.Test;
import org.w3c.dom.Node;
@@ -154,7 +155,7 @@ public class TIFFUtilitiesTest {
reader.setInput(checkTest1);
for (int i = 0; i < 3; i++) {
Node metaData = reader.getImageMetadata(i)
.getAsTree(TIFFMedataFormat.SUN_NATIVE_IMAGE_METADATA_FORMAT_NAME);
.getAsTree(TIFFImageMetadataFormat.SUN_NATIVE_IMAGE_METADATA_FORMAT_NAME);
short orientation = ((Number) expression.evaluate(metaData, XPathConstants.NUMBER)).shortValue();
Assert.assertEquals(orientation, TIFFExtension.ORIENTATION_RIGHTTOP);
}
@@ -171,7 +172,7 @@ public class TIFFUtilitiesTest {
reader.setInput(checkTest2);
for (int i = 0; i < 3; i++) {
Node metaData = reader.getImageMetadata(i)
.getAsTree(TIFFMedataFormat.SUN_NATIVE_IMAGE_METADATA_FORMAT_NAME);
.getAsTree(TIFFImageMetadataFormat.SUN_NATIVE_IMAGE_METADATA_FORMAT_NAME);
short orientation = ((Number) expression.evaluate(metaData, XPathConstants.NUMBER)).shortValue();
Assert.assertEquals(orientation, i == 1
? TIFFExtension.ORIENTATION_BOTRIGHT
+2 -9
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<artifactId>imageio-batik</artifactId>
<name>TwelveMonkeys :: ImageIO :: Batik Plugin</name>
@@ -68,13 +68,6 @@
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>xmlgraphics-commons</artifactId>
<version>2.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-anim</artifactId>
@@ -98,7 +91,7 @@
<!--
There seems to be some weirdness in the
Batik/FOP poms (Batik depends on FOP 0.20-5) that screws things up,
making everything end up depending on Batik 1.5, not 1.6
making everything end up depending on Batik 1.5, not the specified version
-->
<exclusions>
<exclusion>
@@ -30,37 +30,8 @@
package com.twelvemonkeys.imageio.plugins.svg;
import com.twelvemonkeys.image.ImageUtil;
import com.twelvemonkeys.imageio.ImageReaderBase;
import com.twelvemonkeys.imageio.util.IIOUtil;
import com.twelvemonkeys.lang.StringUtil;
import org.apache.batik.anim.dom.SVGDOMImplementation;
import org.apache.batik.anim.dom.SVGOMDocument;
import org.apache.batik.bridge.*;
import org.apache.batik.css.parser.CSSLexicalUnit;
import org.apache.batik.dom.util.DOMUtilities;
import org.apache.batik.ext.awt.image.GraphicsUtil;
import org.apache.batik.gvt.CanvasGraphicsNode;
import org.apache.batik.gvt.GraphicsNode;
import org.apache.batik.gvt.renderer.ConcreteImageRendererFactory;
import org.apache.batik.gvt.renderer.ImageRenderer;
import org.apache.batik.gvt.renderer.ImageRendererFactory;
import org.apache.batik.transcoder.*;
import org.apache.batik.transcoder.image.ImageTranscoder;
import org.apache.batik.util.ParsedURL;
import org.apache.batik.util.SVGConstants;
import org.apache.batik.xml.LexicalUnits;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.svg.SVGSVGElement;
import javax.imageio.IIOException;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.spi.ImageReaderSpi;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.Dimension2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
@@ -70,6 +41,38 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import javax.imageio.IIOException;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.spi.ImageReaderSpi;
import org.apache.batik.anim.dom.SVGDOMImplementation;
import org.apache.batik.anim.dom.SVGOMDocument;
import org.apache.batik.bridge.*;
import org.apache.batik.dom.util.DOMUtilities;
import org.apache.batik.ext.awt.image.GraphicsUtil;
import org.apache.batik.gvt.CanvasGraphicsNode;
import org.apache.batik.gvt.GraphicsNode;
import org.apache.batik.gvt.renderer.ConcreteImageRendererFactory;
import org.apache.batik.gvt.renderer.ImageRenderer;
import org.apache.batik.gvt.renderer.ImageRendererFactory;
import org.apache.batik.transcoder.SVGAbstractTranscoder;
import org.apache.batik.transcoder.TranscoderException;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.TranscodingHints;
import org.apache.batik.transcoder.image.ImageTranscoder;
import org.apache.batik.util.ParsedURL;
import org.apache.batik.util.SVGConstants;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.svg.SVGSVGElement;
import com.twelvemonkeys.image.ImageUtil;
import com.twelvemonkeys.imageio.ImageReaderBase;
import com.twelvemonkeys.imageio.util.IIOUtil;
import com.twelvemonkeys.lang.StringUtil;
/**
* Image reader for SVG document fragments.
*
@@ -132,6 +135,7 @@ public class SVGImageReader extends ImageReaderBase {
// Set ImageReadParams as hints
// Note: The cast to Map invokes a different method that preserves
// unset defaults, DO NOT REMOVE!
//noinspection rawtypes
rasterizer.setTranscodingHints((Map) paramsToHints(svgParam));
}
@@ -260,7 +264,7 @@ public class SVGImageReader extends ImageReaderBase {
}
}
public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IOException {
public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) {
return Collections.singleton(ImageTypeSpecifier.createFromRenderedImage(rasterizer.createImage(1, 1))).iterator();
}
@@ -289,7 +293,7 @@ public class SVGImageReader extends ImageReaderBase {
}
// This is cheating... We don't fully transcode after all
protected void transcode(Document document, final String uri, final TranscoderOutput output) throws TranscoderException {
protected void transcode(Document document, final String uri, final TranscoderOutput output) {
// Sets up root, curTxf & curAoi
// ----
if (document != null) {
@@ -584,9 +588,7 @@ public class SVGImageReader extends ImageReaderBase {
return dest;
}
catch (Exception ex) {
TranscoderException exception = new TranscoderException(ex.getMessage());
exception.initCause(ex);
throw exception;
throw new TranscoderException(ex.getMessage(), ex);
}
finally {
if (context != null) {
@@ -54,8 +54,6 @@ import java.util.Collections;
import java.util.List;
import static org.junit.Assert.*;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.*;
/**
@@ -225,7 +223,7 @@ public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader>
assertEquals(500, image.getHeight());
// CSS and embedded resources all go!
verifyZeroInteractions(listener);
verifyNoInteractions(listener);
}
finally {
reader.dispose();
@@ -266,7 +264,7 @@ public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader>
assertEquals(500, image.getHeight());
// No more warnings now that the base URI is set
verifyZeroInteractions(listener);
verifyNoInteractions(listener);
}
finally {
reader.dispose();
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<artifactId>imageio-bmp</artifactId>
<name>TwelveMonkeys :: ImageIO :: BMP plugin</name>
@@ -30,6 +30,22 @@
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.color.ColorSpace;
import java.awt.image.*;
@@ -40,27 +56,6 @@ import java.nio.ByteOrder;
import java.util.Collections;
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.
*
@@ -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,
final byte[] rowDataByte, final WritableRaster destChannel, final Raster srcChannel, final int y) throws IOException {
// Flip into position?
int srcY = !header.topDown ? height - 1 - y : y;
int dstY = (srcY - srcRegion.y) / ySub;
// If subsampled or outside source region, skip entire row
if (y % ySub != 0 || height - 1 - y < srcRegion.y || height - 1 - y >= srcRegion.y + srcRegion.height) {
if (srcY % ySub != 0 || srcY < srcRegion.y || srcY >= srcRegion.y + srcRegion.height) {
input.skipBytes(rowDataByte.length);
return;
@@ -498,19 +497,17 @@ public final class BMPImageReader extends ImageReaderBase {
}
}
if (header.topDown) {
destChannel.setDataElements(0, y, srcChannel);
} else {
// Flip into position
int dstY = (height - 1 - y - srcRegion.y) / ySub;
destChannel.setDataElements(0, dstY, srcChannel);
}
destChannel.setDataElements(0, dstY, srcChannel);
}
private void readRowUShort(final DataInput input, final int height, final Rectangle srcRegion, final int xSub, final int ySub,
final short[] rowDataUShort, final WritableRaster destChannel, final Raster srcChannel, final int y) throws IOException {
// Flip into position?
int srcY = !header.topDown ? height - 1 - y : y;
int dstY = (srcY - srcRegion.y) / ySub;
// If subsampled or outside source region, skip entire row
if (y % ySub != 0 || height - 1 - y < srcRegion.y || height - 1 - y >= srcRegion.y + srcRegion.height) {
if (srcY % ySub != 0 || srcY < srcRegion.y || srcY >= srcRegion.y + srcRegion.height) {
input.skipBytes(rowDataUShort.length * 2 + (rowDataUShort.length % 2) * 2);
return;
@@ -530,19 +527,17 @@ public final class BMPImageReader extends ImageReaderBase {
}
}
if (header.topDown) {
destChannel.setDataElements(0, y, srcChannel);
} else {
// Flip into position
int dstY = (height - 1 - y - srcRegion.y) / ySub;
destChannel.setDataElements(0, dstY, srcChannel);
}
destChannel.setDataElements(0, dstY, srcChannel);
}
private void readRowInt(final DataInput input, final int height, final Rectangle srcRegion, final int xSub, final int ySub,
final int[] rowDataInt, final WritableRaster destChannel, final Raster srcChannel, final int y) throws IOException {
// Flip into position?
int srcY = !header.topDown ? height - 1 - y : y;
int dstY = (srcY - srcRegion.y) / ySub;
// If subsampled or outside source region, skip entire row
if (y % ySub != 0 || height - 1 - y < srcRegion.y || height - 1 - y >= srcRegion.y + srcRegion.height) {
if (srcY % ySub != 0 || srcY < srcRegion.y || srcY >= srcRegion.y + srcRegion.height) {
input.skipBytes(rowDataInt.length * 4);
return;
@@ -557,13 +552,7 @@ public final class BMPImageReader extends ImageReaderBase {
}
}
if (header.topDown) {
destChannel.setDataElements(0, y, srcChannel);
} else {
// Flip into position
int dstY = (height - 1 - y - srcRegion.y) / ySub;
destChannel.setDataElements(0, dstY, srcChannel);
}
destChannel.setDataElements(0, dstY, srcChannel);
}
// TODO: Candidate util method
@@ -32,8 +32,8 @@ package com.twelvemonkeys.imageio.plugins.bmp;
import static org.junit.Assert.*;
import static org.junit.Assume.assumeNoException;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
@@ -295,7 +295,7 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
// At least imageStarted and imageComplete, plus any number of imageProgress
InOrder ordered = inOrder(listener);
ordered.verify(listener).imageStarted(reader, 0);
ordered.verify(listener, atLeastOnce()).imageProgress(eq(reader), anyInt());
ordered.verify(listener, atLeastOnce()).imageProgress(eq(reader), anyFloat());
ordered.verify(listener).imageComplete(reader);
}
@@ -318,7 +318,7 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
// At least imageStarted and imageComplete, plus any number of imageProgress
InOrder ordered = inOrder(listener);
ordered.verify(listener).imageStarted(reader, 0);
ordered.verify(listener, atLeastOnce()).imageProgress(eq(reader), anyInt());
ordered.verify(listener, atLeastOnce()).imageProgress(eq(reader), anyFloat());
ordered.verify(listener).imageComplete(reader);
}
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<artifactId>imageio-clippath</artifactId>
<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
public final class AdobePathBuilder {
private final AdobePathReader delegate;
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<artifactId>imageio-core</artifactId>
<name>TwelveMonkeys :: ImageIO :: Core</name>
@@ -239,6 +239,7 @@ public final class ColorSpaces {
* @return {@code true} if {@code profile} is equal to the default sRGB profile.
* @throws IllegalArgumentException if {@code profile} is {@code null}
*
* @see java.awt.color.ColorSpace#CS_sRGB
* @see java.awt.color.ColorSpace#isCS_sRGB()
*/
public static boolean isCS_sRGB(final ICC_Profile profile) {
@@ -247,6 +248,21 @@ public final class ColorSpaces {
return profile.getColorSpaceType() == ColorSpace.TYPE_RGB && Arrays.equals(getProfileHeaderWithProfileId(profile), sRGB.header);
}
/**
* Tests whether an ICC color profile is equal to the default GRAY profile.
*
* @param profile the ICC profile to test. May not be {@code null}.
* @return {@code true} if {@code profile} is equal to the default GRAY profile.
* @throws IllegalArgumentException if {@code profile} is {@code null}
*
* @see java.awt.color.ColorSpace#CS_GRAY
*/
public static boolean isCS_GRAY(final ICC_Profile profile) {
Validate.notNull(profile, "profile");
return profile.getColorSpaceType() == ColorSpace.TYPE_GRAY && Arrays.equals(getProfileHeaderWithProfileId(profile), GRAY.header);
}
/**
* Tests whether an ICC color profile is known to cause problems for {@link java.awt.image.ColorConvertOp}.
* <p>
@@ -45,36 +45,82 @@ public final class YCbCrConverter {
private final static int CENTERJSAMPLE = 128;
private final static int ONE_HALF = 1 << (SCALEBITS - 1);
private final static int[] Cr_R_LUT = new int[MAXJSAMPLE + 1];
private final static int[] Cb_B_LUT = new int[MAXJSAMPLE + 1];
private final static int[] Cr_G_LUT = new int[MAXJSAMPLE + 1];
private final static int[] Cb_G_LUT = new int[MAXJSAMPLE + 1];
private final static class JPEG {
private final static int[] Cr_R_LUT = new int[MAXJSAMPLE + 1];
private final static int[] Cb_B_LUT = new int[MAXJSAMPLE + 1];
private final static int[] Cr_G_LUT = new int[MAXJSAMPLE + 1];
private final static int[] Cb_G_LUT = new int[MAXJSAMPLE + 1];
/**
* Initializes tables for YCC->RGB color space conversion.
*/
private static void buildYCCtoRGBtable() {
if (ColorSpaces.DEBUG) {
System.err.println("Building YCC conversion table");
/**
* Initializes tables for YCC->RGB color space conversion.
*/
private static void buildYCCtoRGBtable() {
if (ColorSpaces.DEBUG) {
System.err.println("Building JPEG YCbCr conversion table");
}
for (int i = 0, x = -CENTERJSAMPLE; i <= MAXJSAMPLE; i++, x++) {
// i is the actual input pixel value, in the range 0..MAXJSAMPLE
// The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE
// Cr=>R value is nearest int to 1.40200 * x
Cr_R_LUT[i] = (int) ((1.40200 * (1 << SCALEBITS) + 0.5) * x + ONE_HALF) >> SCALEBITS;
// Cb=>B value is nearest int to 1.77200 * x
Cb_B_LUT[i] = (int) ((1.77200 * (1 << SCALEBITS) + 0.5) * x + ONE_HALF) >> SCALEBITS;
// Cr=>G value is scaled-up -0.71414 * x
Cr_G_LUT[i] = -(int) (0.71414 * (1 << SCALEBITS) + 0.5) * x;
// Cb=>G value is scaled-up -0.34414 * x
// We also add in ONE_HALF so that need not do it in inner loop
Cb_G_LUT[i] = -(int) ((0.34414) * (1 << SCALEBITS) + 0.5) * x + ONE_HALF;
}
}
for (int i = 0, x = -CENTERJSAMPLE; i <= MAXJSAMPLE; i++, x++) {
// i is the actual input pixel value, in the range 0..MAXJSAMPLE
// The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE
// Cr=>R value is nearest int to 1.40200 * x
Cr_R_LUT[i] = (int) ((1.40200 * (1 << SCALEBITS) + 0.5) * x + ONE_HALF) >> SCALEBITS;
// Cb=>B value is nearest int to 1.77200 * x
Cb_B_LUT[i] = (int) ((1.77200 * (1 << SCALEBITS) + 0.5) * x + ONE_HALF) >> SCALEBITS;
// Cr=>G value is scaled-up -0.71414 * x
Cr_G_LUT[i] = -(int) (0.71414 * (1 << SCALEBITS) + 0.5) * x;
// Cb=>G value is scaled-up -0.34414 * x
// We also add in ONE_HALF so that need not do it in inner loop
Cb_G_LUT[i] = -(int) ((0.34414) * (1 << SCALEBITS) + 0.5) * x + ONE_HALF;
static {
buildYCCtoRGBtable();
}
}
static {
buildYCCtoRGBtable();
private final static class ITU_R_601 {
private final static int[] Cr_R_LUT = new int[MAXJSAMPLE + 1];
private final static int[] Cb_B_LUT = new int[MAXJSAMPLE + 1];
private final static int[] Cr_G_LUT = new int[MAXJSAMPLE + 1];
private final static int[] Cb_G_LUT = new int[MAXJSAMPLE + 1];
private final static int[] Y_LUT = new int[MAXJSAMPLE + 1];
/**
* Initializes tables for YCC->RGB color space conversion.
*/
private static void buildYCCtoRGBtable() {
if (ColorSpaces.DEBUG) {
System.err.println("Building ITU-R REC.601 YCbCr conversion table");
}
for (int i = 0, x = -CENTERJSAMPLE; i <= MAXJSAMPLE; i++, x++) {
// i is the actual input pixel value, in the range 0..MAXJSAMPLE
// The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE
// Y'CbCr to RGB conversion, using values from BT.601 specification:
// R = 1.16438 * (Y'-16) + 1.59603 * (Cr-128)
// G = 1.16438 * (Y'-16) - 0.39176 * (Cb-128) - 0.81297 * (Cr-128)
// B = 1.16438 * (Y'-16) + 2.01723 * (Cb-128)
// Cr=>R value is nearest int to 1.59603 * x
Cr_R_LUT[i] = ((int) (1.59603 * (1 << SCALEBITS) + 0.5) * x + ONE_HALF) >> SCALEBITS;
// Cb=>B value is nearest int to 2.01723 * x
Cb_B_LUT[i] = ((int) (2.01723 * (1 << SCALEBITS) + 0.5) * x + ONE_HALF) >> SCALEBITS;
// Cr=>G value is scaled-up -0.81297 * x
Cr_G_LUT[i] = -(int) (0.81297 * (1 << SCALEBITS) + 0.5) * x;
// Cb=>G value is scaled-up -0.39176 * x
// We also add in ONE_HALF so that need not do it in inner loop
Cb_G_LUT[i] = -(int) ((0.39176) * (1 << SCALEBITS) + 0.5) * x + ONE_HALF;
// Y`=>RGB
Y_LUT[i] = ((int) (1.16438 * (1 << SCALEBITS) + 0.5) * (i - 16) + ONE_HALF) >> SCALEBITS;
}
}
static {
buildYCCtoRGBtable();
}
}
public static void convertYCbCr2RGB(final byte[] yCbCr, final byte[] rgb, final double[] coefficients, double[] referenceBW, final int offset) {
@@ -108,17 +154,27 @@ public final class YCbCrConverter {
rgb[offset + 1] = clamp(green);
}
public static void convertYCbCr2RGB(final byte[] yCbCr, final byte[] rgb, final int offset) {
int y = yCbCr[offset] & 0xff;
int cr = yCbCr[offset + 2] & 0xff;
public static void convertJPEGYCbCr2RGB(final byte[] yCbCr, final byte[] rgb, final int offset) {
int y = yCbCr[offset ] & 0xff;
int cb = yCbCr[offset + 1] & 0xff;
int cr = yCbCr[offset + 2] & 0xff;
rgb[offset] = clamp(y + Cr_R_LUT[cr]);
rgb[offset + 1] = clamp(y + (Cb_G_LUT[cb] + Cr_G_LUT[cr] >> SCALEBITS));
rgb[offset + 2] = clamp(y + Cb_B_LUT[cb]);
rgb[offset ] = clamp(y + JPEG.Cr_R_LUT[cr]);
rgb[offset + 1] = clamp(y + (JPEG.Cb_G_LUT[cb] + JPEG.Cr_G_LUT[cr] >> SCALEBITS));
rgb[offset + 2] = clamp(y + JPEG.Cb_B_LUT[cb]);
}
private static byte clamp(int val) {
public static void convertRec601YCbCr2RGB(final byte[] yCbCr, final byte[] rgb, final int offset) {
int y = yCbCr[offset ] & 0xff;
int cb = yCbCr[offset + 1] & 0xff;
int cr = yCbCr[offset + 2] & 0xff;
rgb[offset ] = clamp(ITU_R_601.Y_LUT[y] + ITU_R_601.Cr_R_LUT[cr]);
rgb[offset + 1] = clamp(ITU_R_601.Y_LUT[y] + (ITU_R_601.Cr_G_LUT[cr] + ITU_R_601.Cb_G_LUT[cb] >> SCALEBITS));
rgb[offset + 2] = clamp(ITU_R_601.Y_LUT[y] + ITU_R_601.Cb_B_LUT[cb]);
}
private static byte clamp(final int val) {
return (byte) Math.max(0, Math.min(255, val));
}
}
@@ -93,8 +93,8 @@ public final class BufferedFileImageInputStream extends ImageInputStreamImpl {
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
private boolean fillBuffer() throws IOException {
bufferPos = 0;
int length = raf.read(buffer, 0, buffer.length);
bufferPos = 0;
bufferLimit = max(length, 0);
return bufferLimit > 0;
@@ -125,27 +125,35 @@ public final class BufferedFileImageInputStream extends ImageInputStreamImpl {
}
@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();
bitOffset = 0;
if (bufferEmpty()) {
// Bypass buffer if buffer is empty for reads longer than buffer
if (pLength >= buffer.length) {
return readDirect(pBuffer, pOffset, pLength);
if (length >= buffer.length) {
return readDirect(bytes, offset, length);
}
else if (!fillBuffer()) {
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.
bufferLimit = 0;
int read = raf.read(pBuffer, pOffset, pLength);
int read = raf.read(bytes, offset, length);
if (read > 0) {
streamPos += read;
@@ -154,17 +162,17 @@ public final class BufferedFileImageInputStream extends ImageInputStreamImpl {
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
int length = Math.min(bufferLimit - bufferPos, pLength);
int available = Math.min(bufferLimit - bufferPos, length);
if (length > 0) {
System.arraycopy(buffer, bufferPos, pBuffer, pOffset, length);
bufferPos += length;
streamPos += length;
if (available > 0) {
System.arraycopy(buffer, bufferPos, bytes, offset, available);
bufferPos += available;
streamPos += available;
}
return length;
return available;
}
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
long newBufferPos = bufferPos + position - streamPos;
if (newBufferPos >= 0 && newBufferPos <= bufferLimit) {
if (newBufferPos >= 0 && newBufferPos < bufferLimit) {
bufferPos = (int) newBufferPos;
}
else {
@@ -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;
}
}
@@ -184,6 +184,24 @@ public class ColorSpacesTest {
ColorSpaces.isCS_sRGB(null);
}
@Test
public void testIsCS_GRAYTrue() {
assertTrue(ColorSpaces.isCS_GRAY(ICC_Profile.getInstance(ColorSpace.CS_GRAY)));
}
@Test
public void testIsCS_GRAYFalse() {
assertFalse(ColorSpaces.isCS_GRAY(ICC_Profile.getInstance(ColorSpace.CS_sRGB)));
assertFalse(ColorSpaces.isCS_GRAY(ICC_Profile.getInstance(ColorSpace.CS_LINEAR_RGB)));
assertFalse(ColorSpaces.isCS_GRAY(ICC_Profile.getInstance(ColorSpace.CS_CIEXYZ)));
assertFalse(ColorSpaces.isCS_GRAY(ICC_Profile.getInstance(ColorSpace.CS_PYCC)));
}
@Test(expected = IllegalArgumentException.class)
public void testIsCS_GRAYNull() {
ColorSpaces.isCS_GRAY(null);
}
@Test
public void testEqualHeadersDifferentProfile() throws IOException {
// These profiles are extracted from various JPEGs, and have the exact same profile header...
@@ -39,8 +39,6 @@ import java.io.IOException;
import java.util.Arrays;
import static org.junit.Assert.assertArrayEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.*;
public class KCMSSanitizerStrategyTest {
@@ -67,8 +67,9 @@ public class BufferedFileImageInputStreamTest {
@Test
public void testCreate() throws IOException {
BufferedFileImageInputStream stream = new BufferedFileImageInputStream(File.createTempFile("empty", ".tmp"));
assertEquals("Data length should be same as stream length", 0, stream.length());
try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(File.createTempFile("empty", ".tmp"))) {
assertEquals("Data length should be same as stream length", 0, stream.length());
}
}
@Test
@@ -104,12 +105,12 @@ public class BufferedFileImageInputStreamTest {
byte[] data = new byte[1024 * 1024];
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];
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);
assertTrue("Wrong data read: " + i, rangeEquals(data, i * result.length, result, 0, result.length));
for (int i = 0; i < data.length / result.length; i++) {
stream.readFully(result);
assertTrue("Wrong data read: " + i, rangeEquals(data, i * result.length, result, 0, result.length));
}
}
}
@@ -135,16 +136,16 @@ public class BufferedFileImageInputStreamTest {
byte[] data = new byte[1024 * 14];
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);
stream.skipBytes(result.length);
assertTrue("Wrong data read: " + i, rangeEquals(data, i * result.length, result, 0, result.length));
for (int i = 0; i < data.length / result.length; i += 2) {
stream.readFully(result);
stream.skipBytes(result.length);
assertTrue("Wrong data read: " + i, rangeEquals(data, i * result.length, result, 0, result.length));
}
}
}
@@ -153,19 +154,35 @@ public class BufferedFileImageInputStreamTest {
byte[] data = new byte[1024 * 18];
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++) {
// Read backwards
long newPos = stream.length() - result.length - i * result.length;
stream.seek(newPos);
assertEquals("Wrong stream position", newPos, stream.getStreamPosition());
stream.readFully(result);
assertTrue("Wrong data read: " + i, rangeEquals(data, (int) newPos, result, 0, result.length));
@Test
public void testReadOutsideDataSeek0Read() throws IOException {
byte[] data = new byte[256];
File file = randomDataToFile(data);
try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file)) {
assertEquals("File length should be same as stream length", file.length(), stream.length());
byte[] buffer = new byte[data.length * 2];
stream.read(buffer);
stream.seek(0);
assertNotEquals(-1, stream.read());
assertNotEquals(-1, stream.read(buffer));
}
}
@@ -176,10 +193,10 @@ public class BufferedFileImageInputStreamTest {
long value = ByteBuffer.wrap(bytes).getLong();
// Create stream
ImageInputStream stream = new BufferedFileImageInputStream(file);
for (int i = 1; i <= 64; i++) {
assertEquals(String.format("bit %d differ", i), (value << (i - 1L)) >>> 63L, stream.readBit());
try (ImageInputStream stream = new BufferedFileImageInputStream(file)) {
for (int i = 1; i <= 64; i++) {
assertEquals(String.format("bit %d differ", i), (value << (i - 1L)) >>> 63L, stream.readBit());
}
}
}
@@ -190,12 +207,12 @@ public class BufferedFileImageInputStreamTest {
long value = ByteBuffer.wrap(bytes).getLong();
// Create stream
ImageInputStream stream = new BufferedFileImageInputStream(file);
for (int i = 1; i <= 64; i++) {
stream.seek(0);
assertEquals(String.format("bit %d differ", i), value >>> (64L - i), stream.readBits(i));
assertEquals(i % 8, stream.getBitOffset());
try (ImageInputStream stream = new BufferedFileImageInputStream(file)) {
for (int i = 1; i <= 64; i++) {
stream.seek(0);
assertEquals(String.format("bit %d differ", i), value >>> (64L - i), stream.readBits(i));
assertEquals(i % 8, stream.getBitOffset());
}
}
}
@@ -206,13 +223,13 @@ public class BufferedFileImageInputStreamTest {
long value = ByteBuffer.wrap(bytes).getLong();
// Create stream
ImageInputStream stream = new BufferedFileImageInputStream(file);
for (int i = 1; i <= 60; i++) {
stream.seek(0);
stream.setBitOffset(i % 8);
assertEquals(String.format("bit %d differ", i), (value << (i % 8)) >>> (64L - i), stream.readBits(i));
assertEquals(i * 2 % 8, stream.getBitOffset());
try (ImageInputStream stream = new BufferedFileImageInputStream(file)) {
for (int i = 1; i <= 60; i++) {
stream.seek(0);
stream.setBitOffset(i % 8);
assertEquals(String.format("bit %d differ", i), (value << (i % 8)) >>> (64L - i), stream.readBits(i));
assertEquals(i * 2 % 8, stream.getBitOffset());
}
}
}
@@ -221,35 +238,37 @@ public class BufferedFileImageInputStreamTest {
byte[] bytes = new byte[8743]; // Slightly more than one buffer size
File file = randomDataToFile(bytes);
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++) {
assertEquals(buffer.getShort(), stream.readShort());
}
try (final ImageInputStream stream = new BufferedFileImageInputStream(file)) {
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
assertThrows(EOFException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
stream.readShort();
for (int i = 0; i < bytes.length / 2; i++) {
assertEquals(buffer.getShort(), stream.readShort());
}
});
stream.seek(0);
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
buffer.position(0);
buffer.order(ByteOrder.LITTLE_ENDIAN);
assertThrows(EOFException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
stream.readShort();
}
});
for (int i = 0; i < bytes.length / 2; i++) {
assertEquals(buffer.getShort(), stream.readShort());
}
stream.seek(0);
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
buffer.position(0);
buffer.order(ByteOrder.LITTLE_ENDIAN);
assertThrows(EOFException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
stream.readShort();
for (int i = 0; i < bytes.length / 2; i++) {
assertEquals(buffer.getShort(), stream.readShort());
}
});
assertThrows(EOFException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
stream.readShort();
}
});
}
}
@Test
@@ -257,35 +276,37 @@ public class BufferedFileImageInputStreamTest {
byte[] bytes = new byte[8743]; // Slightly more than one buffer size
File file = randomDataToFile(bytes);
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++) {
assertEquals(buffer.getInt(), stream.readInt());
}
try (final ImageInputStream stream = new BufferedFileImageInputStream(file)) {
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
assertThrows(EOFException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
stream.readInt();
for (int i = 0; i < bytes.length / 4; i++) {
assertEquals(buffer.getInt(), stream.readInt());
}
});
stream.seek(0);
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
buffer.position(0);
buffer.order(ByteOrder.LITTLE_ENDIAN);
assertThrows(EOFException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
stream.readInt();
}
});
for (int i = 0; i < bytes.length / 4; i++) {
assertEquals(buffer.getInt(), stream.readInt());
}
stream.seek(0);
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
buffer.position(0);
buffer.order(ByteOrder.LITTLE_ENDIAN);
assertThrows(EOFException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
stream.readInt();
for (int i = 0; i < bytes.length / 4; i++) {
assertEquals(buffer.getInt(), stream.readInt());
}
});
assertThrows(EOFException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
stream.readInt();
}
});
}
}
@Test
@@ -293,35 +314,37 @@ public class BufferedFileImageInputStreamTest {
byte[] bytes = new byte[8743]; // Slightly more than one buffer size
File file = randomDataToFile(bytes);
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++) {
assertEquals(buffer.getLong(), stream.readLong());
}
try (final ImageInputStream stream = new BufferedFileImageInputStream(file)) {
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
assertThrows(EOFException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
stream.readLong();
for (int i = 0; i < bytes.length / 8; i++) {
assertEquals(buffer.getLong(), stream.readLong());
}
});
stream.seek(0);
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
buffer.position(0);
buffer.order(ByteOrder.LITTLE_ENDIAN);
assertThrows(EOFException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
stream.readLong();
}
});
for (int i = 0; i < bytes.length / 8; i++) {
assertEquals(buffer.getLong(), stream.readLong());
}
stream.seek(0);
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
buffer.position(0);
buffer.order(ByteOrder.LITTLE_ENDIAN);
assertThrows(EOFException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
stream.readLong();
for (int i = 0; i < bytes.length / 8; i++) {
assertEquals(buffer.getLong(), stream.readLong());
}
});
assertThrows(EOFException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
stream.readLong();
}
});
}
}
@Test
@@ -329,49 +352,50 @@ public class BufferedFileImageInputStreamTest {
byte[] bytes = new byte[9];
File file = randomDataToFile(bytes);
final ImageInputStream stream = new BufferedFileImageInputStream(file);
stream.seek(1000);
try (final ImageInputStream stream = new BufferedFileImageInputStream(file)) {
stream.seek(1000);
assertEquals(-1, stream.read());
assertEquals(-1, stream.read(new byte[1], 0, 1));
assertEquals(-1, stream.read());
assertEquals(-1, stream.read(new byte[1], 0, 1));
assertThrows(EOFException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
stream.readFully(new byte[1]);
}
});
assertThrows(EOFException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
stream.readByte();
}
});
assertThrows(EOFException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
stream.readShort();
}
});
assertThrows(EOFException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
stream.readInt();
}
});
assertThrows(EOFException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
stream.readLong();
}
});
assertThrows(EOFException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
stream.readFully(new byte[1]);
}
});
assertThrows(EOFException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
stream.readByte();
}
});
assertThrows(EOFException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
stream.readShort();
}
});
assertThrows(EOFException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
stream.readInt();
}
});
assertThrows(EOFException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
stream.readLong();
}
});
stream.seek(0);
for (byte value : bytes) {
assertEquals(value, stream.readByte());
stream.seek(0);
for (byte value : bytes) {
assertEquals(value, stream.readByte());
}
assertEquals(-1, stream.read());
}
assertEquals(-1, stream.read());
}
@Test
@@ -383,4 +407,23 @@ public class BufferedFileImageInputStreamTest {
stream.close();
verify(mock, only()).close();
}
@Test
public void testWorkaroundForWBMPImageReaderExpectsReadToBehaveAsReadFully() throws IOException {
// See #606 for details.
// Bug in JDK WBMPImageReader, uses read(byte[], int, int) instead of readFully(byte[], int, int).
// Ie: Relies on read to return all bytes at once, without blocking
int size = BufferedFileImageInputStream.DEFAULT_BUFFER_SIZE * 7;
byte[] bytes = new byte[size];
File file = randomDataToFile(bytes);
try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file)) {
byte[] result = new byte[size];
int head = stream.read(result, 0, 12); // Provoke a buffered read
int len = stream.read(result, 12, size - 12); // Rest of buffer + direct read
assertEquals(size, len + head);
assertArrayEquals(bytes, result);
}
}
}
@@ -45,9 +45,8 @@ import javax.imageio.spi.IIORegistry;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.awt.image.SampleModel;
import java.awt.geom.AffineTransform;
import java.awt.image.*;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
@@ -57,6 +56,7 @@ import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import static java.lang.Math.min;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
@@ -1151,7 +1151,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
// At least imageStarted and imageComplete, plus any number of imageProgress
InOrder ordered = inOrder(listener);
ordered.verify(listener).imageStarted(reader, 0);
ordered.verify(listener, atLeastOnce()).imageProgress(eq(reader), anyInt());
ordered.verify(listener, atLeastOnce()).imageProgress(eq(reader), anyFloat());
ordered.verify(listener).imageComplete(reader);
reader.dispose();
}
@@ -1184,9 +1184,9 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
ordered.verify(listenerToo).imageStarted(reader, 0);
ordered.verify(listenerThree).imageStarted(reader, 0);
ordered.verify(listener, atLeastOnce()).imageProgress(eq(reader), anyInt());
ordered.verify(listenerToo, atLeastOnce()).imageProgress(eq(reader), anyInt());
ordered.verify(listenerThree, atLeastOnce()).imageProgress(eq(reader), anyInt());
ordered.verify(listener, atLeastOnce()).imageProgress(eq(reader), anyFloat());
ordered.verify(listenerToo, atLeastOnce()).imageProgress(eq(reader), anyFloat());
ordered.verify(listenerThree, atLeastOnce()).imageProgress(eq(reader), anyFloat());
ordered.verify(listener).imageComplete(reader);
ordered.verify(listenerToo).imageComplete(reader);
@@ -1226,7 +1226,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
}
// Should not have called any methods...
verifyZeroInteractions(listener);
verifyNoInteractions(listener);
reader.dispose();
}
@@ -1253,11 +1253,11 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
}
// Should not have called any methods on listener1...
verifyZeroInteractions(listener);
verifyNoInteractions(listener);
InOrder ordered = inOrder(listenerToo);
ordered.verify(listenerToo).imageStarted(reader, 0);
ordered.verify(listenerToo, atLeastOnce()).imageProgress(eq(reader), anyInt());
ordered.verify(listenerToo, atLeastOnce()).imageProgress(eq(reader), anyFloat());
ordered.verify(listenerToo).imageComplete(reader);
reader.dispose();
}
@@ -1281,7 +1281,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
}
// Should not have called any methods...
verifyZeroInteractions(listener);
verifyNoInteractions(listener);
reader.dispose();
}
@@ -1307,8 +1307,8 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
}
// Should not have called any methods...
verifyZeroInteractions(listener);
verifyZeroInteractions(listenerToo);
verifyNoInteractions(listener);
verifyNoInteractions(listenerToo);
reader.dispose();
}
@@ -1333,7 +1333,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
}
};
doAnswer(abort).when(abortingListener).imageStarted(any(ImageReader.class), anyInt());
doAnswer(abort).when(abortingListener).imageProgress(any(ImageReader.class), anyInt());
doAnswer(abort).when(abortingListener).imageProgress(any(ImageReader.class), anyFloat());
reader.addIIOReadProgressListener(abortingListener);
@@ -1738,6 +1738,43 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
reader.dispose();
}
protected List<TestData> getTestDataForAffineTransformOpCompatibility() {
// Allow subclasses to filter out test data that can't be converted to a compatible image without data loss
return getTestData();
}
@Test
public void testAffineTransformOpCompatibility() throws IOException {
// Test that the output of normal images are compatible with AffineTransformOp. Is unlikely to work on all test data
ImageReader reader = createReader();
for (TestData testData : getTestDataForAffineTransformOpCompatibility()) {
try (ImageInputStream input = testData.getInputStream()) {
reader.setInput(input);
ImageReadParam param = reader.getDefaultReadParam();
param.setSourceRegion(new Rectangle(min(reader.getWidth(0), 64), min(reader.getHeight(0), 64)));
BufferedImage originalImage = reader.read(0, param);
AffineTransform transform = AffineTransform.getTranslateInstance(10, 10);
AffineTransformOp op = new AffineTransformOp(transform, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
try {
BufferedImage resultImage = op.filter(originalImage, null); // The exception happens here
assertNotNull(resultImage);
}
catch (ImagingOpException e) {
fail(e.getMessage() + ".\n\t"
+ originalImage + "\n\t"
+ testData);
}
}
}
reader.dispose();
}
@Ignore("TODO: Implement")
@Test
public void testSetDestinationBands() {
@@ -52,7 +52,6 @@ import java.net.URL;
import java.util.List;
import static org.junit.Assert.*;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.*;
/**
@@ -79,7 +78,7 @@ public abstract class ImageWriterAbstractTest<T extends ImageWriter> {
protected abstract ImageWriterSpi createProvider();
protected final T createWriter() throws IOException {
return writerClass.cast(provider.createWriterInstance(null));
return writerClass.cast(provider.createWriterInstance());
}
protected abstract List<? extends RenderedImage> getTestData();
@@ -104,7 +103,7 @@ public abstract class ImageWriterAbstractTest<T extends ImageWriter> {
return image;
}
protected final RenderedImage getTestData(final int index) {
return getTestData().get(index);
}
@@ -219,7 +218,7 @@ public abstract class ImageWriterAbstractTest<T extends ImageWriter> {
// At least imageStarted and imageComplete, plus any number of imageProgress
InOrder ordered = inOrder(listener);
ordered.verify(listener).imageStarted(writer, 0);
ordered.verify(listener, atLeastOnce()).imageProgress(eq(writer), anyInt());
ordered.verify(listener, atLeastOnce()).imageProgress(eq(writer), anyFloat());
ordered.verify(listener).imageComplete(writer);
}
@@ -251,9 +250,9 @@ public abstract class ImageWriterAbstractTest<T extends ImageWriter> {
ordered.verify(listenerToo).imageStarted(writer, 0);
ordered.verify(listenerThree).imageStarted(writer, 0);
ordered.verify(listener, atLeastOnce()).imageProgress(eq(writer), anyInt());
ordered.verify(listenerToo, atLeastOnce()).imageProgress(eq(writer), anyInt());
ordered.verify(listenerThree, atLeastOnce()).imageProgress(eq(writer), anyInt());
ordered.verify(listener, atLeastOnce()).imageProgress(eq(writer), anyFloat());
ordered.verify(listenerToo, atLeastOnce()).imageProgress(eq(writer), anyFloat());
ordered.verify(listenerThree, atLeastOnce()).imageProgress(eq(writer), anyFloat());
ordered.verify(listener).imageComplete(writer);
ordered.verify(listenerToo).imageComplete(writer);
@@ -290,7 +289,7 @@ public abstract class ImageWriterAbstractTest<T extends ImageWriter> {
}
// Should not have called any methods...
verifyZeroInteractions(listener);
verifyNoInteractions(listener);
}
@Test
@@ -315,12 +314,12 @@ public abstract class ImageWriterAbstractTest<T extends ImageWriter> {
}
// Should not have called any methods...
verifyZeroInteractions(listener);
verifyNoInteractions(listener);
// At least imageStarted and imageComplete, plus any number of imageProgress
InOrder ordered = inOrder(listenerToo);
ordered.verify(listenerToo).imageStarted(writer, 0);
ordered.verify(listenerToo, atLeastOnce()).imageProgress(eq(writer), anyInt());
ordered.verify(listenerToo, atLeastOnce()).imageProgress(eq(writer), anyFloat());
ordered.verify(listenerToo).imageComplete(writer);
}
@@ -345,7 +344,7 @@ public abstract class ImageWriterAbstractTest<T extends ImageWriter> {
}
// Should not have called any methods...
verifyZeroInteractions(listener);
verifyNoInteractions(listener);
}
@Test
@@ -371,7 +370,7 @@ public abstract class ImageWriterAbstractTest<T extends ImageWriter> {
}
// Should not have called any methods...
verifyZeroInteractions(listener);
verifyZeroInteractions(listenerToo);
verifyNoInteractions(listener);
verifyNoInteractions(listenerToo);
}
}
@@ -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>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<artifactId>imageio-hdr</artifactId>
<name>TwelveMonkeys :: ImageIO :: HDR plugin</name>
@@ -38,12 +38,14 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static java.util.Collections.emptyList;
/**
* TGAImageReaderTest
* HDRImageReaderTest
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: TGAImageReaderTest.java,v 1.0 03.07.14 22:28 haraldk Exp$
* @version $Id: HDRImageReaderTest.java,v 1.0 03.07.14 22:28 haraldk Exp$
*/
public class HDRImageReaderTest extends ImageReaderAbstractTest<HDRImageReader> {
@Override
@@ -58,6 +60,12 @@ public class HDRImageReaderTest extends ImageReaderAbstractTest<HDRImageReader>
);
}
@Override
protected List<TestData> getTestDataForAffineTransformOpCompatibility() {
// HDR images uses floating point buffers...
return emptyList();
}
@Override
protected List<String> getFormatNames() {
return Arrays.asList("HDR", "hdr", "RGBE", "rgbe");
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<artifactId>imageio-icns</artifactId>
<name>TwelveMonkeys :: ImageIO :: ICNS plugin</name>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<artifactId>imageio-iff</artifactId>
<name>TwelveMonkeys :: ImageIO :: IFF plugin</name>
+16 -3
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<artifactId>imageio-jpeg-jai-interop</artifactId>
<name>TwelveMonkeys :: ImageIO :: JPEG/JAI TIFF Interop</name>
@@ -13,14 +13,27 @@
</description>
<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>
<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.3.0</version>
<version>1.4.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
@@ -78,7 +78,7 @@ public class JAITIFFImageReaderInteroperabilityTest extends ImageReaderAbstractT
@Override
protected List<TestData> getTestData() {
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/jpeg-lossless-8bit-gray.tif"), new Dimension(512, 512)) // Lossless JPEG Gray, 8 bit/sample
);
+14 -2
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<artifactId>imageio-jpeg-jep262-interop</artifactId>
<name>TwelveMonkeys :: ImageIO :: JPEG/JEP-262 Interop</name>
@@ -13,9 +13,21 @@
</description>
<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>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
@@ -33,6 +33,7 @@ package com.twelvemonkeys.imageio.plugins.jpeg.jep262interop;
import com.twelvemonkeys.imageio.plugins.jpeg.JPEGImageReaderSpi;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
import org.junit.AssumptionViolatedException;
import org.junit.Ignore;
import org.junit.Test;
@@ -48,7 +49,6 @@ import java.util.Iterator;
import java.util.List;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
/**
* 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)
assumeTrue("Provider " + JEP_262_PROVIDER_CLASS_NAME + " not found", false);
return null;
throw new AssumptionViolatedException("Provider " + JEP_262_PROVIDER_CLASS_NAME + " not found");
}
@Override
protected List<TestData> getTestData() {
return Arrays.asList(
new TestData(getClassLoaderResource("/tiff/foto_0001.tif"), new Dimension(1663, 2338)), // 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/jpeg-lossless-8bit-gray.tif"), new Dimension(512, 512)) // Lossless JPEG Gray, 8 bit/sample
);
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<artifactId>imageio-jpeg</artifactId>
<name>TwelveMonkeys :: ImageIO :: JPEG plugin</name>
@@ -119,7 +119,7 @@ final class EXIFThumbnail {
case 6:
// YCbCr
for (int i = 0; i < thumbLength; i += 3) {
YCbCrConverter.convertYCbCr2RGB(thumbData, thumbData, i);
YCbCrConverter.convertJPEGYCbCr2RGB(thumbData, thumbData, i);
}
break;
default:
@@ -123,7 +123,7 @@ public final class JPEGImageReader extends ImageReaderBase {
private int currentStreamIndex = 0;
private final List<Long> streamOffsets = new ArrayList<>();
protected JPEGImageReader(final ImageReaderSpi provider, final ImageReader delegate) {
JPEGImageReader(final ImageReaderSpi provider, final ImageReader delegate) {
super(provider);
this.delegate = Validate.notNull(delegate);
@@ -1169,7 +1169,7 @@ public final class JPEGImageReader extends ImageReaderBase {
processThumbnailStarted(imageIndex, thumbnailIndex);
processThumbnailProgress(0f);
BufferedImage thumbnail = thumbnails.get(thumbnailIndex).read();;
BufferedImage thumbnail = thumbnails.get(thumbnailIndex).read();
processThumbnailProgress(100f);
processThumbnailComplete();
@@ -1211,7 +1211,7 @@ public final class JPEGImageReader extends ImageReaderBase {
for (int y = 0; y < height; y++) {
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 +1225,7 @@ public final class JPEGImageReader extends ImageReaderBase {
for (int x = 0; x < width; x++) {
int offset = (x + y * width) * 4;
// YCC -> CMY
YCbCrConverter.convertYCbCr2RGB(data, data, offset);
YCbCrConverter.convertJPEGYCbCr2RGB(data, data, offset);
// Inverse K
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.junit.Test;
import org.mockito.internal.matchers.GreaterThan;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
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.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.greaterThan;
import static org.junit.Assert.*;
import static org.junit.Assume.assumeNoException;
import static org.junit.Assume.assumeNotNull;
import static org.mockito.AdditionalMatchers.and;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
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
IIOMetadataNode markerSequence = (IIOMetadataNode) markerSequences.item(markerSequences.getLength() - 1); // The last will be the "main" image
assertNotNull(markerSequence);
assertThat(markerSequence.getChildNodes().getLength(), new GreaterThan<>(0));
assertThat(markerSequence.getChildNodes().getLength(), greaterThan(0));
NodeList unknowns = markerSequence.getElementsByTagName("unknown");
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 org.junit.Test;
import org.mockito.internal.matchers.LessOrEqual;
import javax.imageio.IIOException;
import javax.imageio.ImageIO;
@@ -48,6 +47,7 @@ import java.net.URL;
import java.util.List;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.lessThanOrEqualTo;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -124,7 +124,7 @@ public class JPEGSegmentImageInputStreamTest {
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...
}
+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>imageio-metadata</artifactId>
@@ -46,6 +46,7 @@ import java.io.IOException;
*
* @deprecated Use com.twelvemonkeys.imageio.metadata.tiff.TIFFReader instead.
*/
@Deprecated
public final class EXIFReader extends MetadataReader {
private final TIFFReader delegate = new TIFFReader();
@@ -48,6 +48,7 @@ import java.util.Collection;
*
* @deprecated Use com.twelvemonkeys.imageio.metadata.tiff.TIFFWriter instead.
*/
@Deprecated
public final class EXIFWriter extends MetadataWriter {
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
public final class Rational extends Number implements Comparable<Rational> {
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
public interface TIFF extends com.twelvemonkeys.imageio.metadata.tiff.TIFF {
}
@@ -30,8 +30,15 @@
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.File;
import java.io.IOException;
@@ -39,15 +46,7 @@ import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import javax.imageio.IIOException;
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;
import static com.twelvemonkeys.imageio.metadata.tiff.TIFFEntry.getValueLength;
/**
* 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_EXIF_IFD, Collections.unmodifiableCollection(Collections.singleton(TIFF.TAG_INTEROP_IFD)));
map.put(TIFF.TAG_SUB_IFD, Collections.singleton(TIFF.TAG_SUB_IFD));
map.put(TIFF.TAG_EXIF_IFD, Collections.singleton(TIFF.TAG_INTEROP_IFD));
return Collections.unmodifiableMap(map);
}
private final Set<Long> parsedIFDs = new TreeSet<>();
private long length;
private boolean longOffsets;
private int offsetSize;
private Set<Long> parsedIFDs = new TreeSet<>();
@Override
public Directory read(final ImageInputStream input) throws IOException {
@@ -40,7 +40,6 @@ 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.*;
@@ -58,9 +57,26 @@ public final class TIFFWriter extends MetadataWriter {
private static final int WORD_LENGTH = 2;
private static final int LONGWORD_LENGTH = 4;
private static final int ENTRY_LENGTH = 12;
public boolean write(final Collection<Entry> entries, final ImageOutputStream stream) throws IOException {
// TODO: We probably want to gloss over client code writing IFDs in BigTIFF (or vice versa) somehow... Silently convert IFD -> IFD8
private final boolean longOffsets;
private final int offsetSize;
private final long entryLength;
private final int directoryCountLength;
public TIFFWriter() {
this(LONGWORD_LENGTH);
}
public TIFFWriter(int offsetSize) {
this.offsetSize = Validate.isTrue(offsetSize == 4 || offsetSize == 8, offsetSize, "offsetSize must be 4 for TIFF or 8 for BigTIFF");
longOffsets = offsetSize == 8;
directoryCountLength = longOffsets ? 8 : WORD_LENGTH;
entryLength = 2 * WORD_LENGTH + 2 * offsetSize;
}
public boolean write(final Collection<? extends Entry> entries, final ImageOutputStream stream) throws IOException {
return write(new IFD(entries), stream);
}
@@ -87,7 +103,7 @@ public final class TIFFWriter extends MetadataWriter {
}
// Offset to next IFD (EOF)
stream.writeInt(0);
writeOffset(stream, 0);
return true;
}
@@ -96,7 +112,12 @@ public final class TIFFWriter extends MetadataWriter {
// Header
ByteOrder byteOrder = stream.getByteOrder();
stream.writeShort(byteOrder == ByteOrder.BIG_ENDIAN ? TIFF.BYTE_ORDER_MARK_BIG_ENDIAN : TIFF.BYTE_ORDER_MARK_LITTLE_ENDIAN);
stream.writeShort(42);
stream.writeShort(longOffsets ? TIFF.BIGTIFF_MAGIC : TIFF.TIFF_MAGIC);
if (longOffsets) {
stream.writeShort(offsetSize); // Always 8 in this case
stream.writeShort(0);
}
}
public long writeIFD(final Collection<Entry> entries, final ImageOutputStream stream) throws IOException {
@@ -118,37 +139,42 @@ public final class TIFFWriter extends MetadataWriter {
long dataSize = computeDataSize(ordered);
// Offset to this IFD
final long ifdOffset = stream.getStreamPosition() + dataSize + LONGWORD_LENGTH;
final long ifdOffset = stream.getStreamPosition() + dataSize + offsetSize;
if (!isSubIFD) {
stream.writeInt(assertIntegerOffset(ifdOffset));
dataOffset += LONGWORD_LENGTH;
writeOffset(stream, ifdOffset);
dataOffset += offsetSize;
// Seek to offset
stream.seek(ifdOffset);
}
else {
dataOffset += WORD_LENGTH + ordered.size() * ENTRY_LENGTH;
dataOffset += directoryCountLength + ordered.size() * entryLength;
}
// Write directory
stream.writeShort(ordered.size());
writeDirectoryCount(stream, ordered.size());
for (Entry entry : ordered) {
// Write tag id
// Write tag id, type & value count
stream.writeShort((Integer) entry.getIdentifier());
// Write tag type
stream.writeShort(getType(entry));
// Write value count
stream.writeInt(getCount(entry));
writeValueCount(stream, getCount(entry));
// Write value
if (entry.getValue() instanceof Directory) {
// TODO: This could possibly be a compound directory, in which case the count should be > 1
stream.writeInt(assertIntegerOffset(dataOffset));
long streamPosition = stream.getStreamPosition();
Object value = entry.getValue();
if (value instanceof Directory) {
if (value instanceof CompoundDirectory) {
// Can't have both nested and linked IFDs
throw new AssertionError("SubIFD cannot contain linked IFDs");
}
// We can't write offset here, we need to write value, as both LONG/IFD and LONG8/IFD8 is allowed
// TODO: Or possibly gloss over, by always writing IFD8 for BigTIFF?
long streamPosition = stream.getStreamPosition() + offsetSize;
writeValueInline(dataOffset, getType(entry), stream);
stream.seek(dataOffset);
Directory subIFD = (Directory) entry.getValue();
Directory subIFD = (Directory) value;
writeIFD(subIFD, stream, true);
dataOffset += computeDataSize(subIFD);
stream.seek(streamPosition);
@@ -161,8 +187,26 @@ public final class TIFFWriter extends MetadataWriter {
return ifdOffset;
}
public long computeIFDSize(final Collection<Entry> directory) {
return WORD_LENGTH + computeDataSize(new IFD(directory)) + directory.size() * ENTRY_LENGTH;
private void writeDirectoryCount(ImageOutputStream stream, int count) throws IOException {
if (longOffsets) {
stream.writeLong(count);
}
else {
stream.writeShort(count);
}
}
private void writeValueCount(ImageOutputStream stream, int count) throws IOException {
if (longOffsets) {
stream.writeLong(count);
}
else {
stream.writeInt(count);
}
}
public long computeIFDSize(final Collection<? extends Entry> directory) {
return directoryCountLength + computeDataSize(new IFD(directory)) + directory.size() * entryLength;
}
private long computeDataSize(final Directory directory) {
@@ -175,13 +219,13 @@ public final class TIFFWriter extends MetadataWriter {
throw new IllegalArgumentException(String.format("Unknown size for entry %s", entry));
}
if (length > LONGWORD_LENGTH) {
if (length > offsetSize) {
dataSize += length;
}
if (entry.getValue() instanceof Directory) {
Directory subIFD = (Directory) entry.getValue();
long subIFDSize = WORD_LENGTH + subIFD.size() * ENTRY_LENGTH + computeDataSize(subIFD);
long subIFDSize = directoryCountLength + computeDataSize(subIFD) + subIFD.size() * entryLength;
dataSize += subIFDSize;
}
}
@@ -229,11 +273,11 @@ public final class TIFFWriter extends MetadataWriter {
short type = getType(entry);
long valueLength = getValueLength(type, getCount(entry));
if (valueLength <= LONGWORD_LENGTH) {
if (valueLength <= offsetSize) {
writeValueInline(entry.getValue(), type, stream);
// Pad
for (long i = valueLength; i < LONGWORD_LENGTH; i++) {
for (long i = valueLength; i < offsetSize; i++) {
stream.write(0);
}
@@ -248,7 +292,7 @@ public final class TIFFWriter extends MetadataWriter {
private int getCount(final Entry entry) {
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 {
@@ -344,12 +388,28 @@ public final class TIFFWriter extends MetadataWriter {
doubles = (double[]) value;
}
else {
throw new IllegalArgumentException("Unsupported type for TIFF FLOAT: " + value.getClass());
throw new IllegalArgumentException("Unsupported type for TIFF DOUBLE: " + value.getClass());
}
stream.writeDoubles(doubles, 0, doubles.length);
break;
case TIFF.TYPE_LONG8:
case TIFF.TYPE_SLONG8:
if (longOffsets) {
long[] longs;
if (value instanceof long[]) {
longs = (long[]) value;
}
else {
throw new IllegalArgumentException("Unsupported type for TIFF LONG8: " + value.getClass());
}
stream.writeLongs(longs, 0, longs.length);
break;
}
default:
throw new IllegalArgumentException("Unsupported TIFF type: " + type);
@@ -373,6 +433,7 @@ public final class TIFFWriter extends MetadataWriter {
break;
case TIFF.TYPE_LONG:
case TIFF.TYPE_SLONG:
case TIFF.TYPE_IFD:
stream.writeInt(((Number) value).intValue());
break;
case TIFF.TYPE_RATIONAL:
@@ -387,6 +448,13 @@ public final class TIFFWriter extends MetadataWriter {
case TIFF.TYPE_DOUBLE:
stream.writeDouble(((Number) value).doubleValue());
break;
case TIFF.TYPE_LONG8:
case TIFF.TYPE_SLONG8:
case TIFF.TYPE_IFD8:
if (longOffsets) {
stream.writeLong(((Number) value).longValue());
break;
}
default:
throw new IllegalArgumentException("Unsupported TIFF type: " + type);
@@ -395,18 +463,39 @@ public final class TIFFWriter extends MetadataWriter {
}
private void writeValueAt(final long dataOffset, final Object value, final short type, final ImageOutputStream stream) throws IOException {
stream.writeInt(assertIntegerOffset(dataOffset));
writeOffset(stream, dataOffset);
long position = stream.getStreamPosition();
stream.seek(dataOffset);
writeValueInline(value, type, stream);
stream.seek(position);
}
private int assertIntegerOffset(long offset) throws IIOException {
if (offset > Integer.MAX_VALUE - (long) Integer.MIN_VALUE) {
public void writeOffset(final ImageOutputStream output, long offset) throws IOException {
if (longOffsets) {
output.writeLong(assertLongOffset(offset));
}
else {
output.writeInt(assertIntegerOffset(offset)); // Treated as unsigned
}
}
public int offsetSize() {
return offsetSize;
}
private int assertIntegerOffset(final long offset) throws IIOException {
if (offset < 0 || offset > Integer.MAX_VALUE - (long) Integer.MIN_VALUE) {
throw new IIOException("Integer overflow for TIFF stream");
}
return (int) offset;
}
private long assertLongOffset(final long offset) throws IIOException {
if (offset < 0) {
throw new IIOException("Long overflow for BigTIFF stream");
}
return offset;
}
}
@@ -0,0 +1,345 @@
/*
* Copyright (c) 2013, 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.metadata.tiff;
import com.twelvemonkeys.imageio.metadata.*;
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
import com.twelvemonkeys.io.FastByteArrayOutputStream;
import org.junit.Test;
import javax.imageio.ImageIO;
import javax.imageio.stream.ImageOutputStream;
import javax.imageio.stream.ImageOutputStreamImpl;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/**
* TIFFWriterTest
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: TIFFWriterTest.java,v 1.0 18.07.13 09:53 haraldk Exp$
*/
public class BigTIFFWriterTest extends MetadataWriterAbstractTest {
@Override
protected InputStream getData() throws IOException {
// TODO: Replace with BigTIFF resource
return getResource("/exif/exif-jpeg-segment.bin").openStream();
}
protected TIFFReader createReader() {
return new TIFFReader();
}
@Override
protected TIFFWriter createWriter() {
return new TIFFWriter(8);
}
@Test
public void testWriteReadSimple() throws IOException {
ArrayList<Entry> entries = new ArrayList<>();
entries.add(new TIFFEntry(TIFF.TAG_ORIENTATION, TIFF.TYPE_SHORT, 1));
entries.add(new TIFFEntry(TIFF.TAG_IMAGE_WIDTH, TIFF.TYPE_SHORT, 1600));
entries.add(new AbstractEntry(TIFF.TAG_IMAGE_HEIGHT, 900) {});
entries.add(new TIFFEntry(TIFF.TAG_ARTIST, TIFF.TYPE_ASCII, "Harald K."));
entries.add(new AbstractEntry(TIFF.TAG_SOFTWARE, "TwelveMonkeys ImageIO") {});
Directory directory = new AbstractDirectory(entries) {};
ByteArrayOutputStream output = new FastByteArrayOutputStream(1024);
ImageOutputStream imageStream = ImageIO.createImageOutputStream(output);
createWriter().write(directory, imageStream);
imageStream.flush();
assertEquals(output.size(), imageStream.getStreamPosition());
byte[] data = output.toByteArray();
assertEquals(164, data.length);
assertEquals('M', data[0]);
assertEquals('M', data[1]);
assertEquals(0, data[2]);
assertEquals(43, data[3]);
Directory read = createReader().read(new ByteArrayImageInputStream(data));
assertNotNull(read);
assertEquals(5, read.size());
// TODO: Assert that the tags are written in ascending order (don't test the read directory, but the file structure)!
assertNotNull(read.getEntryById(TIFF.TAG_SOFTWARE));
assertEquals("TwelveMonkeys ImageIO", read.getEntryById(TIFF.TAG_SOFTWARE).getValue());
assertNotNull(read.getEntryById(TIFF.TAG_IMAGE_WIDTH));
assertEquals(1600, read.getEntryById(TIFF.TAG_IMAGE_WIDTH).getValue());
assertNotNull(read.getEntryById(TIFF.TAG_IMAGE_HEIGHT));
assertEquals(900, read.getEntryById(TIFF.TAG_IMAGE_HEIGHT).getValue());
assertNotNull(read.getEntryById(TIFF.TAG_ORIENTATION));
assertEquals(1, read.getEntryById(TIFF.TAG_ORIENTATION).getValue());
assertNotNull(read.getEntryById(TIFF.TAG_ARTIST));
assertEquals("Harald K.", read.getEntryById(TIFF.TAG_ARTIST).getValue());
}
@Test
public void testWriteMotorola() throws IOException {
ArrayList<Entry> entries = new ArrayList<>();
entries.add(new AbstractEntry(TIFF.TAG_SOFTWARE, "TwelveMonkeys ImageIO") {});
entries.add(new TIFFEntry(TIFF.TAG_IMAGE_WIDTH, TIFF.TYPE_LONG, Integer.MAX_VALUE));
Directory directory = new AbstractDirectory(entries) {};
ByteArrayOutputStream output = new FastByteArrayOutputStream(1024);
ImageOutputStream imageStream = ImageIO.createImageOutputStream(output);
imageStream.setByteOrder(ByteOrder.BIG_ENDIAN); // BE = Motorola
createWriter().write(directory, imageStream);
imageStream.flush();
assertEquals(output.size(), imageStream.getStreamPosition());
byte[] data = output.toByteArray();
assertEquals(94, data.length);
assertEquals('M', data[0]);
assertEquals('M', data[1]);
assertEquals(0, data[2]);
assertEquals(43, data[3]);
Directory read = new TIFFReader().read(new ByteArrayImageInputStream(data));
assertNotNull(read);
assertEquals(2, read.size());
assertNotNull(read.getEntryById(TIFF.TAG_SOFTWARE));
assertEquals("TwelveMonkeys ImageIO", read.getEntryById(TIFF.TAG_SOFTWARE).getValue());
assertNotNull(read.getEntryById(TIFF.TAG_IMAGE_WIDTH));
assertEquals((long) Integer.MAX_VALUE, read.getEntryById(TIFF.TAG_IMAGE_WIDTH).getValue());
}
@Test
public void testWriteIntel() throws IOException {
ArrayList<Entry> entries = new ArrayList<>();
entries.add(new AbstractEntry(TIFF.TAG_SOFTWARE, "TwelveMonkeys ImageIO") {});
entries.add(new TIFFEntry(TIFF.TAG_IMAGE_WIDTH, TIFF.TYPE_LONG, Integer.MAX_VALUE));
Directory directory = new AbstractDirectory(entries) {};
ByteArrayOutputStream output = new FastByteArrayOutputStream(1024);
ImageOutputStream imageStream = ImageIO.createImageOutputStream(output);
imageStream.setByteOrder(ByteOrder.LITTLE_ENDIAN); // LE = Intel
createWriter().write(directory, imageStream);
imageStream.flush();
assertEquals(output.size(), imageStream.getStreamPosition());
byte[] data = output.toByteArray();
assertEquals(94, data.length);
assertEquals('I', data[0]);
assertEquals('I', data[1]);
assertEquals(43, data[2]);
assertEquals(0, data[3]);
Directory read = new TIFFReader().read(new ByteArrayImageInputStream(data));
assertNotNull(read);
assertEquals(2, read.size());
assertNotNull(read.getEntryById(TIFF.TAG_SOFTWARE));
assertEquals("TwelveMonkeys ImageIO", read.getEntryById(TIFF.TAG_SOFTWARE).getValue());
assertNotNull(read.getEntryById(TIFF.TAG_IMAGE_WIDTH));
assertEquals((long) Integer.MAX_VALUE, read.getEntryById(TIFF.TAG_IMAGE_WIDTH).getValue());
}
@Test
public void testNestingIFD8Long8() throws IOException {
TIFFEntry artist = new TIFFEntry(TIFF.TAG_SOFTWARE, TIFF.TYPE_ASCII, "TwelveMonkeys ImageIO");
TIFFEntry subSubSubSubIFD = new TIFFEntry(TIFF.TAG_SUB_IFD, TIFF.TYPE_IFD8, new IFD(Collections.singletonList(artist)));
TIFFEntry subSubSubIFD = new TIFFEntry(TIFF.TAG_SUB_IFD, TIFF.TYPE_LONG8, new IFD(Collections.singletonList(subSubSubSubIFD)));
TIFFEntry subSubIFD = new TIFFEntry(TIFF.TAG_SUB_IFD, TIFF.TYPE_LONG8, new IFD(Collections.singletonList(subSubSubIFD)));
TIFFEntry subIFD = new TIFFEntry(TIFF.TAG_SUB_IFD, TIFF.TYPE_IFD8, new IFD(Collections.singletonList(subSubIFD)));
Directory directory = new IFD(Collections.<Entry>singletonList(subIFD));
ByteArrayOutputStream output = new FastByteArrayOutputStream(1024);
ImageOutputStream imageStream = ImageIO.createImageOutputStream(output);
createWriter().write(directory, imageStream);
imageStream.flush();
assertEquals(output.size(), imageStream.getStreamPosition());
Directory read = new TIFFReader().read(new ByteArrayImageInputStream(output.toByteArray()));
assertNotNull(read);
assertEquals(1, read.size());
assertEquals(subIFD, read.getEntryById(TIFF.TAG_SUB_IFD)); // Recursively tests content!
}
@Test
public void testNestingIFDLong() throws IOException {
TIFFEntry artist = new TIFFEntry(TIFF.TAG_SOFTWARE, TIFF.TYPE_ASCII, "TwelveMonkeys ImageIO");
TIFFEntry subSubSubSubIFD = new TIFFEntry(TIFF.TAG_SUB_IFD, TIFF.TYPE_IFD, new IFD(Collections.singletonList(artist)));
TIFFEntry subSubSubIFD = new TIFFEntry(TIFF.TAG_SUB_IFD, TIFF.TYPE_LONG, new IFD(Collections.singletonList(subSubSubSubIFD)));
TIFFEntry subSubIFD = new TIFFEntry(TIFF.TAG_SUB_IFD, TIFF.TYPE_LONG, new IFD(Collections.singletonList(subSubSubIFD)));
TIFFEntry subIFD = new TIFFEntry(TIFF.TAG_SUB_IFD, TIFF.TYPE_IFD, new IFD(Collections.singletonList(subSubIFD)));
Directory directory = new IFD(Collections.<Entry>singletonList(subIFD));
ByteArrayOutputStream output = new FastByteArrayOutputStream(1024);
ImageOutputStream imageStream = ImageIO.createImageOutputStream(output);
createWriter().write(directory, imageStream);
imageStream.flush();
assertEquals(output.size(), imageStream.getStreamPosition());
Directory read = new TIFFReader().read(new ByteArrayImageInputStream(output.toByteArray()));
assertNotNull(read);
assertEquals(1, read.size());
assertEquals(subIFD, read.getEntryById(TIFF.TAG_SUB_IFD)); // Recursively tests content!
}
@Test
public void testReadWriteRead() throws IOException {
Directory original = createReader().read(getDataAsIIS());
ByteArrayOutputStream output = new FastByteArrayOutputStream(256);
ImageOutputStream imageOutput = ImageIO.createImageOutputStream(output);
try {
createWriter().write(original, imageOutput);
}
finally {
imageOutput.close();
}
Directory read = createReader().read(new ByteArrayImageInputStream(output.toByteArray()));
assertEquals(original, read);
}
@Test
public void testComputeIFDSize() throws IOException {
ArrayList<Entry> entries = new ArrayList<>();
entries.add(new TIFFEntry(TIFF.TAG_ORIENTATION, TIFF.TYPE_SHORT, 1));
entries.add(new TIFFEntry(TIFF.TAG_IMAGE_WIDTH, TIFF.TYPE_SHORT, 1600));
entries.add(new AbstractEntry(TIFF.TAG_IMAGE_HEIGHT, 900) {});
entries.add(new TIFFEntry(TIFF.TAG_ARTIST, TIFF.TYPE_ASCII, "Harald K."));
entries.add(new AbstractEntry(TIFF.TAG_SOFTWARE, "TwelveMonkeys ImageIO") {});
TIFFWriter writer = createWriter();
ImageOutputStream stream = new NullImageOutputStream();
writer.writeIFD(entries, stream);
assertEquals(140, writer.computeIFDSize(entries));
assertEquals(148, stream.getStreamPosition());
}
@Test
public void testComputeIFDSizeNestedIFD8Long8() throws IOException {
TIFFEntry artist = new TIFFEntry(TIFF.TAG_SOFTWARE, TIFF.TYPE_ASCII, "TwelveMonkeys ImageIO");
TIFFEntry subSubSubSubIFD = new TIFFEntry(TIFF.TAG_SUB_IFD, TIFF.TYPE_IFD8, new IFD(Collections.singletonList(artist)));
TIFFEntry subSubSubIFD = new TIFFEntry(TIFF.TAG_SUB_IFD, TIFF.TYPE_LONG8, new IFD(Collections.singletonList(subSubSubSubIFD)));
TIFFEntry subSubIFD = new TIFFEntry(TIFF.TAG_SUB_IFD, TIFF.TYPE_LONG8, new IFD(Collections.singletonList(subSubSubIFD)));
TIFFEntry subIFD = new TIFFEntry(TIFF.TAG_SUB_IFD, TIFF.TYPE_IFD8, new IFD(Collections.singletonList(subSubIFD)));
List<Entry> entries = Collections.<Entry>singletonList(subIFD);
TIFFWriter writer = createWriter();
ImageOutputStream stream = new NullImageOutputStream();
writer.writeIFD(entries, stream);
assertEquals(162, writer.computeIFDSize(entries));
assertEquals(170, stream.getStreamPosition());
}
@Test
public void testComputeIFDSizeNestedIFDLong() throws IOException {
TIFFEntry artist = new TIFFEntry(TIFF.TAG_SOFTWARE, TIFF.TYPE_ASCII, "TwelveMonkeys ImageIO");
TIFFEntry subSubSubSubIFD = new TIFFEntry(TIFF.TAG_SUB_IFD, TIFF.TYPE_IFD, new IFD(Collections.singletonList(artist)));
TIFFEntry subSubSubIFD = new TIFFEntry(TIFF.TAG_SUB_IFD, TIFF.TYPE_LONG, new IFD(Collections.singletonList(subSubSubSubIFD)));
TIFFEntry subSubIFD = new TIFFEntry(TIFF.TAG_SUB_IFD, TIFF.TYPE_LONG, new IFD(Collections.singletonList(subSubSubIFD)));
TIFFEntry subIFD = new TIFFEntry(TIFF.TAG_SUB_IFD, TIFF.TYPE_IFD, new IFD(Collections.singletonList(subSubIFD)));
List<Entry> entries = Collections.<Entry>singletonList(subIFD);
TIFFWriter writer = createWriter();
ImageOutputStream stream = new NullImageOutputStream();
writer.writeIFD(entries, stream);
assertEquals(162, writer.computeIFDSize(entries)); // 162 = 5 * (8 + 20) + 22
assertEquals(170, stream.getStreamPosition()); // 170 = 8 + 5 * (8 + 20) + 22
}
private static class NullImageOutputStream extends ImageOutputStreamImpl {
@Override
public void write(int b) throws IOException {
streamPos++;
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
streamPos += len;
}
@Override
public int read() throws IOException {
throw new UnsupportedOperationException("Method read not implemented");
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
throw new UnsupportedOperationException("Method read not implemented");
}
}
}
@@ -33,6 +33,7 @@ package com.twelvemonkeys.imageio.metadata.tiff;
import com.twelvemonkeys.imageio.metadata.*;
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
import com.twelvemonkeys.io.FastByteArrayOutputStream;
import org.junit.Test;
import javax.imageio.ImageIO;
@@ -84,7 +85,7 @@ public class TIFFWriterTest extends MetadataWriterAbstractTest {
ByteArrayOutputStream output = new FastByteArrayOutputStream(1024);
ImageOutputStream imageStream = ImageIO.createImageOutputStream(output);
new TIFFWriter().write(directory, imageStream);
createWriter().write(directory, imageStream);
imageStream.flush();
assertEquals(output.size(), imageStream.getStreamPosition());
@@ -132,7 +133,7 @@ public class TIFFWriterTest extends MetadataWriterAbstractTest {
imageStream.setByteOrder(ByteOrder.BIG_ENDIAN); // BE = Motorola
new TIFFWriter().write(directory, imageStream);
createWriter().write(directory, imageStream);
imageStream.flush();
assertEquals(output.size(), imageStream.getStreamPosition());
@@ -167,7 +168,7 @@ public class TIFFWriterTest extends MetadataWriterAbstractTest {
imageStream.setByteOrder(ByteOrder.LITTLE_ENDIAN); // LE = Intel
new TIFFWriter().write(directory, imageStream);
createWriter().write(directory, imageStream);
imageStream.flush();
assertEquals(output.size(), imageStream.getStreamPosition());
@@ -204,7 +205,7 @@ public class TIFFWriterTest extends MetadataWriterAbstractTest {
ByteArrayOutputStream output = new FastByteArrayOutputStream(1024);
ImageOutputStream imageStream = ImageIO.createImageOutputStream(output);
new TIFFWriter().write(directory, imageStream);
createWriter().write(directory, imageStream);
imageStream.flush();
assertEquals(output.size(), imageStream.getStreamPosition());
@@ -247,9 +248,10 @@ public class TIFFWriterTest extends MetadataWriterAbstractTest {
TIFFWriter writer = createWriter();
ImageOutputStream stream = new NullImageOutputStream();
writer.write(new IFD(entries), stream);
writer.writeIFD(entries, stream);
assertEquals(stream.getStreamPosition(), writer.computeIFDSize(entries) + 12);
assertEquals(94, writer.computeIFDSize(entries));
assertEquals(98, stream.getStreamPosition());
}
@Test
@@ -266,9 +268,10 @@ public class TIFFWriterTest extends MetadataWriterAbstractTest {
TIFFWriter writer = createWriter();
ImageOutputStream stream = new NullImageOutputStream();
writer.write(new IFD(entries), stream);
writer.writeIFD(entries, stream);
assertEquals(stream.getStreamPosition(), writer.computeIFDSize(entries) + 12);
assertEquals(92, writer.computeIFDSize(entries)); // 92 = 5 * (2 + 12) + 22
assertEquals(96, stream.getStreamPosition()); // 96 = 4 + 5 * (2 + 12) + 22
}
private static class NullImageOutputStream extends ImageOutputStreamImpl {
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<artifactId>imageio-pcx</artifactId>
<name>TwelveMonkeys :: ImageIO :: PCX plugin</name>
@@ -104,8 +104,15 @@ public final class PCXImageReader extends ImageReaderBase {
ImageTypeSpecifier rawType = getRawImageType(imageIndex);
List<ImageTypeSpecifier> specifiers = new ArrayList<>();
// TODO: Implement
if (rawType.getSampleModel() instanceof BandedSampleModel) {
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);
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
return ImageTypeSpecifiers.createBanded(ColorSpace.getInstance(ColorSpace.CS_sRGB), createIndices(channels, 1), createIndices(channels, 0), DataBuffer.TYPE_BYTE, channels == 4, false);
case 24:
// Some sources says this is possible...
// Some sources say this is possible...
return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR);
case 32:
// Some sources says this is possible...
// Some sources say this is possible...
return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR);
default:
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/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)
// 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_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)
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<artifactId>imageio-pdf</artifactId>
<name>TwelveMonkeys :: ImageIO :: PDF plugin</name>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<artifactId>imageio-pict</artifactId>
<name>TwelveMonkeys :: ImageIO :: PICT plugin</name>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<artifactId>imageio-pnm</artifactId>
<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
protected List<String> getFormatNames() {
return Arrays.asList(
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<artifactId>imageio-psd</artifactId>
<name>TwelveMonkeys :: ImageIO :: PSD plugin</name>
@@ -33,6 +33,7 @@ package com.twelvemonkeys.imageio.plugins.psd;
import com.twelvemonkeys.imageio.util.IIOUtil;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import java.awt.color.ICC_Profile;
import java.io.IOException;
import java.io.InputStream;
@@ -52,14 +53,23 @@ final class ICCProfile extends PSDImageResource {
}
@Override
protected void readData(ImageInputStream pInput) throws IOException {
InputStream stream = IIOUtil.createStreamAdapter(pInput, size);
try {
protected void readData(final ImageInputStream pInput) throws IOException {
try (InputStream stream = IIOUtil.createStreamAdapter(pInput, size)) {
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;
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.TIFFWriter;
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
import com.twelvemonkeys.imageio.stream.SubImageOutputStream;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import java.io.IOException;
import java.util.Collection;
/**
* EXIF metadata.
@@ -60,6 +65,27 @@ final class PSDEXIF1Data extends PSDDirectoryResource {
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
public String toString() {
Directory directory = getDirectory();
@@ -31,6 +31,7 @@
package com.twelvemonkeys.imageio.plugins.psd;
import javax.imageio.IIOException;
import javax.imageio.stream.ImageOutputStream;
import java.io.DataInput;
import java.io.IOException;
@@ -57,8 +58,8 @@ final class PSDHeader {
// WORD Mode; /* Color mode */
// } PSD_HEADER;
private static final int PSD_MAX_SIZE = 30000;
private static final int PSB_MAX_SIZE = 300000;
static final int PSD_MAX_SIZE = 30000;
static final int PSB_MAX_SIZE = 300000;
final short channels;
final int width;
@@ -67,7 +68,57 @@ final class PSDHeader {
final short mode;
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();
if (signature != PSD.SIGNATURE_8BPS) {
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();
boolean largeFormat;
switch (version) {
case PSD.VERSION_PSD:
largeFormat = false;
@@ -89,15 +141,15 @@ final class PSDHeader {
byte[] reserved = new byte[6];
pInput.readFully(reserved); // We don't really care
channels = pInput.readShort();
short channels = pInput.readShort();
if (channels < 1 || channels > 56) {
throw new IIOException(String.format("Unsupported number of channels for PSD: %d", channels));
}
height = pInput.readInt(); // Rows
width = pInput.readInt(); // Columns
int height = pInput.readInt(); // Rows
int width = pInput.readInt(); // Columns
bits = pInput.readShort();
short bits = pInput.readShort();
switch (bits) {
case 1:
@@ -109,7 +161,7 @@ final class PSDHeader {
throw new IIOException(String.format("Unsupported bit depth for PSD: %d bits", bits));
}
mode = pInput.readShort();
short mode = pInput.readShort();
switch (mode) {
case PSD.COLOR_MODE_BITMAP:
@@ -124,6 +176,21 @@ final class PSDHeader {
default:
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
@@ -177,4 +244,5 @@ final class PSDHeader {
return "Unkown mode";
}
}
}
@@ -331,7 +331,7 @@ public final class PSDImageReader extends ImageReaderBase {
// Just stick to the raw type
}
// Finally add the raw type
// Finally, add the raw type
types.add(rawType);
return types.iterator();
@@ -842,7 +842,7 @@ public final class PSDImageReader extends ImageReaderBase {
assertInput();
if (header == null) {
header = new PSDHeader(imageInput);
header = PSDHeader.read(imageInput);
if (!header.hasValidDimensions()) {
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.
// 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))...
if (layerAndMaskInfoLength > 0) {
long pos = imageInput.getStreamPosition();
//if (metadata.layerInfo == null) {
long layerInfoLength = header.largeFormat ? imageInput.readLong() : imageInput.readUnsignedInt();
if (layerInfoLength > 0) {
@@ -991,7 +990,6 @@ public final class PSDImageReader extends ImageReaderBase {
System.out.println("layerInfo: " + metadata.layerInfo);
System.out.println("globalLayerMask: " + (metadata.globalLayerMask != PSDGlobalLayerMask.NULL_MASK ? metadata.globalLayerMask : null));
}
//}
}
metadata.imageDataStart = metadata.layerAndMaskInfoStart + layerAndMaskInfoLength + (header.largeFormat ? 8 : 4);
@@ -69,12 +69,10 @@ final public class PSDImageReaderSpi extends ImageReaderSpiBase {
switch (version) {
case PSD.VERSION_PSD:
case PSD.VERSION_PSB:
break;
return true;
default:
return false;
// Fall through
}
return true;
}
return false;
@@ -0,0 +1,69 @@
/*
* 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.psd;
import javax.imageio.ImageWriteParam;
import java.util.Locale;
/**
* PSDImageWriteParam
*/
public final class PSDImageWriteParam extends ImageWriteParam {
PSDImageWriteParam() {
this(Locale.getDefault());
}
PSDImageWriteParam(final Locale locale) {
super(locale);
compressionTypes = new String[] {
"None",
"PackBits",
// Two ZIP compression types are defined in spec, never seen in the wild...
// "ZIP",
// "ZIP+Predictor",
};
compressionType = compressionTypes[1];
canWriteCompressed = true;
}
static int getCompressionType(final ImageWriteParam param) {
if (param == null || param.getCompressionMode() != MODE_EXPLICIT || param.getCompressionType() == null || param.getCompressionType().equals("None")) {
return PSD.COMPRESSION_NONE;
}
else if (param.getCompressionType().equals("PackBits")) {
return PSD.COMPRESSION_RLE;
}
throw new IllegalArgumentException(String.format("Unsupported compression type: %s", param.getCompressionType()));
}
}
@@ -0,0 +1,400 @@
/*
* 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.psd;
import com.twelvemonkeys.imageio.ImageWriterBase;
import com.twelvemonkeys.imageio.metadata.Entry;
import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
import com.twelvemonkeys.imageio.metadata.tiff.TIFFEntry;
import com.twelvemonkeys.imageio.util.IIOUtil;
import com.twelvemonkeys.imageio.util.RasterUtils;
import com.twelvemonkeys.io.enc.EncoderStream;
import com.twelvemonkeys.io.enc.PackBitsEncoder;
import javax.imageio.*;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.spi.ImageWriterSpi;
import java.awt.color.ColorSpace;
import java.awt.color.ICC_ColorSpace;
import java.awt.color.ICC_Profile;
import java.awt.image.*;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteOrder;
import java.util.Collections;
/**
* Minimal ImageWriter for Adobe Photoshop Document (PSD) format.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: PSDImageWriter.java,v 1.0 Apr 29, 2008 4:45:52 PM haraldk Exp$
* @see <a href="http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/">Adobe Photoshop File Formats Specification<a>
* @see <a href="http://www.fileformat.info/format/psd/egff.htm">Adobe Photoshop File Format Summary<a>
*/
public final class PSDImageWriter extends ImageWriterBase {
PSDImageWriter(ImageWriterSpi provider) {
super(provider);
}
@Override
public IIOMetadata getDefaultImageMetadata(ImageTypeSpecifier imageType, ImageWriteParam param) {
// TODO: Implement
return null;
}
@Override
public IIOMetadata convertImageMetadata(IIOMetadata inData, ImageTypeSpecifier imageType, ImageWriteParam param) {
// TODO: Implement
return null;
}
@Override
public ImageWriteParam getDefaultWriteParam() {
return new PSDImageWriteParam(getLocale());
}
@Override
public void write(IIOMetadata streamMetadata, IIOImage iioImage, ImageWriteParam param) throws IOException {
assertOutput();
imageOutput.setByteOrder(ByteOrder.BIG_ENDIAN);
RenderedImage image = iioImage.getRenderedImage();
SampleModel sampleModel = image.getSampleModel();
int colorComponents = image.getColorModel().getColorSpace().getNumComponents();
int channels = sampleModel.getNumBands();
int width = image.getWidth();
int height = image.getHeight();
int bits = getBitsPerSample(sampleModel);
int mode = getColorMode(image.getColorModel());
// TODO: Allow stream metadata or param to force PSD/PSB (version 1/2)?
boolean largeFormat = width > PSDHeader.PSD_MAX_SIZE || height > PSDHeader.PSD_MAX_SIZE;
new PSDHeader(channels, width, height, bits, mode, largeFormat).write(imageOutput);
writeColorModeData(image, mode);
writeImageResources(image, mode);
// Length of the layer and mask information section. (**PSB** length is 8 bytes.)
// TODO: Write an empty dummy layer here, if there's alpha? See below... Or see if Photoshop handles alpha if no layers at all...
if (largeFormat) {
imageOutput.writeLong(0);
}
else {
imageOutput.writeInt(0);
}
processImageStarted(0);
// Image Data Section (composite layer only).
// The last section of a Photoshop file contains the image pixel data.
// Image data is stored in planar order: first all the red data, then all the green data, etc.
// Each plane is stored in scan-line order, with no pad bytes,
final int compression = PSDImageWriteParam.getCompressionType(param);
imageOutput.writeShort(compression);
long byteCountPos = imageOutput.getStreamPosition();
// PSB (large format) byte counts are actually 32 bit offsets, not 16 bit as described in spec
int[] byteCounts = new int[compression == PSD.COMPRESSION_RLE ? height * channels : 0];
imageOutput.skipBytes(byteCounts.length * (largeFormat ? 4 : 2));
// TODO: Loop over tiles?
Raster tile = sampleModel.getTransferType() == DataBuffer.TYPE_INT && sampleModel instanceof SinglePixelPackedSampleModel
? RasterUtils.asByteRaster(image.getTile(0, 0))
: image.getTile(0, 0);
for (int channel = 0; channel < channels; channel++) {
// TODO: Alpha issues:
// 1. Alpha channel is written (but not read, because there are no layers, and alpha is considered present only if layer count is negative)
// - Can we write a small hidden layer, just to have -1 layers?
// 2. Alpha needs to be premultiplied against white background (to avoid inverse halo)
Raster channelRaster = tile.createChild(0, 0, width, height, 0, 0, new int[] {channel});
switch (bits) {
case 1:
// TODO: Figure out why we can't write multi-pixel packed 1 bit samples as bytes...
case 8:
write8BitChannel(channel, colorComponents, mode, compression, channelRaster, byteCounts);
break;
case 16:
write16BitChannel(channel, colorComponents, mode, compression, channelRaster, byteCounts);
break;
case 32:
write32BitChannel(channel, colorComponents, mode, compression, channelRaster, byteCounts);
break;
default:
throw new AssertionError(); // Should be guarded against already
}
processImageProgress(channel * 100f / channels);
}
updateByteCounts(byteCountPos, byteCounts, largeFormat);
processImageComplete();
}
private void updateByteCounts(long byteCountPos, int[] byteCounts, boolean largeFormat) throws IOException {
if (byteCounts.length == 0) {
return;
}
// Update byte counts for RLE
long pos = imageOutput.getStreamPosition();
imageOutput.seek(byteCountPos);
if (largeFormat) {
imageOutput.writeInts(byteCounts, 0, byteCounts.length);
}
else {
for (int byteCount : byteCounts) {
imageOutput.writeShort(byteCount);
}
}
imageOutput.seek(pos);
}
private void writeColorModeData(RenderedImage image, int mode) throws IOException {
if (mode == PSD.COLOR_MODE_INDEXED) {
IndexColorModel icm = (IndexColorModel) image.getColorModel();
// Indexed color images: length is 768; color data contains the color table for the image, in non-interleaved order.
imageOutput.writeInt(768);
byte[] colors = new byte[256];
icm.getReds(colors);
imageOutput.write(colors);
icm.getGreens(colors);
imageOutput.write(colors);
icm.getBlues(colors);
imageOutput.write(colors);
}
else {
imageOutput.writeInt(0);
}
}
private void writeImageResources(RenderedImage image, int mode) throws IOException {
// Length of image resource section. The length may be zero
imageOutput.writeInt(0);
long startImageResources = imageOutput.getStreamPosition();
// Write ICC color profile if not "native" sRGB or gray (or bitmap/indexed)
if (mode != PSD.COLOR_MODE_BITMAP && mode != PSD.COLOR_MODE_INDEXED) {
ColorSpace colorSpace = image.getColorModel().getColorSpace();
if (!colorSpace.isCS_sRGB() && colorSpace instanceof ICC_ColorSpace) {
ICC_Profile profile = ((ICC_ColorSpace) colorSpace).getProfile();
ICCProfile.writeData(imageOutput, profile);
}
}
// Write creator software (Exif)
Entry software = new TIFFEntry(TIFF.TAG_SOFTWARE, TIFF.TYPE_ASCII, "TwelveMonkeys ImageIO PSD writer " + originatingProvider.getVersion());
PSDEXIF1Data.writeData(imageOutput, Collections.singleton(software));
long endImageResources = imageOutput.getStreamPosition();
// Update image resources length
imageOutput.seek(startImageResources - 4);
imageOutput.writeInt((int) (endImageResources - startImageResources));
imageOutput.seek(endImageResources);
}
private void write8BitChannel(int channel, int colorComponents, int colorMode, int compression, Raster raster, int[] byteCounts) throws IOException {
int width = raster.getWidth();
int height = raster.getHeight();
byte[] rowBytes = null;
for (int y = 0; y < height; y++) {
rowBytes = (byte[]) raster.getDataElements(0, y, width, 1, rowBytes);
// Photoshop likes to store CMYK values inverted (but not the alpha value)
if (colorMode == PSD.COLOR_MODE_CMYK && channel < colorComponents) {
for (int i = 0; i < rowBytes.length; i++) {
rowBytes[i] = (byte) (0xff - rowBytes[i] & 0xff);
}
}
if (compression == PSD.COMPRESSION_NONE) {
imageOutput.write(rowBytes);
}
else if (compression == PSD.COMPRESSION_RLE) {
long startPos = imageOutput.getStreamPosition();
// The RLE compressed data follows, with each scan line compressed separately
try (OutputStream stream = new EncoderStream(IIOUtil.createStreamAdapter(imageOutput), new PackBitsEncoder())) {
stream.write(rowBytes);
}
long endPos = imageOutput.getStreamPosition();
byteCounts[y + channel * height] = (int) (endPos - startPos);
}
else {
throw new IIOException("PSD with ZIP compression not supported");
}
}
}
private void write16BitChannel(int channel, int colorComponents, int colorMode, int compression, Raster raster, int[] byteCounts) throws IOException {
int width = raster.getWidth();
int height = raster.getHeight();
short[] row = null;
for (int y = 0; y < height; y++) {
row = (short[]) raster.getDataElements(0, y, width, 1, row);
// Photoshop likes to store CMYK values inverted (but not the alpha value)
if (colorMode == PSD.COLOR_MODE_CMYK && channel < colorComponents) {
for (int i = 0; i < row.length; i++) {
row[i] = (short) (0xffff - row[i] & 0xffff);
}
}
if (compression == PSD.COMPRESSION_NONE) {
imageOutput.writeShorts(row, 0, row.length);
}
else if (compression == PSD.COMPRESSION_RLE) {
long startPos = imageOutput.getStreamPosition();
// The RLE compressed data follows, with each scan line compressed separately
try (DataOutputStream stream = new DataOutputStream(new EncoderStream(IIOUtil.createStreamAdapter(imageOutput), new PackBitsEncoder()))) {
for (short sample : row) {
stream.writeShort(sample);
}
}
long endPos = imageOutput.getStreamPosition();
byteCounts[y + channel * height] = (int) (endPos - startPos);
}
else {
throw new IIOException("PSD with ZIP compression not supported");
}
}
}
private void write32BitChannel(int channel, int colorComponents, int colorMode, int compression, Raster raster, int[] byteCounts) throws IOException {
int width = raster.getWidth();
int height = raster.getHeight();
int[] row = null;
for (int y = 0; y < height; y++) {
row = (int[]) raster.getDataElements(0, y, width, 1, row);
// Photoshop likes to store CMYK values inverted (but not the alpha value)
if (colorMode == PSD.COLOR_MODE_CMYK && channel < colorComponents) {
for (int i = 0; i < row.length; i++) {
row[i] = 0xffffffff - row[i];
}
}
if (compression == PSD.COMPRESSION_NONE) {
imageOutput.writeInts(row, 0, row.length);
}
else if (compression == PSD.COMPRESSION_RLE) {
long startPos = imageOutput.getStreamPosition();
// The RLE compressed data follows, with each scan line compressed separately
try (DataOutputStream stream = new DataOutputStream(new EncoderStream(IIOUtil.createStreamAdapter(imageOutput), new PackBitsEncoder()))) {
for (int sample : row) {
stream.writeInt(sample);
}
}
long endPos = imageOutput.getStreamPosition();
byteCounts[y + channel * height] = (int) (endPos - startPos);
}
else {
throw new IIOException("PSD with ZIP compression not supported");
}
}
}
static int getColorMode(ColorModel colorModel) {
if (colorModel instanceof IndexColorModel) {
if (colorModel.getPixelSize() == 1) {
return PSD.COLOR_MODE_BITMAP;
}
else {
return PSD.COLOR_MODE_INDEXED;
}
}
int csType = colorModel.getColorSpace().getType();
switch (csType) {
case ColorSpace.TYPE_GRAY:
if (colorModel.getPixelSize() == 1) {
return PSD.COLOR_MODE_BITMAP;
}
else {
return PSD.COLOR_MODE_GRAYSCALE;
}
case ColorSpace.TYPE_RGB:
return PSD.COLOR_MODE_RGB;
case ColorSpace.TYPE_CMYK:
return PSD.COLOR_MODE_CMYK;
default:
throw new IllegalArgumentException("Unsupported color space type for PSD: " + csType);
}
}
static int getBitsPerSample(SampleModel sampleModel) {
int bits = sampleModel.getSampleSize(0);
for (int i = 1; i < sampleModel.getNumBands(); i++) {
if (bits != sampleModel.getSampleSize(i)) {
throw new IllegalArgumentException("All samples must be of equal size for PSD: " + bits);
}
}
switch (bits) {
case 1:
case 8:
case 16:
case 32:
return (short) bits;
default:
throw new IllegalArgumentException("Unsupported sample size for PSD (expected 1, 8, 16 or 32): " + bits);
}
}
public static void main(String[] args) throws IOException {
BufferedImage image = ImageIO.read(new File(args[0]));
ImageIO.write(image, "PSD", new File("test.psd"));
}
}
@@ -0,0 +1,77 @@
/*
* 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.psd;
import com.twelvemonkeys.imageio.spi.ImageWriterSpiBase;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriter;
import java.util.Locale;
import static com.twelvemonkeys.imageio.plugins.psd.PSDImageWriter.getBitsPerSample;
import static com.twelvemonkeys.imageio.plugins.psd.PSDImageWriter.getColorMode;
/**
* PSDImageWriterSpi
*/
public final class PSDImageWriterSpi extends ImageWriterSpiBase {
public PSDImageWriterSpi() {
super(new PSDProviderInfo());
}
@Override
public boolean canEncodeImage(ImageTypeSpecifier type) {
// PSD supports:
// - 1, 8, 16 or 32 bit/sample
// - Number of samples <= 56
// - RGB, CMYK, Gray, Indexed color
try {
getBitsPerSample(type.getSampleModel());
getColorMode(type.getColorModel());
}
catch (IllegalArgumentException ignore) {
// We can't write this type
return false;
}
return type.getNumBands() <= 56; // Can't be negative
}
@Override
public ImageWriter createWriterInstance(Object extension) {
return new PSDImageWriter(this);
}
public String getDescription(final Locale pLocale) {
return "Adobe Photoshop Document (PSD) image writer";
}
}
@@ -43,8 +43,8 @@ final class PSDProviderInfo extends ReaderWriterProviderInfo {
protected PSDProviderInfo() {
super(
PSDProviderInfo.class,
new String[] {"psd", "PSD"},
new String[] {"psd"},
new String[] {"psd", "PSD", "psb", "PSB"},
new String[] {"psd", "psb"},
new String[] {
"image/vnd.adobe.photoshop", // Official, IANA registered
"application/vnd.adobe.photoshop", // Used in XMP
@@ -54,8 +54,8 @@ final class PSDProviderInfo extends ReaderWriterProviderInfo {
},
"com.twelvemonkeys.imageio.plugins.psd.PSDImageReader",
new String[] {"com.twelvemonkeys.imageio.plugins.psd.PSDImageReaderSpi"},
null,
null, // new String[] {"com.twelvemonkeys.imageio.plugins.psd.PSDImageWriterSpi"},
"com.twelvemonkeys.imageio.plugins.psd.PSDImageWriter",
new String[] {"com.twelvemonkeys.imageio.plugins.psd.PSDImageWriterSpi"},
false, null, null, 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);
reader = PSDUtil.readUnicodeString(pInput);
fileVersion = pInput.readInt();
}
@@ -0,0 +1 @@
com.twelvemonkeys.imageio.plugins.psd.PSDImageWriterSpi
@@ -86,8 +86,6 @@ public class PSDImageReaderTest extends ImageReaderAbstractTest<PSDImageReader>
new TestData(getClassLoaderResource("/psd/test_gray16.psd"), new Dimension(710, 512)),
// 4 channel, CMYK, 16 bit samples
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)
new TestData(getClassLoaderResource("/psb/test_original.psb"), new Dimension(710, 512)),
// 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("/psb/rgb-multichannel-no-transparency.psb"), new Dimension(100, 100)),
// 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+
);
}
@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
protected List<String> getFormatNames() {
return Collections.singletonList("psd");
@@ -472,7 +507,7 @@ public class PSDImageReaderTest extends ImageReaderAbstractTest<PSDImageReader>
public void testMultiChannelNoTransparencyPSB() throws IOException {
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"))) {
imageReader.setInput(stream);
@@ -0,0 +1,37 @@
package com.twelvemonkeys.imageio.plugins.psd;
import com.twelvemonkeys.imageio.util.ImageWriterAbstractTest;
import javax.imageio.spi.ImageWriterSpi;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.util.Arrays;
import java.util.List;
/**
* PSDImageWriterTest.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: PSDImageWriterTest.java,v 1.0 05/05/2021 haraldk Exp$
*/
public class PSDImageWriterTest extends ImageWriterAbstractTest<PSDImageWriter> {
@Override
protected ImageWriterSpi createProvider() {
return new PSDImageWriterSpi();
}
@Override
protected List<? extends RenderedImage> getTestData() {
return Arrays.asList(
new BufferedImage(300, 200, BufferedImage.TYPE_INT_RGB),
new BufferedImage(301, 199, BufferedImage.TYPE_INT_ARGB),
new BufferedImage(299, 201, BufferedImage.TYPE_3BYTE_BGR),
new BufferedImage(160, 90, BufferedImage.TYPE_4BYTE_ABGR),
new BufferedImage(90, 160, BufferedImage.TYPE_BYTE_GRAY),
new BufferedImage(30, 20, BufferedImage.TYPE_USHORT_GRAY),
new BufferedImage(30, 20, BufferedImage.TYPE_BYTE_BINARY),
new BufferedImage(30, 20, BufferedImage.TYPE_BYTE_INDEXED)
);
}
}
+13 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<artifactId>imageio-reference</artifactId>
<name>TwelveMonkeys :: ImageIO :: JDK Reference Tests</name>
@@ -16,6 +16,18 @@
<project.jpms.module.name>com.twelvemonkeys.imageio.reference</project.jpms.module.name>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
@@ -46,6 +46,7 @@ import java.util.Collections;
import java.util.List;
import static com.twelvemonkeys.imageio.util.IIOUtil.lookupProviderByName;
import static org.junit.Assume.assumeTrue;
/**
* JPEGImageReaderTest
@@ -89,24 +90,16 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
@Override
public void testSetDestination() throws IOException {
// Known bug in Sun JPEGImageReader before Java 6
if (IS_JAVA_6_OR_LATER) {
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");
}
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();
}
@Test
@Override
public void testSetDestinationType() throws IOException {
// Known bug in Sun JPEGImageReader before Java 6
if (IS_JAVA_6_OR_LATER) {
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");
}
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();
}
@Test
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<artifactId>imageio-sgi</artifactId>
<name>TwelveMonkeys :: ImageIO :: SGI plugin</name>
@@ -59,7 +59,7 @@ public final class SGIImageReader extends ImageReaderBase {
private SGIHeader header;
protected SGIImageReader(final ImageReaderSpi provider) {
SGIImageReader(final ImageReaderSpi provider) {
super(provider);
}
@@ -88,9 +88,31 @@ public final class SGIImageReader extends ImageReaderBase {
public Iterator<ImageTypeSpecifier> getImageTypes(final int imageIndex) throws IOException {
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);
return specifiers.iterator();
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<artifactId>imageio-tga</artifactId>
<name>TwelveMonkeys :: ImageIO :: TGA plugin</name>
@@ -96,10 +96,18 @@ final class TGAImageReader extends ImageReaderBase {
ImageTypeSpecifier rawType = getRawImageType(imageIndex);
List<ImageTypeSpecifier> specifiers = new ArrayList<>();
// TODO: Implement
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();
}
@@ -116,7 +124,14 @@ final class TGAImageReader extends ImageReaderBase {
return ImageTypeSpecifiers.createFromIndexColorModel(header.getColorMap());
case TGA.IMAGETYPE_MONOCHROME:
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_RLE:
ColorSpace sRGB = ColorSpace.getInstance(ColorSpace.CS_sRGB);
@@ -135,12 +150,14 @@ final class TGAImageReader extends ImageReaderBase {
case 24:
return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR);
case 32:
// 4BYTE_BGRX...
// Can't mask out alpha (efficiently) for 4BYTE, so we'll ignore it while reading instead,
// if hasAlpha is false
return ImageTypeSpecifiers.createInterleaved(sRGB, new int[] {2, 1, 0, 3}, DataBuffer.TYPE_BYTE, true, isAlphaPremultiplied);
// NOTE: We'll read using little endian byte order, thus the file layout is BGRA/BGRx
if (hasAlpha) {
return ImageTypeSpecifier.createFromBufferedImageType(isAlphaPremultiplied ? BufferedImage.TYPE_INT_ARGB_PRE : BufferedImage.TYPE_INT_ARGB);
}
return ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB);
default:
throw new IIOException("Unknown pixel depth for truecolor: " + header.getPixelDepth());
throw new IIOException("Unknown pixel depth for true color: " + header.getPixelDepth());
}
default:
throw new IIOException("Unknown image type: " + header.getImageType());
@@ -187,20 +204,26 @@ final class TGAImageReader extends ImageReaderBase {
input = imageInput;
}
int pixelDepth = header.getPixelDepth();
boolean flipped = isOriginLowerLeft(header.getOrigin());
for (int y = 0; y < height; y++) {
switch (header.getPixelDepth()) {
switch (pixelDepth) {
case 8:
case 24:
case 32:
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;
case 16:
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;
default:
throw new AssertionError("Unsupported pixel depth: " + header.getPixelDepth());
throw new AssertionError("Unsupported pixel depth: " + pixelDepth);
}
processImageProgress(100f * y / height);
@@ -220,10 +243,26 @@ final class TGAImageReader extends ImageReaderBase {
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 {
// 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 (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);
return;
@@ -244,19 +283,7 @@ final class TGAImageReader extends ImageReaderBase {
}
}
switch (origin) {
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);
}
destChannel.setDataElements(0, dstY, srcChannel);
}
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 {
// 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 (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);
return;
@@ -283,19 +314,32 @@ final class TGAImageReader extends ImageReaderBase {
}
}
switch (origin) {
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);
destChannel.setDataElements(0, dstY, srcChannel);
}
private void readRowInt(final DataInput input, int height, Rectangle srcRegion, boolean flip, int xSub, int ySub,
int[] rowDataInt, 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 (y % ySub != 0 || srcY < srcRegion.y || srcY >= srcRegion.y + srcRegion.height) {
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
@@ -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) {
if (rect.contains(raster.getMinX(), 0, raster.getWidth(), 1)
&& xSub == 1
@@ -446,20 +503,26 @@ final class TGAImageReader extends ImageReaderBase {
// Thumbnail is always stored non-compressed, no need for RLE support
imageInput.seek(extensions.getThumbnailOffset() + 2);
int pixelDepth = header.getPixelDepth();
boolean flipped = isOriginLowerLeft(header.getOrigin());
for (int y = 0; y < height; y++) {
switch (header.getPixelDepth()) {
switch (pixelDepth) {
case 8:
case 24:
case 32:
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;
case 16:
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;
default:
throw new AssertionError("Unsupported pixel depth: " + header.getPixelDepth());
throw new AssertionError("Unsupported pixel depth: " + pixelDepth);
}
processThumbnailProgress(100f * y / height);
@@ -193,10 +193,13 @@ final class TGAMetadata extends AbstractMetadata {
switch (header.getPixelDepth()) {
case 8:
bitsPerSample.setAttribute("value", createListValue(1, Integer.toString(header.getPixelDepth())));
bitsPerSample.setAttribute("value", createListValue(1, "8"));
break;
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");
}
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_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>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<artifactId>imageio-thumbsdb</artifactId>
<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.8.0-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>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<artifactId>imageio-tiff-jdk-interop</artifactId>
<name>TwelveMonkeys :: ImageIO :: TIFF/JDK JPEG Interop</name>
@@ -13,9 +13,22 @@
</description>
<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>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
@@ -28,25 +28,26 @@
* 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 com.twelvemonkeys.imageio.plugins.tiff.TIFFImageReaderSpi;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
import static org.junit.Assert.fail;
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.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
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.

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