mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2026-05-19 00:00:03 -04:00
Compare commits
40 Commits
3.7-bugfix
...
camera-raw
| Author | SHA1 | Date | |
|---|---|---|---|
| 4b2ebec429 | |||
| 0498a1a095 | |||
| 4fd20f7fa0 | |||
| c9bd68623a | |||
| 4d2326c18d | |||
| 94eac2d6e5 | |||
| f63a33d541 | |||
| 00f8d87f36 | |||
| 4c2ab6da7b | |||
| b5088312e2 | |||
| f04f968f12 | |||
| 8896092e31 | |||
| 2f9768a1d4 | |||
| 06bcf22242 | |||
| 20c7f8e60e | |||
| 15a9ad0a9b | |||
| 7ae2d636dc | |||
| 12e756b23c | |||
| 4e2bf131d2 | |||
| d0c4a07556 | |||
| 21059c8d5a | |||
| fa7b530809 | |||
| 790cf3b32e | |||
| b1baaad23b | |||
| 7fa704ace5 | |||
| 8d07f4fe90 | |||
| 32bba6857b | |||
| 7ff74465d3 | |||
| 1f5bf80f7e | |||
| 5c35c6e997 | |||
| 7692eeef5e | |||
| cdadbf7051 | |||
| 7c2423bc94 | |||
| f9837d4540 | |||
| 7728a912b1 | |||
| 923598636e | |||
| 6ab0e64107 | |||
| f69f706f60 | |||
| 53dd493cfa | |||
| 607502fbbd |
@@ -0,0 +1 @@
|
||||
github: haraldk
|
||||
@@ -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.
|
||||
@@ -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,17 @@
|
||||
**What is fixed**
|
||||
|
||||
Add link to the issue this PR fixes.
|
||||
|
||||
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
|
||||
@@ -47,11 +47,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 +166,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 +220,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 +274,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 +289,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 +298,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 +370,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 +497,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
|
||||
|
||||
+16
-1
@@ -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>
|
||||
@@ -58,6 +58,16 @@
|
||||
<artifactId>imageio-bmp</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-cr2</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-dng</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-hdr</artifactId>
|
||||
@@ -78,6 +88,11 @@
|
||||
<artifactId>imageio-jpeg</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-nef</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-pcx</artifactId>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
+1
-1
@@ -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
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
+23
-15
@@ -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;
|
||||
}
|
||||
}
|
||||
+197
-154
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
package com.twelvemonkeys.imageio.util;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import java.awt.*;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.*;
|
||||
import java.util.Random;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertSame;
|
||||
|
||||
/**
|
||||
* RasterUtilsTest.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: RasterUtilsTest.java,v 1.0 05/05/2021 haraldk Exp$
|
||||
*/
|
||||
public class RasterUtilsTest {
|
||||
@Test(expected = NullPointerException.class)
|
||||
public void testAsByteRasterFromNull() {
|
||||
RasterUtils.asByteRaster((Raster) null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("RedundantCast")
|
||||
@Test(expected = NullPointerException.class)
|
||||
public void testAsByteRasterWritableFromNull() {
|
||||
RasterUtils.asByteRaster((WritableRaster) null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsByteRasterPassThrough() {
|
||||
WritableRaster[] rasters = new WritableRaster[] {
|
||||
new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR).getRaster(),
|
||||
new BufferedImage(1, 1, BufferedImage.TYPE_4BYTE_ABGR).getRaster(),
|
||||
new BufferedImage(1, 1, BufferedImage.TYPE_4BYTE_ABGR_PRE).getRaster(),
|
||||
new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY).getRaster(),
|
||||
Raster.createBandedRaster(DataBuffer.TYPE_BYTE, 1, 1, 7, null),
|
||||
Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, 1, 1, 2, null),
|
||||
new WritableRaster(new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE, 1, 1, 1, 1, new int[1]), new Point(0, 0)) {}
|
||||
};
|
||||
|
||||
for (Raster raster : rasters) {
|
||||
assertSame(raster, RasterUtils.asByteRaster(raster));
|
||||
}
|
||||
|
||||
for (WritableRaster raster : rasters) {
|
||||
assertSame(raster, RasterUtils.asByteRaster(raster));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsByteRasterWritableFromTYPE_INT_RGB() {
|
||||
BufferedImage image = new BufferedImage(9, 11, BufferedImage.TYPE_INT_RGB);
|
||||
|
||||
WritableRaster raster = RasterUtils.asByteRaster(image.getRaster());
|
||||
|
||||
assertEquals(DataBuffer.TYPE_BYTE, raster.getTransferType());
|
||||
assertEquals(PixelInterleavedSampleModel.class, raster.getSampleModel().getClass());
|
||||
assertEquals(image.getWidth(), raster.getWidth());
|
||||
assertEquals(image.getHeight(), raster.getHeight());
|
||||
|
||||
assertEquals(3, raster.getNumBands());
|
||||
assertEquals(3, raster.getNumDataElements());
|
||||
|
||||
assertImageRasterEquals(image, raster);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsByteRasterWritableFromTYPE_INT_ARGB() {
|
||||
BufferedImage image = new BufferedImage(9, 11, BufferedImage.TYPE_INT_ARGB);
|
||||
|
||||
WritableRaster raster = RasterUtils.asByteRaster(image.getRaster());
|
||||
|
||||
assertEquals(DataBuffer.TYPE_BYTE, raster.getTransferType());
|
||||
assertEquals(PixelInterleavedSampleModel.class, raster.getSampleModel().getClass());
|
||||
assertEquals(image.getWidth(), raster.getWidth());
|
||||
assertEquals(image.getHeight(), raster.getHeight());
|
||||
|
||||
assertEquals(4, raster.getNumBands());
|
||||
assertEquals(4, raster.getNumDataElements());
|
||||
|
||||
assertImageRasterEquals(image, raster);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsByteRasterWritableFromTYPE_INT_ARGB_PRE() {
|
||||
BufferedImage image = new BufferedImage(9, 11, BufferedImage.TYPE_INT_ARGB_PRE);
|
||||
|
||||
WritableRaster raster = RasterUtils.asByteRaster(image.getRaster());
|
||||
|
||||
assertEquals(DataBuffer.TYPE_BYTE, raster.getTransferType());
|
||||
assertEquals(PixelInterleavedSampleModel.class, raster.getSampleModel().getClass());
|
||||
assertEquals(image.getWidth(), raster.getWidth());
|
||||
assertEquals(image.getHeight(), raster.getHeight());
|
||||
|
||||
assertEquals(4, raster.getNumBands());
|
||||
assertEquals(4, raster.getNumDataElements());
|
||||
|
||||
// We don't assert on values here, as the premultiplied values makes it hard...
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsByteRasterWritableFromTYPE_INT_BGR() {
|
||||
BufferedImage image = new BufferedImage(9, 11, BufferedImage.TYPE_INT_BGR);
|
||||
|
||||
WritableRaster raster = RasterUtils.asByteRaster(image.getRaster());
|
||||
|
||||
assertEquals(DataBuffer.TYPE_BYTE, raster.getTransferType());
|
||||
assertEquals(PixelInterleavedSampleModel.class, raster.getSampleModel().getClass());
|
||||
assertEquals(image.getWidth(), raster.getWidth());
|
||||
assertEquals(image.getHeight(), raster.getHeight());
|
||||
|
||||
assertEquals(3, raster.getNumBands());
|
||||
assertEquals(3, raster.getNumDataElements());
|
||||
|
||||
assertImageRasterEquals(image, raster);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsByteRasterWritableFromTYPE_CUSTOM_GRAB() {
|
||||
BufferedImage image = ImageTypeSpecifier.createPacked(ColorSpace.getInstance(ColorSpace.CS_sRGB),
|
||||
0x00FF0000,
|
||||
0xFF000000,
|
||||
0x000000FF,
|
||||
0x0000FF00,
|
||||
DataBuffer.TYPE_INT, false).createBufferedImage(7, 13);
|
||||
|
||||
WritableRaster raster = RasterUtils.asByteRaster(image.getRaster());
|
||||
|
||||
assertEquals(DataBuffer.TYPE_BYTE, raster.getTransferType());
|
||||
assertEquals(PixelInterleavedSampleModel.class, raster.getSampleModel().getClass());
|
||||
assertEquals(image.getWidth(), raster.getWidth());
|
||||
assertEquals(image.getHeight(), raster.getHeight());
|
||||
|
||||
assertEquals(4, raster.getNumBands());
|
||||
assertEquals(4, raster.getNumDataElements());
|
||||
|
||||
assertImageRasterEquals(image, raster);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsByteRasterWritableFromTYPE_CUSTOM_BxRG() {
|
||||
BufferedImage image = ImageTypeSpecifier.createPacked(ColorSpace.getInstance(ColorSpace.CS_sRGB),
|
||||
0x0000FF00,
|
||||
0x000000FF,
|
||||
0xFF000000,
|
||||
0,
|
||||
DataBuffer.TYPE_INT, false).createBufferedImage(7, 13);
|
||||
|
||||
WritableRaster raster = RasterUtils.asByteRaster(image.getRaster());
|
||||
|
||||
assertEquals(DataBuffer.TYPE_BYTE, raster.getTransferType());
|
||||
assertEquals(PixelInterleavedSampleModel.class, raster.getSampleModel().getClass());
|
||||
assertEquals(image.getWidth(), raster.getWidth());
|
||||
assertEquals(image.getHeight(), raster.getHeight());
|
||||
|
||||
assertEquals(3, raster.getNumBands());
|
||||
assertEquals(3, raster.getNumDataElements());
|
||||
|
||||
assertImageRasterEquals(image, raster);
|
||||
}
|
||||
|
||||
private static void assertImageRasterEquals(BufferedImage image, WritableRaster raster) {
|
||||
// NOTE: This is NOT necessarily how the values are stored in the data buffer
|
||||
int[] argbOffs = new int[] {16, 8, 0, 24};
|
||||
|
||||
Raster imageRaster = image.getRaster();
|
||||
|
||||
Random rng = new Random(27365481723L);
|
||||
|
||||
for (int y = 0; y < raster.getHeight(); y++) {
|
||||
for (int x = 0; x < raster.getWidth(); x++) {
|
||||
int argb = 0;
|
||||
|
||||
for (int b = 0; b < raster.getNumBands(); b++) {
|
||||
int s = rng.nextInt(0xFF);
|
||||
raster.setSample(x, y, b, s);
|
||||
|
||||
assertEquals(s, raster.getSample(x, y, b));
|
||||
assertEquals(s, imageRaster.getSample(x, y, b));
|
||||
|
||||
argb |= (s << argbOffs[b]);
|
||||
}
|
||||
|
||||
if (raster.getNumBands() < 4) {
|
||||
argb |= 0xFF000000;
|
||||
}
|
||||
|
||||
int expectedArgb = image.getRGB(x, y);
|
||||
if (argb != expectedArgb) {
|
||||
assertEquals(x + ", " + y + ": ", String.format("#%08x", expectedArgb), String.format("#%08x", argb));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2014, 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 "TwelveMonkeys" 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 OWNER 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.
|
||||
-->
|
||||
|
||||
<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/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>imageio</artifactId>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>imageio-cr2</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: CR2 plugin</name>
|
||||
<description>ImageIO plugin for Canon RAW (CR2) format.</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
<classifier>tests</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-metadata</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<version>${project.version}</version>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-jpeg</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
+492
@@ -0,0 +1,492 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 "TwelveMonkeys" 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 OWNER 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.cr2;
|
||||
|
||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||
import com.twelvemonkeys.imageio.metadata.CompoundDirectory;
|
||||
import com.twelvemonkeys.imageio.metadata.Directory;
|
||||
import com.twelvemonkeys.imageio.metadata.Entry;
|
||||
import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegment;
|
||||
import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegmentUtil;
|
||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
|
||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFFReader;
|
||||
import com.twelvemonkeys.imageio.plugins.jpeg.Slice;
|
||||
import com.twelvemonkeys.imageio.plugins.jpeg.SliceContext;
|
||||
import com.twelvemonkeys.imageio.stream.SubImageInputStream;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
//import com.twelvemonkeys.imageio.plugins.jpeg.LosslessJPEGDecoder;
|
||||
|
||||
/**
|
||||
* Canon CR2 RAW ImageReader.
|
||||
* <p/>
|
||||
* Acknowledgement:
|
||||
* This ImageReader is based on the excellent work of Laurent Clevy, and would probably not exist without it.
|
||||
*
|
||||
* @see <a href="http://lclevy.free.fr/dng/">Understanding What is stored in a Canon RAW .CR2 file, How and Why</a>
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: CR2ImageReader.java,v 1.0 07.04.14 21:31 haraldk Exp$
|
||||
*/
|
||||
public final class CR2ImageReader extends ImageReaderBase {
|
||||
// See http://lclevy.free.fr/dng/
|
||||
// TODO: Avoid duped code from TIFFImageReader
|
||||
// TODO: Probably a good idea to move some of the getAsShort/Int/Long/Array to TIFF/EXIF metadata module
|
||||
// TODO: Automatic EXIF rotation, if we find a good way to do that for JPEG/EXIF/TIFF and keeping the metadata sane...
|
||||
|
||||
final static boolean DEBUG = true; //"true".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.imageio.plugins.dng.debug"));
|
||||
|
||||
// Thumbnail is in IFD1 (2nd entry)
|
||||
private final static int THUMBNAIL_IFD = 1;
|
||||
|
||||
private CompoundDirectory IFDs;
|
||||
private Directory currentIFD;
|
||||
|
||||
CR2ImageReader(final ImageReaderSpi provider) {
|
||||
super(provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resetMembers() {
|
||||
IFDs = null;
|
||||
currentIFD = null;
|
||||
}
|
||||
|
||||
private void readMetadata() throws IOException {
|
||||
if (imageInput == null) {
|
||||
throw new IllegalStateException("input not set");
|
||||
}
|
||||
|
||||
if (IFDs == null) {
|
||||
// We'll validate the TIFF structure later, for now just see if we have 'CR'0x0200 at the right place
|
||||
|
||||
imageInput.skipBytes(8); // TIFF byte order mark + magic + IFD0 offset
|
||||
|
||||
if (imageInput.readByte() != 'C' || imageInput.readByte() != 'R') {
|
||||
throw new IIOException("Not a valid CR2 structure: Missing CR magic ('CR')");
|
||||
}
|
||||
|
||||
int version = imageInput.readUnsignedByte();
|
||||
int revision = imageInput.readUnsignedByte();
|
||||
|
||||
// TODO: Choke on anything but 2.0? All sample data from 400D until 5D mk III has 2.0 version...
|
||||
|
||||
imageInput.seek(0);
|
||||
|
||||
IFDs = (CompoundDirectory) new TIFFReader().read(imageInput); // NOTE: Sets byte order as a side effect
|
||||
|
||||
if (DEBUG) {
|
||||
System.err.println("Byte order: " + imageInput.getByteOrder());
|
||||
System.err.println("Version: " + version + "." + revision);
|
||||
System.err.println("Number of IFDs: " + IFDs.directoryCount());
|
||||
|
||||
for (int i = 0; i < IFDs.directoryCount(); i++) {
|
||||
System.err.printf("IFD %d: %s\n", i, IFDs.getDirectory(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void readIFD(final int ifdIndex) throws IOException {
|
||||
readMetadata();
|
||||
|
||||
if (ifdIndex < 0) {
|
||||
throw new IndexOutOfBoundsException("index < minIndex");
|
||||
}
|
||||
else if (ifdIndex >= IFDs.directoryCount()) {
|
||||
throw new IndexOutOfBoundsException("index >= numImages (" + ifdIndex + " >= " + IFDs.directoryCount() + ")");
|
||||
}
|
||||
|
||||
currentIFD = IFDs.getDirectory(ifdIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumImages(final boolean allowSearch) throws IOException {
|
||||
readMetadata();
|
||||
|
||||
// This validation is maybe a little too restrictive, but ok for now
|
||||
if (IFDs.directoryCount() != 4) {
|
||||
throw new IIOException("Unexpected number of IFDs in CR2: " + IFDs.directoryCount());
|
||||
}
|
||||
|
||||
return IFDs.directoryCount() - 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumThumbnails(int imageIndex) throws IOException {
|
||||
readMetadata();
|
||||
checkBounds(imageIndex);
|
||||
|
||||
return imageIndex == 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean readerSupportsThumbnails() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getThumbnailWidth(int imageIndex, int thumbnailIndex) throws IOException {
|
||||
readMetadata();
|
||||
checkBounds(imageIndex);
|
||||
|
||||
// TODO: Need to get from JPEGImageReader (no ImageWidth tag), this is an ok (but lame) implementation for now
|
||||
return super.getThumbnailWidth(imageIndex, thumbnailIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getThumbnailHeight(int imageIndex, int thumbnailIndex) throws IOException {
|
||||
readMetadata();
|
||||
checkBounds(imageIndex);
|
||||
|
||||
// TODO: Need to get from JPEGImageReader (no ImageHeight tag), this is an ok (but lame) implementation for now
|
||||
return super.getThumbnailHeight(imageIndex, thumbnailIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedImage readThumbnail(int imageIndex, int thumbnailIndex) throws IOException {
|
||||
readIFD(THUMBNAIL_IFD);
|
||||
|
||||
if (imageIndex != 0) {
|
||||
throw new IndexOutOfBoundsException("No thumbnail for imageIndex: " + imageIndex);
|
||||
}
|
||||
if (thumbnailIndex != 0) {
|
||||
throw new IndexOutOfBoundsException("thumbnailIndex out of bounds: " + thumbnailIndex);
|
||||
}
|
||||
|
||||
// This IFD (IFD1) lacks Compression 6 (old JPEG), but has the relevant tags for a JPEG/EXIF thumbnail
|
||||
int jpegOffset = getValueAsInt(TIFF.TAG_JPEG_INTERCHANGE_FORMAT, "JPEGInterchangeFormat");
|
||||
int jpegLength = getValueAsInt(TIFF.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, "JPEGInterchangeFormatLength");
|
||||
|
||||
imageInput.seek(jpegOffset);
|
||||
|
||||
// TODO: Consider using cached JPEGImageReader directly
|
||||
return ImageIO.read(new SubImageInputStream(imageInput, jpegLength));
|
||||
}
|
||||
|
||||
private long[] getValueAsLongArray(final int tag, final String tagName, boolean required) throws IIOException {
|
||||
Entry entry = currentIFD.getEntryById(tag);
|
||||
if (entry == null) {
|
||||
if (required) {
|
||||
throw new IIOException("Missing TIFF tag " + tagName);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
long[] value;
|
||||
|
||||
if (entry.valueCount() == 1) {
|
||||
// For single entries, this will be a boxed type
|
||||
value = new long[] {((Number) entry.getValue()).longValue()};
|
||||
}
|
||||
else if (entry.getValue() instanceof short[]) {
|
||||
short[] shorts = (short[]) entry.getValue();
|
||||
value = new long[shorts.length];
|
||||
|
||||
for (int i = 0, length = value.length; i < length; i++) {
|
||||
value[i] = shorts[i];
|
||||
}
|
||||
}
|
||||
else if (entry.getValue() instanceof int[]) {
|
||||
int[] ints = (int[]) entry.getValue();
|
||||
value = new long[ints.length];
|
||||
|
||||
for (int i = 0, length = value.length; i < length; i++) {
|
||||
value[i] = ints[i];
|
||||
}
|
||||
}
|
||||
else if (entry.getValue() instanceof long[]) {
|
||||
value = (long[]) entry.getValue();
|
||||
}
|
||||
else {
|
||||
throw new IIOException(String.format("Unsupported %s type: %s (%s)", tagName, entry.getTypeName(), entry.getValue().getClass()));
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private Number getValueAsNumberWithDefault(final int tag, final String tagName, final Number defaultValue) throws IIOException {
|
||||
Entry entry = currentIFD.getEntryById(tag);
|
||||
|
||||
if (entry == null) {
|
||||
if (defaultValue != null) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
throw new IIOException("Missing TIFF tag: " + (tagName != null ? tagName : tag));
|
||||
}
|
||||
|
||||
return (Number) entry.getValue();
|
||||
}
|
||||
|
||||
private long getValueAsLongWithDefault(final int tag, final String tagName, final Long defaultValue) throws IIOException {
|
||||
return getValueAsNumberWithDefault(tag, tagName, defaultValue).longValue();
|
||||
}
|
||||
|
||||
private long getValueAsLongWithDefault(final int tag, final Long defaultValue) throws IIOException {
|
||||
return getValueAsLongWithDefault(tag, null, defaultValue);
|
||||
}
|
||||
|
||||
private int getValueAsIntWithDefault(final int tag, final String tagName, final Integer defaultValue) throws IIOException {
|
||||
return getValueAsNumberWithDefault(tag, tagName, defaultValue).intValue();
|
||||
}
|
||||
|
||||
private int getValueAsIntWithDefault(final int tag, Integer defaultValue) throws IIOException {
|
||||
return getValueAsIntWithDefault(tag, null, defaultValue);
|
||||
}
|
||||
|
||||
private int getValueAsInt(final int tag, String tagName) throws IIOException {
|
||||
return getValueAsIntWithDefault(tag, tagName, null);
|
||||
}
|
||||
|
||||
private int imageIndexToIFDNumber(int imageIndex) {
|
||||
return imageIndex >= THUMBNAIL_IFD ? imageIndex + 1 : imageIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth(int imageIndex) throws IOException {
|
||||
readIFD(imageIndexToIFDNumber(imageIndex));
|
||||
|
||||
return getValueAsInt(TIFF.TAG_IMAGE_WIDTH, "ImageWidth");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight(int imageIndex) throws IOException {
|
||||
readIFD(imageIndexToIFDNumber(imageIndex));
|
||||
|
||||
return getValueAsInt(TIFF.TAG_IMAGE_HEIGHT, "ImageHeight");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IOException {
|
||||
readIFD(imageIndexToIFDNumber(imageIndex));
|
||||
|
||||
// TODO: For IFD0, get from JPEGImageReader delagate
|
||||
|
||||
// return Arrays.asList(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR)).iterator();
|
||||
Entry bitsPerSample = currentIFD.getEntryById(TIFF.TAG_BITS_PER_SAMPLE);
|
||||
if (bitsPerSample == null) {
|
||||
// TODO: FixME!
|
||||
return Arrays.asList(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR)).iterator();
|
||||
}
|
||||
|
||||
// For IFD1, create linear RGB, but we don't really know...
|
||||
int bitDepth = ((int[]) bitsPerSample.getValue())[0]; // Assume all equal!
|
||||
if (bitDepth == 8) {
|
||||
return Arrays.asList(ImageTypeSpecifier.createInterleaved(ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB), new int [] {0, 1, 2}, DataBuffer.TYPE_BYTE, false, false)).iterator();
|
||||
}
|
||||
else if (bitDepth == 16) {
|
||||
return Arrays.asList(ImageTypeSpecifier.createInterleaved(ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB), new int [] {0, 1, 2}, DataBuffer.TYPE_USHORT, false, false)).iterator();
|
||||
}
|
||||
|
||||
throw new IIOException("Unsupported bit depth: " + bitDepth);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedImage read(int imageIndex, ImageReadParam param) throws IOException {
|
||||
readIFD(imageIndexToIFDNumber(imageIndex));
|
||||
|
||||
if (imageIndex == 0) {
|
||||
// This one says Compression 6 (old JPEG) and contains normal JPEG data at StripOffsets (but has no JPEGInterchangeFormat tag)
|
||||
int compression = getValueAsInt(TIFF.TAG_COMPRESSION, "Compression");
|
||||
if (compression != 6) {
|
||||
throw new IIOException("Unknown TIFF compression for CR2 IFD0: " + compression);
|
||||
}
|
||||
|
||||
int stripOffsets = getValueAsInt(TIFF.TAG_STRIP_OFFSETS, "StripOffsets");
|
||||
int stripByteCounts = getValueAsInt(TIFF.TAG_STRIP_BYTE_COUNTS, "StripByteCounts");
|
||||
imageInput.seek(stripOffsets);
|
||||
|
||||
List<JPEGSegment> segments = JPEGSegmentUtil.readSegments(new SubImageInputStream(imageInput, stripByteCounts), JPEGSegmentUtil.ALL_SEGMENTS);
|
||||
System.err.println("segments: " + segments);
|
||||
|
||||
imageInput.seek(stripOffsets);
|
||||
BufferedImage image = ImageIO.read(new SubImageInputStream(imageInput, stripByteCounts));
|
||||
System.err.println("image: " + image);
|
||||
return image;
|
||||
}
|
||||
|
||||
if (imageIndex == 1) {
|
||||
// This one is semi-ok, for older cameras is says Compression 6 (old JPEG), for 7D it says Compression 1 (None)
|
||||
// We'll just ignore the compression and assume it's 1 (None).
|
||||
// TODO: Probably a good idea to verify that we have no other compression than 6/1
|
||||
// TODO: Consider just masking out this image, as it's not of much use...
|
||||
|
||||
int width = getWidth(imageIndex);
|
||||
int height = getHeight(imageIndex);
|
||||
|
||||
BufferedImage destination = getDestination(param, getImageTypes(imageIndex), width, height);
|
||||
WritableRaster raster;
|
||||
|
||||
int dataType = destination.getSampleModel().getDataType();
|
||||
if (dataType == DataBuffer.TYPE_BYTE) {
|
||||
// Emulate raw type (as dest, but RGB instead of BGR)
|
||||
raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, width, 1, width * 3, 3, new int[] {0, 1, 2}, null);
|
||||
}
|
||||
else if (dataType == DataBuffer.TYPE_USHORT) {
|
||||
// Emulate raw type (as dest, but RGB instead of BGR)
|
||||
raster = Raster.createInterleavedRaster(DataBuffer.TYPE_USHORT, width, 1, width * 3, 3, new int[] {0, 1, 2}, null);
|
||||
}
|
||||
else {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
DataBuffer dataBuffer = raster.getDataBuffer();
|
||||
|
||||
SampleModel destSampleModel = destination.getSampleModel();
|
||||
DataBuffer destBuffer = destination.getRaster().getDataBuffer();
|
||||
SampleModel srcSampleModel = raster.getSampleModel();
|
||||
|
||||
if (destBuffer.getSize() != getValueAsInt(TIFF.TAG_STRIP_BYTE_COUNTS, "StripByteCounts")) {
|
||||
System.err.println("dataBuffer: " + dataBuffer.getSize());
|
||||
System.err.println("StripByteCounts: " + getValueAsInt(TIFF.TAG_STRIP_BYTE_COUNTS, "StripByteCounts"));
|
||||
}
|
||||
|
||||
int stripOffsets = getValueAsInt(TIFF.TAG_STRIP_OFFSETS, "StripOffsets");
|
||||
imageInput.seek(stripOffsets);
|
||||
|
||||
Object data = null;
|
||||
for (int y = 0; y < height; y++) {
|
||||
if (dataType == DataBuffer.TYPE_BYTE) {
|
||||
imageInput.readFully(((DataBufferByte) dataBuffer).getData());
|
||||
}
|
||||
else {
|
||||
imageInput.readFully(((DataBufferUShort) dataBuffer).getData(), 0, dataBuffer.getSize());
|
||||
}
|
||||
|
||||
data = srcSampleModel.getDataElements(0, 0, width, 1, data, dataBuffer);
|
||||
destSampleModel.setDataElements(0, y, width, 1, data, destBuffer);
|
||||
}
|
||||
|
||||
// TODO: This seems to work as crop values, if so correct w/h from getImageWidth/Height??
|
||||
Entry unknown = currentIFD.getEntryById(50908);
|
||||
if (unknown != null) {
|
||||
Graphics2D g = destination.createGraphics();
|
||||
try {
|
||||
long[] values = (long[]) unknown.getValue();
|
||||
|
||||
g.setPaint(new Color(63, 223, 88, 128));
|
||||
// g.drawRect((int) values[2], (int) values[3], (int) values[0], (int) values[1]);
|
||||
// g.fillRect((int) values[2], (int) values[3], (int) values[0], (int) values[1]);
|
||||
g.fillRect(0, 0, width, (int) values[3]);
|
||||
g.fillRect(0, (int) values[3], (int) values[2], (int) (values[1] + values[3]));
|
||||
g.fillRect((int) (values[2] + values[0]), (int) values[3], (int) values[2], (int) (values[1] + values[3]));
|
||||
g.fillRect(0, (int) (values[3] + values[1]), width, height - (int) (values[3] + values[1]));
|
||||
}
|
||||
finally {
|
||||
g.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
return destination;
|
||||
}
|
||||
|
||||
if (imageIndex == 2) {
|
||||
// TODO: This is the real RAW data. It's supposed to be lossless JPEG encoded, single channel,
|
||||
// and has to be interpolated to become full 3 channel data from the Bayer CFA array,
|
||||
// then further processed with white balance correction, black subtraction and color scaling.
|
||||
// At least. ;-)
|
||||
|
||||
// We should probably just mask this image out, until we can read it
|
||||
|
||||
int stripOffsets = getValueAsInt(TIFF.TAG_STRIP_OFFSETS, "StripOffsets");
|
||||
int stripByteCounts = getValueAsInt(TIFF.TAG_STRIP_BYTE_COUNTS, "StripByteCounts");
|
||||
long[] slices = getValueAsLongArray(50752, "Slices", true);
|
||||
|
||||
try {
|
||||
final Slice slice = Slice.createSlice(slices);
|
||||
SliceContext.set(slice);
|
||||
|
||||
// TODO: Get correct dimensions (sensor size?)
|
||||
int width = getWidth(0);
|
||||
int height = getHeight(0);
|
||||
|
||||
imageInput.seek(stripOffsets);
|
||||
return ImageIO.read(new SubImageInputStream(imageInput, stripByteCounts));
|
||||
} finally {
|
||||
SliceContext.remove();
|
||||
}
|
||||
|
||||
// byte[] data = new LosslessJPEGDecoder().decompress(new SubImageInputStream(imageInput, stripByteCounts), null);
|
||||
//
|
||||
// // TODO: We really have 2 bytes/sample
|
||||
// short[] data2 = new short[data.length / 2];
|
||||
// ByteBuffer wrap = ByteBuffer.wrap(data);
|
||||
// wrap.asShortBuffer().get(data2);
|
||||
//
|
||||
// System.err.println("data.length: " + data2.length);
|
||||
// System.err.println("width x height: " + width * height);
|
||||
//
|
||||
// DataBuffer dataBuffer = new DataBufferUShort(data2, data2.length);
|
||||
// WritableRaster raster = Raster.createInterleavedRaster(dataBuffer, width, height, width, 1, new int[] {0}, null);
|
||||
// ColorModel cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), false, false, Transparency.OPAQUE, raster.getTransferType());
|
||||
// BufferedImage image = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
|
||||
//
|
||||
// System.err.println("image: " + image);
|
||||
//
|
||||
// return image;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
CR2ImageReader reader = new CR2ImageReader(new CR2ImageReaderSpi());
|
||||
|
||||
for (String arg : args) {
|
||||
ImageInputStream stream = ImageIO.createImageInputStream(new File(arg));
|
||||
reader.setInput(stream);
|
||||
|
||||
int numImages = reader.getNumImages(true);
|
||||
for (int i = 0; i < numImages; i++) {
|
||||
int numThumbnails = reader.getNumThumbnails(i);
|
||||
for (int n = 0; n < numThumbnails; n++) {
|
||||
showIt(reader.readThumbnail(i, n), arg + " image " + i + " thumbnail " + n);
|
||||
}
|
||||
|
||||
showIt(reader.read(i), arg + " image " + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 "TwelveMonkeys" 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 OWNER 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.cr2;
|
||||
|
||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
|
||||
import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
|
||||
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* CR2ImageReaderSpi
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: CR2ImageReaderSpi.java,v 1.0 07.04.14 21:26 haraldk Exp$
|
||||
*/
|
||||
public final class CR2ImageReaderSpi extends ImageReaderSpiBase {
|
||||
public CR2ImageReaderSpi() {
|
||||
super(new CR2ProviderInfo());
|
||||
}
|
||||
|
||||
public boolean canDecodeInput(final Object pSource) throws IOException {
|
||||
if (!(pSource instanceof ImageInputStream)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ImageInputStream stream = (ImageInputStream) pSource;
|
||||
|
||||
stream.mark();
|
||||
try {
|
||||
byte[] bom = new byte[2];
|
||||
stream.readFully(bom);
|
||||
|
||||
ByteOrder originalOrder = stream.getByteOrder();
|
||||
|
||||
try {
|
||||
if (bom[0] == 'I' && bom[1] == 'I') {
|
||||
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
}
|
||||
else if (bom[0] == 'M' && bom[1] == 'M') {
|
||||
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
||||
int tiffMagic = stream.readUnsignedShort();
|
||||
if (tiffMagic != TIFF.TIFF_MAGIC) {
|
||||
return false;
|
||||
}
|
||||
|
||||
stream.skipBytes(4); // TIFF IFD0 offset
|
||||
|
||||
if (stream.readByte() != 'C' || stream.readByte() != 'R') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Version 2.0
|
||||
return stream.readUnsignedByte() == 2 && stream.readUnsignedByte() == 0;
|
||||
}
|
||||
finally {
|
||||
stream.setByteOrder(originalOrder);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
stream.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageReader createReaderInstance(Object extension) throws IOException {
|
||||
return new CR2ImageReader(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription(Locale locale) {
|
||||
return "Canon RAW (CR2) format Reader";
|
||||
}
|
||||
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oleg Ermolaev
|
||||
* 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 "TwelveMonkeys" 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 OWNER 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.cr2;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
||||
|
||||
/**
|
||||
* @author Oleg Ermolaev Date: 04.05.2018 1:50
|
||||
*/
|
||||
class CR2ProviderInfo extends ReaderWriterProviderInfo {
|
||||
protected CR2ProviderInfo() {
|
||||
super(
|
||||
CR2ProviderInfo.class,
|
||||
new String[]{"cr2", "CR2"},
|
||||
new String[]{"cr2"},
|
||||
new String[]{"image/x-canon-raw", // TODO: Look up
|
||||
},
|
||||
"com.twelvemonkeys.imageio.plugins.cr2.CR2ImageReader",
|
||||
new String[]{"com.twelvemonkeys.imageio.plugins.cr2.CR2ImageReaderSpi"},
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
com.twelvemonkeys.imageio.plugins.cr2.CR2ImageReaderSpi
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 "TwelveMonkeys" 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 OWNER 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.cr2;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import java.awt.*;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* CR2ImageReaderTest
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: CR2ImageReaderTest.java,v 1.0 07.04.14 21:52 haraldk Exp$
|
||||
*/
|
||||
@Ignore
|
||||
public class CR2ImageReaderTest extends ImageReaderAbstractTest<CR2ImageReader> {
|
||||
@Override
|
||||
protected List<TestData> getTestData() {
|
||||
return Arrays.asList(
|
||||
new TestData(
|
||||
getClassLoaderResource("/cr2/IMG_3483.CR2"), // Canon EOS 400D
|
||||
new Dimension(3888, 2592) // This is what the TIFF structure says...
|
||||
/* from http://lclevy.free.fr/dng/:
|
||||
new Dimension(3888 / 4, 2592 / 4), // The image is supposed to be 1/4 of the size in the TIFF tags...
|
||||
new Dimension(-1, -1), // "small version", no size information, only JPEG data (EXIF thumbnail)
|
||||
new Dimension(384, 256), // according to http://lclevy.free.fr/dng/ this is not compressed, but TIFF structure says JPEG... Perhaps JPEG lossless?
|
||||
new Dimension(3888, 2592) // Full size image, no size information
|
||||
*/
|
||||
)
|
||||
// TODO: EOS 7D sample
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ImageReaderSpi createProvider() {
|
||||
return new CR2ImageReaderSpi();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getFormatNames() {
|
||||
return Arrays.asList("cr2");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getSuffixes() {
|
||||
return Arrays.asList("cr2");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getMIMETypes() {
|
||||
return Arrays.asList("image/x-canon-raw");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Known issue: Subsampled reading not supported")
|
||||
@Override
|
||||
public void testReadWithSubsampleParamPixels() throws IOException {
|
||||
super.testReadWithSubsampleParamPixels();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Known issue: Source region reading not supported")
|
||||
@Override
|
||||
public void testReadWithSourceRegionParamEqualImage() throws IOException {
|
||||
super.testReadWithSourceRegionParamEqualImage();
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,7 @@
|
||||
Contents:
|
||||
IMG_3483.CR2 - Rusty chain at Vækerø harbour, Oslo, Norway, Canon 400D, by me
|
||||
IMG_4841.CR2 - Chimneys at Casa Mila, Barcelona, Spain, Canon 400D, by me
|
||||
IMG_6933.CR2 - Panda PEZ dispenser at home, Oslo Noway, Canon 7D, by me
|
||||
|
||||
Above mentioned images are freely distributable for testing purposes.
|
||||
-- Harald K
|
||||
@@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2014, 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 "TwelveMonkeys" 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 OWNER 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.
|
||||
-->
|
||||
|
||||
<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/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>imageio</artifactId>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>imageio-crw</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: CRW plugin</name>
|
||||
<description>ImageIO plugin for Canon RAW (CRW) format.</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
<type>test-jar</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-metadata</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<version>${project.version}</version>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-jpeg</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.twelvemonkeys.imageio.plugins.crw;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* CRW
|
||||
*
|
||||
* @see <a href="https://sno.phy.queensu.ca/~phil/exiftool/canon_raw.html">The Canon RAW (CRW) File Format</a>
|
||||
*/
|
||||
public interface CRW {
|
||||
int TAG_SLICES = 50752;
|
||||
}
|
||||
+388
@@ -0,0 +1,388 @@
|
||||
package com.twelvemonkeys.imageio.plugins.crw;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
|
||||
/**
|
||||
* CRWDecoder
|
||||
*
|
||||
* @see <a href="http://cybercom.net/~dcoffin/dcraw/decompress.c">A simple reference decompressor for CRW files</a>
|
||||
* @author Harald Kuhr (Java port)
|
||||
* @author Dave Coffin (original decompress.c)
|
||||
*/
|
||||
final class CRWDecoder {
|
||||
|
||||
static class Decode {
|
||||
Decode[] branch = new Decode[2];
|
||||
int leaf;
|
||||
}
|
||||
|
||||
private final ImageInputStream imageInput;
|
||||
|
||||
private final int height;
|
||||
private final int width;
|
||||
private final int table;
|
||||
|
||||
private Decode[] first_decode = new Decode[32];
|
||||
private Decode[] second_decode = new Decode[512];
|
||||
|
||||
CRWDecoder(ImageInputStream imageInput, int width, int height, int table) {
|
||||
this.imageInput = imageInput;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.table = table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code false} if the image starts with compressed data,
|
||||
* {@code true} if it starts with uncompressed low-order bits.
|
||||
* <p>
|
||||
* In Canon compressed data, 0xff is always followed by 0x00.
|
||||
*/
|
||||
private boolean canonHasLowbits() throws IOException {
|
||||
byte[] test = new byte[0x4000]; // TODO: Should probably be (height * width / 4) * enough bytes...
|
||||
|
||||
imageInput.seek(0);
|
||||
imageInput.readFully(test);
|
||||
|
||||
boolean ret = true;
|
||||
// for (int i = 540; i < test.length - 1; i++) {
|
||||
for (int i = 0; i < test.length - 1; i++) { // Note: The original 540 is probably CIFF header length + offset (26 + 514)
|
||||
if ((test[i] & 0xff) == 0xff) {
|
||||
if (test[i + 1] != 0) {
|
||||
return true;
|
||||
}
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
A rough description of Canon's compression algorithm:
|
||||
|
||||
+ Each pixel outputs a 10-bit sample, from 0 to 1023.
|
||||
+ Split the data into blocks of 64 samples each.
|
||||
+ Subtract from each sample the value of the sample two positions
|
||||
to the left, which has the same color filter. From the two
|
||||
leftmost samples in each row, subtract 512.
|
||||
+ For each nonzero sample, make a token consisting of two four-bit
|
||||
numbers. The low nibble is the number of bits required to
|
||||
represent the sample, and the high nibble is the number of
|
||||
zero samples preceding this sample.
|
||||
+ Output this token as a variable-length bitstring using
|
||||
one of three tablesets. Follow it with a fixed-length
|
||||
bitstring containing the sample.
|
||||
|
||||
The "first_decode" table is used for the first sample in each
|
||||
block, and the "second_decode" table is used for the others.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Construct a decode tree according the specification in *source.
|
||||
* The first 16 bytes specify how many codes should be 1-bit, 2-bit
|
||||
* 3-bit, etc. Bytes after that are the leaf values.
|
||||
* <p>
|
||||
* For example, if the source is
|
||||
* <p>
|
||||
* { 0,1,4,2,3,1,2,0,0,0,0,0,0,0,0,0,
|
||||
* 0x04,0x03,0x05,0x06,0x02,0x07,0x01,0x08,0x09,0x00,0x0a,0x0b,0xff },
|
||||
* <p>
|
||||
* then the code is
|
||||
* <p>
|
||||
* 00 0x04
|
||||
* 010 0x03
|
||||
* 011 0x05
|
||||
* 100 0x06
|
||||
* 101 0x02
|
||||
* 1100 0x07
|
||||
* 1101 0x01
|
||||
* 11100 0x08
|
||||
* 11101 0x09
|
||||
* 11110 0x00
|
||||
* 111110 0x0a
|
||||
* 1111110 0x0b
|
||||
* 1111111 0xff
|
||||
*/
|
||||
private Decode[] free; /* Next unused node */
|
||||
private int freeIndex;
|
||||
private int leaf; /* no. of leaves already added */
|
||||
|
||||
// private void make_decoder(struct decode *dest, const uchar *source, int level)
|
||||
private void make_decoder(Decode[] dest, int destIndex, final byte[] source, int level) {
|
||||
// static struct decode *free; /* Next unused node */
|
||||
// static int leaf; /* no. of leaves already added */
|
||||
int i, next;
|
||||
|
||||
if (level==0) {
|
||||
free = dest;
|
||||
freeIndex = 0;
|
||||
|
||||
leaf = 0;
|
||||
}
|
||||
// free++;
|
||||
freeIndex++;
|
||||
// At what level should the next leaf appear?
|
||||
for (i=next=0; i <= leaf && next < 16; ) {
|
||||
i += (source[next++] & 0xff);
|
||||
}
|
||||
|
||||
if (i > leaf) {
|
||||
if (level < next) { /* Are we there yet? */
|
||||
// dest->branch[0] = free;
|
||||
// make_decoder(free, source, level + 1);
|
||||
dest[destIndex].branch[0] = free[freeIndex];
|
||||
make_decoder(free, freeIndex, source, level + 1);
|
||||
// dest->branch[1] = free;
|
||||
// make_decoder(free, source, level + 1);
|
||||
dest[destIndex].branch[1] = free[freeIndex];
|
||||
make_decoder(free, freeIndex, source, level + 1);
|
||||
} else {
|
||||
// dest->leaf = source[16 + leaf++];
|
||||
dest[destIndex].leaf = (source[16 + leaf++] & 0xff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static final byte[][] first_tree/*[3][29]*/ = {
|
||||
{ 0,1,4,2,3,1,2,0,0,0,0,0,0,0,0,0,
|
||||
0x04,0x03,0x05,0x06,0x02,0x07,0x01,0x08,0x09,0x00,0x0a,0x0b, (byte) 0xff},
|
||||
|
||||
{ 0,2,2,3,1,1,1,1,2,0,0,0,0,0,0,0,
|
||||
0x03,0x02,0x04,0x01,0x05,0x00,0x06,0x07,0x09,0x08,0x0a,0x0b, (byte) 0xff},
|
||||
|
||||
{ 0,0,6,3,1,1,2,0,0,0,0,0,0,0,0,0,
|
||||
0x06,0x05,0x07,0x04,0x08,0x03,0x09,0x02,0x00,0x0a,0x01,0x0b, (byte) 0xff},
|
||||
};
|
||||
|
||||
static final byte[][] second_tree/*[3][180]*/ = {
|
||||
{ 0,2,2,2,1,4,2,1,2,5,1,1,0,0,0, (byte) 139,
|
||||
0x03,0x04,0x02,0x05,0x01,0x06,0x07,0x08,
|
||||
0x12,0x13,0x11,0x14,0x09,0x15,0x22,0x00,0x21,0x16,0x0a, (byte) 0xf0,
|
||||
0x23,0x17,0x24,0x31,0x32,0x18,0x19,0x33,0x25,0x41,0x34,0x42,
|
||||
0x35,0x51,0x36,0x37,0x38,0x29,0x79,0x26,0x1a,0x39,0x56,0x57,
|
||||
0x28,0x27,0x52,0x55,0x58,0x43,0x76,0x59,0x77,0x54,0x61, (byte) 0xf9,
|
||||
0x71,0x78,0x75, (byte) 0x96, (byte) 0x97,0x49, (byte) 0xb7,0x53, (byte) 0xd7,0x74, (byte) 0xb6, (byte) 0x98,
|
||||
0x47,0x48, (byte) 0x95,0x69, (byte) 0x99, (byte) 0x91, (byte) 0xfa, (byte) 0xb8,0x68, (byte) 0xb5, (byte) 0xb9, (byte) 0xd6,
|
||||
(byte) 0xf7, (byte) 0xd8,0x67,0x46,0x45, (byte) 0x94,(byte) 0x89,(byte) 0xf8,(byte) 0x81,(byte) 0xd5,(byte) 0xf6,(byte) 0xb4,
|
||||
(byte) 0x88,(byte) 0xb1,0x2a,0x44,0x72,(byte) 0xd9,(byte) 0x87,0x66,(byte) 0xd4,(byte) 0xf5,0x3a,(byte) 0xa7,
|
||||
0x73,(byte) 0xa9,(byte) 0xa8,(byte) 0x86,0x62,(byte) 0xc7,0x65,(byte) 0xc8,(byte) 0xc9,(byte) 0xa1,(byte) 0xf4,(byte) 0xd1,
|
||||
(byte) 0xe9,0x5a,(byte) 0x92,(byte) 0x85,(byte) 0xa6,(byte) 0xe7,(byte) 0x93,(byte) 0xe8,(byte) 0xc1,(byte) 0xc6,0x7a,0x64,
|
||||
(byte) 0xe1,0x4a,0x6a,(byte) 0xe6,(byte) 0xb3,(byte) 0xf1,(byte) 0xd3,(byte) 0xa5,(byte) 0x8a,(byte) 0xb2,(byte) 0x9a,(byte) 0xba,
|
||||
(byte) 0x84,(byte) 0xa4,0x63,(byte) 0xe5,(byte) 0xc5,(byte) 0xf3,(byte) 0xd2,(byte) 0xc4,(byte) 0x82,(byte) 0xaa,(byte) 0xda,(byte) 0xe4,
|
||||
(byte) 0xf2,(byte) 0xca,(byte) 0x83,(byte) 0xa3,(byte) 0xa2,(byte) 0xc3,(byte) 0xea,(byte) 0xc2,(byte) 0xe2,(byte) 0xe3,(byte) 0xff,(byte) 0xff },
|
||||
|
||||
{ 0,2,2,1,4,1,4,1,3,3,1,0,0,0,0, (byte) 140,
|
||||
0x02,0x03,0x01,0x04,0x05,0x12,0x11,0x06,
|
||||
0x13,0x07,0x08,0x14,0x22,0x09,0x21,0x00,0x23,0x15,0x31,0x32,
|
||||
0x0a,0x16,(byte) 0xf0,0x24,0x33,0x41,0x42,0x19,0x17,0x25,0x18,0x51,
|
||||
0x34,0x43,0x52,0x29,0x35,0x61,0x39,0x71,0x62,0x36,0x53,0x26,
|
||||
0x38,0x1a,0x37,(byte) 0x81,0x27,(byte) 0x91,0x79,0x55,0x45,0x28,0x72,0x59,
|
||||
(byte) 0xa1,(byte) 0xb1,0x44,0x69,0x54,0x58,(byte) 0xd1,(byte) 0xfa,0x57,(byte) 0xe1,(byte) 0xf1,(byte) 0xb9,
|
||||
0x49,0x47,0x63,0x6a,(byte) 0xf9,0x56,0x46,(byte) 0xa8,0x2a,0x4a,0x78,(byte) 0x99,
|
||||
0x3a,0x75,0x74,(byte) 0x86,0x65,(byte) 0xc1,0x76,(byte) 0xb6,(byte) 0x96,(byte) 0xd6,(byte) 0x89,(byte) 0x85,
|
||||
(byte) 0xc9,(byte) 0xf5,(byte) 0x95,(byte) 0xb4,(byte) 0xc7,(byte) 0xf7,(byte) 0x8a,(byte) 0x97,(byte) 0xb8,0x73,(byte) 0xb7,(byte) 0xd8,
|
||||
(byte) 0xd9,(byte) 0x87,(byte) 0xa7,0x7a,0x48,(byte) 0x82,(byte) 0x84,(byte) 0xea,(byte) 0xf4,(byte) 0xa6,(byte) 0xc5,0x5a,
|
||||
(byte) 0x94,(byte) 0xa4,(byte) 0xc6,(byte) 0x92,(byte) 0xc3,0x68,(byte) 0xb5,(byte) 0xc8,(byte) 0xe4,(byte) 0xe5,(byte) 0xe6,(byte) 0xe9,
|
||||
(byte) 0xa2,(byte) 0xa3,(byte) 0xe3,(byte) 0xc2,0x66,0x67,(byte) 0x93,(byte) 0xaa,(byte) 0xd4,(byte) 0xd5,(byte) 0xe7,(byte) 0xf8,
|
||||
(byte) 0x88,(byte) 0x9a,(byte) 0xd7,0x77,(byte) 0xc4,0x64,(byte) 0xe2,(byte) 0x98,(byte) 0xa5,(byte) 0xca,(byte) 0xda,(byte) 0xe8,
|
||||
(byte) 0xf3,(byte) 0xf6,(byte) 0xa9,(byte) 0xb2,(byte) 0xb3,(byte) 0xf2,(byte) 0xd2,(byte) 0x83,(byte) 0xba,(byte) 0xd3,(byte) 0xff,(byte) 0xff },
|
||||
|
||||
{ 0,0,6,2,1,3,3,2,5,1,2,2,8,10,0,117,
|
||||
0x04,0x05,0x03,0x06,0x02,0x07,0x01,0x08,
|
||||
0x09,0x12,0x13,0x14,0x11,0x15,0x0a,0x16,0x17,(byte) 0xf0,0x00,0x22,
|
||||
0x21,0x18,0x23,0x19,0x24,0x32,0x31,0x25,0x33,0x38,0x37,0x34,
|
||||
0x35,0x36,0x39,0x79,0x57,0x58,0x59,0x28,0x56,0x78,0x27,0x41,
|
||||
0x29,0x77,0x26,0x42,0x76,(byte) 0x99,0x1a,0x55,(byte) 0x98,(byte) 0x97,(byte) 0xf9,0x48,
|
||||
0x54,(byte) 0x96,(byte) 0x89,0x47,(byte) 0xb7,0x49,(byte) 0xfa,0x75,0x68,(byte) 0xb6,0x67,0x69,
|
||||
(byte) 0xb9,(byte) 0xb8,(byte) 0xd8,0x52,(byte) 0xd7,(byte) 0x88,(byte) 0xb5,0x74,0x51,0x46,(byte) 0xd9,(byte) 0xf8,
|
||||
0x3a,(byte) 0xd6,(byte) 0x87,0x45,0x7a,(byte) 0x95,(byte) 0xd5,(byte) 0xf6,(byte) 0x86,(byte) 0xb4,(byte) 0xa9,(byte) 0x94,
|
||||
0x53,0x2a,(byte) 0xa8,0x43,(byte) 0xf5,(byte) 0xf7,(byte) 0xd4,0x66,(byte) 0xa7,0x5a,0x44,(byte) 0x8a,
|
||||
(byte) 0xc9,(byte) 0xe8,(byte) 0xc8,(byte) 0xe7,(byte) 0x9a,0x6a,0x73,0x4a,0x61,(byte) 0xc7,(byte) 0xf4,(byte) 0xc6,
|
||||
0x65,(byte) 0xe9,0x72,(byte) 0xe6,0x71,(byte) 0x91,(byte) 0x93,(byte) 0xa6,(byte) 0xda,(byte) 0x92,(byte) 0x85,0x62,
|
||||
(byte) 0xf3,(byte) 0xc5,(byte) 0xb2,(byte) 0xa4,(byte) 0x84,(byte) 0xba,0x64,(byte) 0xa5,(byte) 0xb3,(byte) 0xd2,(byte) 0x81,(byte) 0xe5,
|
||||
(byte) 0xd3,(byte) 0xaa,(byte) 0xc4,(byte) 0xca,(byte) 0xf2,(byte) 0xb1,(byte) 0xe4,(byte) 0xd1,(byte) 0x83,0x63,(byte) 0xea,(byte) 0xc3,
|
||||
(byte) 0xe2,(byte) 0x82,(byte) 0xf1,(byte) 0xa3,(byte) 0xc2,(byte) 0xa1,(byte) 0xc1,(byte) 0xe3,(byte) 0xa2,(byte) 0xe1,(byte) 0xff,(byte) 0xff }
|
||||
};
|
||||
|
||||
private void init_tables(int table) {
|
||||
if (table > 2) table = 2;
|
||||
// memset( first_decode, 0, sizeof first_decode);
|
||||
// memset(second_decode, 0, sizeof second_decode);
|
||||
// make_decoder( first_decode, first_tree[table], 0);
|
||||
// make_decoder(second_decode, second_tree[table], 0);
|
||||
|
||||
for (int i = 0; i < first_decode.length; i++) {
|
||||
first_decode[i] = new Decode();
|
||||
}
|
||||
for (int i = 0; i < second_decode.length; i++) {
|
||||
second_decode[i] = new Decode();
|
||||
}
|
||||
|
||||
make_decoder( first_decode, 0, first_tree[table], 0);
|
||||
make_decoder(second_decode, 0, second_tree[table], 0);
|
||||
}
|
||||
|
||||
//#if 0
|
||||
// writebits (int val, int nbits)
|
||||
// {
|
||||
// val <<= 32 - nbits;
|
||||
// while (nbits--) {
|
||||
// putchar(val & 0x80000000 ? '1':'0');
|
||||
// val <<= 1;
|
||||
// }
|
||||
// }
|
||||
//#endif
|
||||
|
||||
/*
|
||||
getbits(-1) initializes the buffer
|
||||
getbits(n) where 0 <= n <= 25 returns an n-bit integer
|
||||
*/
|
||||
private int bitbuf=0;
|
||||
private int vbits=0;
|
||||
|
||||
int getbits(int nbits) throws IOException {
|
||||
int c;
|
||||
|
||||
if (nbits == 0) return 0;
|
||||
|
||||
int ret;
|
||||
|
||||
if (nbits == -1) {
|
||||
ret = bitbuf = vbits = 0;
|
||||
}
|
||||
else {
|
||||
// ret = bitbuf << (32 - vbits) >> (32 - nbits);
|
||||
ret = bitbuf << (32 - vbits) >>> (32 - nbits);
|
||||
vbits -= nbits;
|
||||
}
|
||||
while (vbits < 25) {
|
||||
// c=fgetc(ifp);
|
||||
c=imageInput.readUnsignedByte();
|
||||
bitbuf = (bitbuf << 8) + c;
|
||||
// if (c == 0xff) fgetc(ifp); /* always extra 00 after ff */
|
||||
if (c == 0xff) {
|
||||
imageInput.readUnsignedByte(); // always extra 00 after ff
|
||||
}
|
||||
vbits += 8;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
short[] decode() throws IOException {
|
||||
short[] result = new short[width * height];
|
||||
|
||||
// struct Decode *decode, *dindex;
|
||||
Decode decode;
|
||||
Decode dindex;
|
||||
int i, j, leaf, len, diff, r;
|
||||
long save;
|
||||
int[] diffbuf = new int[64]; // h * w = 8 * 8 for each compressed block
|
||||
int carry=0, column=0;
|
||||
int[] base = new int[2];
|
||||
// unsigned short outbuf[64];
|
||||
short[] outbuf = new short[64];
|
||||
|
||||
int c;
|
||||
|
||||
boolean lowbits = canonHasLowbits();
|
||||
|
||||
init_tables(table);
|
||||
|
||||
// fseek (ifp, 540 + lowbits*height*width/4, SEEK_SET);
|
||||
// imageInput.seek(540 + (lowbits ? 1 : 0) * height * width / 4);
|
||||
imageInput.seek((lowbits ? 1 : 0) * height * width / 4); // NOTE: The original 540 offset is probably CIFF header length: 26 + DecoderTable[2]: 514
|
||||
getbits(-1); /* Prime the bit buffer */
|
||||
|
||||
while (column < width * height) {
|
||||
// memset(diffbuf,0,sizeof diffbuf);
|
||||
Arrays.fill(diffbuf, 0);
|
||||
|
||||
// decode = first_decode;
|
||||
decode = first_decode[0];
|
||||
for (i = 0; i < 64; i++) {
|
||||
|
||||
// for (dindex=decode; dindex->branch[0]; )
|
||||
// dindex = dindex->branch[getbits(1)];
|
||||
for (dindex = decode; dindex.branch[0] != null; ) {
|
||||
dindex = dindex.branch[getbits(1)];
|
||||
}
|
||||
|
||||
// leaf = dindex->leaf;
|
||||
leaf = dindex.leaf;
|
||||
// decode = second_decode;
|
||||
decode = second_decode[0];
|
||||
|
||||
if (leaf == 0 && i != 0) {
|
||||
break;
|
||||
}
|
||||
if (leaf == 0xff) {
|
||||
continue;
|
||||
}
|
||||
|
||||
i += (leaf >> 4);
|
||||
len = leaf & 15;
|
||||
|
||||
if (len == 0) {
|
||||
continue;
|
||||
}
|
||||
diff = getbits(len);
|
||||
if ((diff & (1 << (len - 1))) == 0) {
|
||||
diff -= (1 << len) - 1;
|
||||
}
|
||||
|
||||
if (i < 64) {
|
||||
diffbuf[i] = diff;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Ok ---
|
||||
|
||||
diffbuf[0] += carry;
|
||||
carry = diffbuf[0];
|
||||
|
||||
for (i = 0; i < 64; i++) {
|
||||
if (column++ % width == 0) {
|
||||
base[0] = base[1] = 512;
|
||||
}
|
||||
|
||||
// TODO: The original C code uses unsigned short, so this may overflow differently
|
||||
// outbuf[i] = ( base[i & 1] += diffbuf[i] );
|
||||
|
||||
outbuf[i] = (short) (base[i & 1] += diffbuf[i]);
|
||||
}
|
||||
|
||||
// -- Ok ---
|
||||
|
||||
if (lowbits) {
|
||||
// save = ftell(ifp);
|
||||
save = imageInput.getStreamPosition();
|
||||
// fseek (ifp, (column-64)/4 + 26, SEEK_SET);
|
||||
// imageInput.seek((column - 64) / 4 + 26);
|
||||
imageInput.seek((column - 64) / 4); // Note: The original 26 is CIFF header length (?)
|
||||
for (i = j = 0; j < 64 / 4; j++) {
|
||||
// c = fgetc(ifp);
|
||||
c = imageInput.readUnsignedByte();
|
||||
for (r = 0; r < 8; r += 2) {
|
||||
// TODO: Is this correct? The original C code is on one line, the Java equivalent then throws an AIOOBE...
|
||||
// outbuf[i++] = (outbuf[i] << 2) + ((c >> r) & 3);
|
||||
short sample = (short) ((outbuf[i] << 2) + ((c >> r) & 3));
|
||||
outbuf[i++] = sample;
|
||||
}
|
||||
}
|
||||
// fseek (ifp, save, SEEK_SET);
|
||||
imageInput.seek(save);
|
||||
}
|
||||
|
||||
// fwrite(outbuf,2,64,stdout);
|
||||
System.arraycopy(outbuf, 0, result, column - 64, 64);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+294
@@ -0,0 +1,294 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 "TwelveMonkeys" 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 OWNER 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.crw;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFFReader;
|
||||
import com.twelvemonkeys.imageio.plugins.crw.ciff.CIFF;
|
||||
import com.twelvemonkeys.imageio.plugins.crw.ciff.CIFFDirectory;
|
||||
import com.twelvemonkeys.imageio.plugins.crw.ciff.CIFFEntry;
|
||||
import com.twelvemonkeys.imageio.plugins.crw.ciff.CIFFReader;
|
||||
import com.twelvemonkeys.imageio.stream.BufferedImageInputStream;
|
||||
import com.twelvemonkeys.imageio.stream.SubImageInputStream;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Canon CRW RAW ImageReader.
|
||||
* <p/>
|
||||
*
|
||||
* @see <a href="http://cybercom.net/~dcoffin/dcraw/">Decoding raw digital photos in Linux</a>
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author CRW C reference decoder written by Dave Coffin.
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: CRWImageReader.java,v 1.0 07.04.14 21:31 haraldk Exp$
|
||||
*/
|
||||
public final class CRWImageReader extends ImageReaderBase {
|
||||
// TODO: Avoid duped code from TIFFImageReader, create a ExifRAWBaseImageReader something
|
||||
// TODO: Probably a good idea to move some of the getAsShort/Int/Long/Array to TIFF/EXIF metadata module
|
||||
// TODO: Automatic EXIF rotation, if we find a good way to do that for JPEG/EXIF/TIFF and keeping the metadata sane...
|
||||
|
||||
final static boolean DEBUG = true; //"true".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.imageio.plugins.crw.debug"));
|
||||
|
||||
// TODO: Thumbnail may or may not be present
|
||||
|
||||
private CIFFDirectory heap;
|
||||
private boolean hasJPEGPreview;
|
||||
private boolean hasRAWData;
|
||||
|
||||
CRWImageReader(final ImageReaderSpi provider) {
|
||||
super(provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resetMembers() {
|
||||
heap = null;
|
||||
hasJPEGPreview = false;
|
||||
hasRAWData = false;
|
||||
}
|
||||
|
||||
private void readMetadata() throws IOException {
|
||||
if (imageInput == null) {
|
||||
throw new IllegalStateException("input not set");
|
||||
}
|
||||
|
||||
if (heap == null) {
|
||||
heap = new CIFFReader().read(imageInput);
|
||||
|
||||
hasJPEGPreview = heap.getEntryById(CIFF.TAG_JPEG_PREVIEW) != null;
|
||||
hasRAWData = heap.getEntryById(CIFF.TAG_RAW_DATA) != null;
|
||||
|
||||
if (DEBUG) {
|
||||
System.err.println("directory: " + heap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumImages(final boolean allowSearch) throws IOException {
|
||||
readMetadata();
|
||||
|
||||
return (hasJPEGPreview ? 1 : 0) + (hasRAWData ? 1 : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumThumbnails(int imageIndex) throws IOException {
|
||||
readMetadata();
|
||||
checkBounds(imageIndex);
|
||||
|
||||
// TODO: Count thumbnails!
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean readerSupportsThumbnails() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getThumbnailWidth(int imageIndex, int thumbnailIndex) throws IOException {
|
||||
readMetadata();
|
||||
checkBounds(imageIndex);
|
||||
|
||||
// TODO: Need to get from JPEGImageReader (no ImageWidth tag), this is an ok (but lame) implementation for now
|
||||
return super.getThumbnailWidth(imageIndex, thumbnailIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getThumbnailHeight(int imageIndex, int thumbnailIndex) throws IOException {
|
||||
readMetadata();
|
||||
checkBounds(imageIndex);
|
||||
|
||||
// TODO: Need to get from JPEGImageReader (no ImageHeight tag), this is an ok (but lame) implementation for now
|
||||
return super.getThumbnailHeight(imageIndex, thumbnailIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedImage readThumbnail(int imageIndex, int thumbnailIndex) throws IOException {
|
||||
readMetadata();
|
||||
|
||||
if (imageIndex != 0) {
|
||||
throw new IndexOutOfBoundsException("No thumbnail for imageIndex: " + imageIndex);
|
||||
}
|
||||
if (thumbnailIndex >= getNumThumbnails(0)) {
|
||||
throw new IndexOutOfBoundsException("thumbnailIndex out of bounds: " + thumbnailIndex);
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException("readThumbnail");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth(int imageIndex) throws IOException {
|
||||
readMetadata();
|
||||
|
||||
if (imageIndex == 0 && hasJPEGPreview) {
|
||||
return getJPEGPreviewDimension().width;
|
||||
}
|
||||
|
||||
return getRawImageDimension().width;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight(int imageIndex) throws IOException {
|
||||
readMetadata();
|
||||
|
||||
if (imageIndex == 0 && hasJPEGPreview) {
|
||||
return getJPEGPreviewDimension().height;
|
||||
}
|
||||
|
||||
return getRawImageDimension().height;
|
||||
}
|
||||
|
||||
private Dimension getJPEGPreviewDimension() {
|
||||
// TODO: This is incorrect for the G1 sample, which stores the RAW size here...
|
||||
CIFFDirectory imageProperties = heap.getSubDirectory(CIFF.TAG_IMAGE_PROPERTIES);
|
||||
CIFFEntry imageSpec = imageProperties.getEntryById(CIFF.TAG_IMAGE_SPEC);
|
||||
int[] imageSpecValue = (int[]) imageSpec.getValue();
|
||||
|
||||
return new Dimension(imageSpecValue[0], imageSpecValue[1]);
|
||||
}
|
||||
|
||||
private Dimension getRawImageDimension() {
|
||||
CIFFDirectory exifInfo = getExifInfo();
|
||||
CIFFEntry sensorInfo = exifInfo.getEntryById(CIFF.TAG_SENSOR_INFO);
|
||||
|
||||
if (sensorInfo != null) {
|
||||
short[] sensorInfoValue = (short[]) sensorInfo.getValue();
|
||||
|
||||
return new Dimension(sensorInfoValue[1], sensorInfoValue[2]);
|
||||
}
|
||||
|
||||
// PowerShot Pro70 et al, don't have a JPEG preview and contains the dimensions in the ImageSpec tag
|
||||
return getJPEGPreviewDimension();
|
||||
}
|
||||
|
||||
private CIFFDirectory getExifInfo() {
|
||||
CIFFDirectory imageProperties = heap.getSubDirectory(CIFF.TAG_IMAGE_PROPERTIES);
|
||||
return imageProperties.getSubDirectory(CIFF.TAG_EXIF_INFORMATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IOException {
|
||||
readMetadata();
|
||||
|
||||
if (imageIndex == 0 && hasJPEGPreview) {
|
||||
BufferedImage image = readJPEGPreview();
|
||||
return singletonList(ImageTypeSpecifier.createFromRenderedImage(image)).iterator();
|
||||
}
|
||||
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedImage read(int imageIndex, ImageReadParam param) throws IOException {
|
||||
readMetadata();
|
||||
|
||||
if (imageIndex == 0 && hasJPEGPreview) {
|
||||
return readJPEGPreview();
|
||||
}
|
||||
|
||||
return readRAWData();
|
||||
}
|
||||
|
||||
private BufferedImage readJPEGPreview() throws IOException {
|
||||
CIFFEntry jpegPreview = heap.getEntryById(CIFF.TAG_JPEG_PREVIEW);
|
||||
|
||||
imageInput.seek(jpegPreview.offset());
|
||||
|
||||
return ImageIO.read(new SubImageInputStream(imageInput, jpegPreview.length()));
|
||||
}
|
||||
|
||||
private BufferedImage readRAWData() throws IOException {
|
||||
CIFFDirectory exifInfo = getExifInfo();
|
||||
CIFFEntry decoderTable = exifInfo.getEntryById(CIFF.TAG_DECODER_TABLE);
|
||||
int[] decoderTableValue = decoderTable != null ? (int[]) decoderTable.getValue() : null;
|
||||
int table = decoderTableValue != null ? decoderTableValue[0] : 0;
|
||||
long offset = decoderTableValue != null ? decoderTableValue[2] : 0;
|
||||
|
||||
Dimension size = getRawImageDimension();
|
||||
int width = size.width;
|
||||
int height = size.height;
|
||||
|
||||
// TODO: This is probably not the best way to get the bits/pixel, but seems to be correct (when it's there).
|
||||
// However,
|
||||
CIFFEntry whiteSample = exifInfo.getEntryById(CIFF.TAG_WHITE_SAMPLE);
|
||||
short[] whiteSampleValue = whiteSample != null ? (short[]) whiteSample.getValue() : null;
|
||||
short bitsPerSample = whiteSampleValue != null && whiteSample.length() > 5 ? whiteSampleValue[5] : 12;
|
||||
|
||||
CIFFEntry rawData = heap.getEntryById(CIFF.TAG_RAW_DATA);
|
||||
|
||||
imageInput.seek(rawData.offset() + offset);
|
||||
|
||||
ImageInputStream stream = new BufferedImageInputStream(new SubImageInputStream(imageInput, rawData.length()));
|
||||
|
||||
CRWDecoder decoder = new CRWDecoder(stream, width, height, table);
|
||||
short[] data = decoder.decode();
|
||||
|
||||
DataBuffer dataBuffer = new DataBufferUShort(data, data.length);
|
||||
WritableRaster raster = Raster.createInterleavedRaster(dataBuffer, width, height, width, 1, new int[] {0}, null);
|
||||
|
||||
ColorModel cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), new int[]{bitsPerSample}, false, false, Transparency.OPAQUE, dataBuffer.getDataType());
|
||||
|
||||
return new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
CRWImageReader reader = new CRWImageReader(new CRWImageReaderSpi());
|
||||
|
||||
for (String arg : args) {
|
||||
ImageInputStream stream = ImageIO.createImageInputStream(new File(arg));
|
||||
System.err.println("canDecode: " + reader.getOriginatingProvider().canDecodeInput(stream));
|
||||
|
||||
reader.setInput(stream);
|
||||
|
||||
int numImages = reader.getNumImages(true);
|
||||
for (int i = 0; i < numImages; i++) {
|
||||
int numThumbnails = reader.getNumThumbnails(i);
|
||||
for (int n = 0; n < numThumbnails; n++) {
|
||||
showIt(reader.readThumbnail(i, n), arg + " image " + i + " thumbnail " + n);
|
||||
}
|
||||
|
||||
showIt(reader.read(i), arg + " image " + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 "TwelveMonkeys" 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 OWNER 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.crw;
|
||||
|
||||
import static java.util.Arrays.copyOfRange;
|
||||
|
||||
import com.twelvemonkeys.imageio.plugins.crw.ciff.CIFF;
|
||||
import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
|
||||
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.spi.ServiceRegistry;
|
||||
|
||||
/**
|
||||
* CRWImageReaderSpi
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: CRWImageReaderSpi.java,v 1.0 07.04.14 21:26 haraldk Exp$
|
||||
*/
|
||||
public final class CRWImageReaderSpi extends ImageReaderSpiBase {
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public CRWImageReaderSpi() {
|
||||
super(new CRWProviderInfo());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRegistration(ServiceRegistry registry, Class<?> category) {
|
||||
// TODO: This code assumes that any TIFFImageReaderSpi is already installed... It may be installed at a later time. :-(
|
||||
// Make sure we're ordered before any TIFF reader
|
||||
Iterator<ImageReaderSpi> spis = registry.getServiceProviders(ImageReaderSpi.class, new ServiceRegistry.Filter() {
|
||||
@Override
|
||||
public boolean filter(Object provider) {
|
||||
return provider instanceof ImageReaderSpi && isTIFFReaderSpi((ImageReaderSpi) provider);
|
||||
}
|
||||
|
||||
private boolean isTIFFReaderSpi(final ImageReaderSpi provider) {
|
||||
String[] formatNames = provider.getFormatNames();
|
||||
for (String formatName : formatNames) {
|
||||
if (formatName.equalsIgnoreCase("TIFF")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}, true);
|
||||
|
||||
while (spis.hasNext()) {
|
||||
ImageReaderSpi spi = spis.next();
|
||||
registry.setOrdering(ImageReaderSpi.class, this, spi);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canDecodeInput(final Object pSource) throws IOException {
|
||||
if (!(pSource instanceof ImageInputStream)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ImageInputStream stream = (ImageInputStream) pSource;
|
||||
|
||||
stream.mark();
|
||||
try {
|
||||
byte[] bom = new byte[2];
|
||||
stream.readFully(bom);
|
||||
|
||||
ByteOrder originalOrder = stream.getByteOrder();
|
||||
|
||||
try {
|
||||
// CIFF byte order mark II (Intel) or MM (Motorola), just like TIFF
|
||||
if (bom[0] == 'I' && bom[1] == 'I') {
|
||||
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
}
|
||||
else if (bom[0] == 'M' && bom[1] == 'M') {
|
||||
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
||||
// CIFF header is always 26 bytes
|
||||
int size = stream.readInt();
|
||||
if (size != CIFF.HEADER_SIZE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// CRW uses type HEAP and subtype CCDR
|
||||
byte[] type = new byte[8];
|
||||
stream.readFully(type);
|
||||
|
||||
if (!Arrays.equals(CIFF.TYPE_HEAP, copyOfRange(type, 0, 4))
|
||||
|| !Arrays.equals(CIFF.SUBTYPE_CCDR, copyOfRange(type, 4, 8))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Version 1.2
|
||||
return stream.readUnsignedInt() == CIFF.VERSION_1_2;
|
||||
}
|
||||
finally {
|
||||
stream.setByteOrder(originalOrder);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
stream.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CRWImageReader createReaderInstance(Object extension) {
|
||||
return new CRWImageReader(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription(Locale locale) {
|
||||
return "Canon RAW (CRW) format Reader";
|
||||
}
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package com.twelvemonkeys.imageio.plugins.crw;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
||||
|
||||
/**
|
||||
* CRWProviderInfo
|
||||
*/
|
||||
final class CRWProviderInfo extends ReaderWriterProviderInfo {
|
||||
CRWProviderInfo() {
|
||||
super(
|
||||
CRWProviderInfo.class,
|
||||
new String[] {"crw", "CRW"},
|
||||
new String[] {"crw"},
|
||||
new String[] {
|
||||
"image/x-canon-raw", // TODO: Look up
|
||||
},
|
||||
"CRWImageReader",
|
||||
new String[] {"CRWImageReaderSpi"},
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
null, null,
|
||||
null, null,
|
||||
true,
|
||||
null, null,
|
||||
null, null
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oleg Ermolaev
|
||||
* 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 "TwelveMonkeys" 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 OWNER 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.crw;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* For CR2 RAW image.
|
||||
*
|
||||
* @author Oleg Ermolaev Date: 05.05.2018 2:04
|
||||
*/
|
||||
final class Slice {
|
||||
private final int firstWidthCount;
|
||||
private final int firstWidth;
|
||||
private final int lastWidth;
|
||||
|
||||
private Slice(int firstWidthCount, int firstWidth, int lastWidth) {
|
||||
this.firstWidthCount = firstWidthCount;
|
||||
this.firstWidth = firstWidth;
|
||||
this.lastWidth = lastWidth;
|
||||
}
|
||||
|
||||
static Slice createSlice(long[] values) throws IIOException {
|
||||
if (values == null || values.length != 3) {
|
||||
throw new IIOException("Unexpected slices array: " + Arrays.toString(values));
|
||||
}
|
||||
|
||||
long firstWidthCount = values[0];
|
||||
long firstWidth = values[1];
|
||||
long lastWidth = values[2];
|
||||
|
||||
if (!(0 < firstWidthCount && firstWidthCount <= Integer.MAX_VALUE) ||
|
||||
!(0 < firstWidth && firstWidth <= Integer.MAX_VALUE) ||
|
||||
!(0 < lastWidth && lastWidth <= Integer.MAX_VALUE) ||
|
||||
firstWidthCount * firstWidth + lastWidth > Integer.MAX_VALUE) {
|
||||
throw new IIOException("Unexpected slices array: " + Arrays.toString(values));
|
||||
}
|
||||
|
||||
return new Slice((int) firstWidthCount, (int) firstWidth, (int) lastWidth);
|
||||
}
|
||||
|
||||
private int getWidth() {
|
||||
return firstWidthCount * firstWidth + lastWidth;
|
||||
}
|
||||
|
||||
int[] unslice(int[][] data, int componentCount, int height) throws IIOException {
|
||||
final int width = getWidth();
|
||||
final int[] result = new int[width * height];
|
||||
|
||||
for (int componentIndex = 0; componentIndex < componentCount; componentIndex++) {
|
||||
if (result.length != data[componentIndex].length * componentCount) {
|
||||
throw new IIOException(String.format("Invalid array size for component #%d", componentIndex));
|
||||
}
|
||||
}
|
||||
|
||||
int position = 0;
|
||||
int currentWidth = firstWidth / componentCount;
|
||||
|
||||
for (int sliceIndex = 0; sliceIndex < firstWidthCount + 1; ++sliceIndex) {
|
||||
if (sliceIndex == firstWidthCount) {
|
||||
currentWidth = lastWidth / componentCount;
|
||||
}
|
||||
|
||||
final int sliceOffset = sliceIndex * firstWidth;
|
||||
for (int y = 0; y < height; ++y) {
|
||||
final int yOffset = y * width;
|
||||
|
||||
for (int x = 0; x < currentWidth; ++x) {
|
||||
final int xOffset = x * componentCount;
|
||||
|
||||
for (int componentIndex = 0; componentIndex < componentCount; componentIndex++) {
|
||||
result[sliceOffset + yOffset + xOffset + componentIndex] = data[componentIndex][position];
|
||||
}
|
||||
|
||||
position++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
package com.twelvemonkeys.imageio.plugins.crw.ciff;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* CIFF
|
||||
*
|
||||
* @see <a href="http://xyrion.org/ciff/CIFFspecV1R04.pdf">CIFF: Specification on Image Data File</a>
|
||||
*/
|
||||
public interface CIFF {
|
||||
|
||||
int HEADER_SIZE = 26;
|
||||
int VERSION_1_2 = 0x00010002;
|
||||
|
||||
byte[] TYPE_HEAP = "HEAP".getBytes(StandardCharsets.US_ASCII);
|
||||
|
||||
// According to CIFF spec, other subtypes are "JPGM", "TIFP" and "ARCH".
|
||||
byte[] SUBTYPE_ARCH = "ARCH".getBytes(StandardCharsets.US_ASCII);
|
||||
byte[] SUBTYPE_CCDR = "CCDR".getBytes(StandardCharsets.US_ASCII);
|
||||
byte[] SUBTYPE_JPGM = "JPGM".getBytes(StandardCharsets.US_ASCII);
|
||||
byte[] SUBTYPE_TIFP = "TIFP".getBytes(StandardCharsets.US_ASCII);
|
||||
|
||||
int STORAGE_HEAP = 0x0000; // kStg_InHeapSpace
|
||||
int STORAGE_RECORD = 0x4000; // kStg_InRecordEntry
|
||||
|
||||
int DATA_TYPE_BYTE = 0x0000;
|
||||
int DATA_TYPE_ASCII = 0x0800;
|
||||
int DATA_TYPE_WORD = 0x1000;
|
||||
int DATA_TYPE_DWORD = 0x1800;
|
||||
int DATA_TYPE_UNDEFINED = 0x2000; // kDT_BYTE2
|
||||
|
||||
int DATA_TYPE_HEAP_1 = 0x2800; // kDT_HeapTypeProperty1
|
||||
int DATA_TYPE_HEAP_2 = 0x3000; // kDT_HeapTypeProperty2
|
||||
|
||||
// From Spec
|
||||
int TAG_WILDCARD = 0xffff;
|
||||
int TAG_NULL = 0x0000; // null record
|
||||
int TAG_FREE = 0x0001; // free record
|
||||
int TAG_EX_USED = 0x0002; // special type for implementation purpose
|
||||
|
||||
int TAG_DESCRIPTION = DATA_TYPE_ASCII | 0x0005;
|
||||
int TAG_MODEL_NAME = DATA_TYPE_ASCII | 0x000a;
|
||||
int TAG_FIRMWARE_VERSION = DATA_TYPE_ASCII | 0x000b;
|
||||
int TAG_COMPONENT_VERSION = DATA_TYPE_ASCII | 0x000c;
|
||||
int TAG_ROM_OPERATION_MODE = DATA_TYPE_ASCII | 0x000d;
|
||||
int TAG_OWNER_NAME = DATA_TYPE_ASCII | 0x0010;
|
||||
int TAG_IMAGE_FILE_NAME = DATA_TYPE_ASCII | 0x0016;
|
||||
int TAG_THUMBNAIL_FILE_NAME = DATA_TYPE_ASCII | 0x0017;
|
||||
int TAG_TARGET_IMAGE_TYPE = DATA_TYPE_WORD | 0x000a;
|
||||
int TAG_SR_RELEASE_METHOD = DATA_TYPE_WORD | 0x0010;
|
||||
int TAG_SR_RELEASE_TIMING = DATA_TYPE_WORD | 0x0011;
|
||||
int TAG_RELEASE_SETTING = DATA_TYPE_WORD | 0x0016;
|
||||
int TAG_BODY_SENSITIVITY = DATA_TYPE_WORD | 0x001c;
|
||||
int TAG_IMAGE_FORMAT = DATA_TYPE_DWORD | 0x0003;
|
||||
int TAG_RECORD_ID = DATA_TYPE_DWORD | 0x0004;
|
||||
int TAG_SELF_TIMER_TIME = DATA_TYPE_DWORD | 0x0006;
|
||||
int TAG_SR_TARGET_DISTANCE_SETTING = DATA_TYPE_DWORD | 0x0007;
|
||||
int TAG_BODY_ID = DATA_TYPE_DWORD | 0x000b;
|
||||
int TAG_CAPTURED_TIME = DATA_TYPE_DWORD | 0x000e;
|
||||
int TAG_IMAGE_SPEC = DATA_TYPE_DWORD | 0x0010;
|
||||
int TAG_SR_EF = DATA_TYPE_DWORD | 0x0013;
|
||||
int TAG_MI_EV = DATA_TYPE_DWORD | 0x0014;
|
||||
int TAG_SERIAL_NUMBER = DATA_TYPE_DWORD | 0x0017;
|
||||
int TAG_SR_EXPOSURE = DATA_TYPE_DWORD | 0x0018;
|
||||
int TAG_CAMERA_OBJECT = 0x0007 | DATA_TYPE_HEAP_1;
|
||||
int TAG_SHOOTING_RECORD = 0x0002 | DATA_TYPE_HEAP_2;
|
||||
int TAG_MEASURED_INFO = 0x0003 | DATA_TYPE_HEAP_2;
|
||||
int TAG_CAMERA_SPECIFICATiON = 0x0004 | DATA_TYPE_HEAP_2; // kTC_CameraSpecificaiton
|
||||
|
||||
// From Phil Harvey's ExifTool (https://sno.phy.queensu.ca/~phil/exiftool/canon_raw.html)
|
||||
|
||||
// 0x0006 - 0x300b - 8 -
|
||||
int TAG_CANON_COLOR_INFO1 = DATA_TYPE_BYTE | 0x0032; // - Subdir: 0x300b, Exif: -
|
||||
// 0x0036 - 0x300b ? varies -
|
||||
// 0x003f - 0x300b ? 5120 -
|
||||
// 0x0040 - 0x300b ? 256 -
|
||||
// 0x0041 - 0x300b ? 256 -
|
||||
|
||||
// ASCII Strings
|
||||
// int TAG_CANON_FILE_DESCRIPTION = 0x0805; // - Subdir: 0x2804, Exif: -
|
||||
// int TAG_USER_COMMENT = DATA_TYPE_ASCII | 0x0005; // - Subdir: 0x300a, Exif: -
|
||||
// int TAG_CANON_RAW_MAKE_MODEL = DATA_TYPE_ASCII | 0x080a; // - Subdir: 0x2807, Exif: -
|
||||
// int TAG_CANON_FIRMWARE_VERSION = DATA_TYPE_ASCII | 0x080b; // 32 Firmware version. eg) "Firmware Version 1.1.1" - Subdir: 0x3004, Exif: 0x07
|
||||
// int TAG_COMPONENT_VERSION = 0x080c; // ?
|
||||
// int TAG_ROM_OPERATION_MODE = 0x080d; // 4 eg) The string "USA" for 300D's sold in North America - Subdir: 0x3004, Exif: -
|
||||
// int TAG_OWNER_NAME = 0x0810; // 32 Owner's name. eg) "Phil Harvey" - Subdir: 0x2807, Exif: 0x09
|
||||
int TAG_CANON_IMAGE_TYPE = DATA_TYPE_ASCII | 0x0815; // 32 Type of file. eg) "CRW:EOS DIGITAL REBEL CMOS RAW" - Subdir: 0x2804, Exif: 0x06
|
||||
// int TAG_ORIGINAL_FILE_NAME = 0x0816; // 32 Original file name. eg) "CRW_1834.CRW" - Subdir: 0x300a, Exif: -
|
||||
// int TAG_THUMBNAIL_FILE_NAME = 0x0817; // 32 Thumbnail file name. eg) "CRW_1834.THM" - Subdir: 0x300a, Exif: -
|
||||
|
||||
// SHORT (2-Byte Alignmnt)
|
||||
// int TAG_TARGET_IMAGE_TYPE = 0x100a; // 2 0=real-world subject, 1=written document - Subdir: 0x300a, Exif: -
|
||||
// int TAG_SHUTTER_RELEASE_METHOD = 0x1010; // 2 0=single shot, 1=continuous shooting - Subdir: 0x3002, Exif: -
|
||||
// int TAG_SHUTTER_RELEASE_TIMING = 0x1011; // 2 0=priority on shutter, 1=priority on focus - Subdir: 0x3002, Exif: -
|
||||
// 0x1014 - 0x3002 - 8 -
|
||||
// int TAG_RELEASE_SETTING = 0x1016; // 2 - - Subdir: 0x3002, Exif: -
|
||||
int TAG_BASE_ISO = DATA_TYPE_WORD | 0x101c; // 2 The camera body's base ISO sensitivity - Subdir: 0x3004, Exif: -
|
||||
// 0x1026 - 0x300a - 6 -
|
||||
int TAG_CANON_FLASH_INFO = DATA_TYPE_WORD | 0x1028; // 8 Unknown information, flash related - Subdir: 0x300b, Exif: 0x03
|
||||
int TAG_FOCAL_LENGTH = DATA_TYPE_WORD | 0x1029; // 8 Four 16 bit integers: 0) unknown, 1) focal length in mm, 2-3) sensor width and height in units of 1/1000 inch - Subdir: 0x300b, Exif: 0x02
|
||||
int TAG_CANON_SHOT_INFO = DATA_TYPE_WORD | 0x102a; // varies Data block giving shot information - Subdir: 0x300b, Exif: 0x04
|
||||
int TAG_CANON_COLOR_INFO2 = DATA_TYPE_WORD | 0x102c; // 256 Data block of color information (format unknown) - Subdir: 0x300b, Exif: -
|
||||
int TAG_CANON_CAMERA_SETTINGS = DATA_TYPE_WORD | 0x102d; // varies Data block giving camera settings - Subdir: 0x300b, Exif: 0x01
|
||||
int TAG_WHITE_SAMPLE = DATA_TYPE_WORD | 0x1030; // 102 or 118 White sample information with encrypted 8x8 sample data - Subdir: 0x300b, Exif: -
|
||||
int TAG_SENSOR_INFO = DATA_TYPE_WORD | 0x1031; // 34 Sensor size and resolution information - Subdir: 0x300b, Exif: -
|
||||
int TAG_CANON_CUSTOM_FUNCTIONS = DATA_TYPE_WORD | 0x1033; // varies Data block giving Canon custom settings - Subdir: 0x300b, Exif: 0x0f
|
||||
int TAG_CANON_AF_INFO = DATA_TYPE_WORD | 0x1038; // varies Data block giving AF-specific information - Subdir: 0x300b, Exif: 0x12
|
||||
// 0x1039 0x13 0x300b ? 8 -
|
||||
// 0x103c - 0x300b ? 156 -
|
||||
// 0x107f - 0x300b - varies -
|
||||
int TAG_CANON_FILE_INFO = DATA_TYPE_WORD | 0x1093; // 18 Data block giving file-specific information - Subdir: 0x300b, Exif: 0x93
|
||||
// 0x10a8 0xa8 0x300b ? 20 -
|
||||
int TAG_COLOR_BALANCE = DATA_TYPE_WORD | 0x10a9; // 82 Table of 16-bit integers. The first integer (like many other data blocks) is the number of bytes in the record. This is followed by red, green1, green2 and blue levels for WhiteBalance settings: auto, daylight, shade, cloudy, tungsten, fluorescent, flash, custom and kelvin. The final 4 entries appear to be some sort of baseline red, green1, green2 and blue levels. - Subdir: 0x300b, Exif: 0xa9
|
||||
// 0x10aa 0xaa 0x300b ? 10 -
|
||||
// 0x10ad - 0x300b ? 62 -
|
||||
int TAG_COLOR_TEMPERATURE = DATA_TYPE_WORD | 0x10ae; // 2 16-bit integer giving the color temperature - Subdir: 0x300b, Exif: 0xae
|
||||
// 0x10af - 0x300b ? 2 -
|
||||
int TAG_COLOR_SPACE = DATA_TYPE_WORD | 0x10b4; // 2 16-bit integer specifying the color space (1=sRGB, 2=Adobe RGB, 0xffff=uncalibrated) - Subdir: 0x300b, Exif: 0xb4
|
||||
int TAG_RAW_JPEG_INFO = DATA_TYPE_WORD | 0x10b5; // 10 Data block giving embedded JPG information - Subdir: 0x300b, Exif: 0xb5
|
||||
// 0x10c0 0xc0 0x300b ? 26 -
|
||||
// 0x10c1 0xc1 0x300b ? 26 -
|
||||
// 0x10c2 - 0x300b ? 884 -
|
||||
|
||||
// LONG (4-Byte Alignment)
|
||||
// int TAG_IMAGE_FORMAT = 0x1803; // 8 32-bit integer specifying image format (0x20001 for CRW), followed by 32-bit float giving target compression ratio - Subdir: 0x300a, Exif: -
|
||||
// int TAG_RECORD_ID = 0x1804; // 4 The number of pictures taken since the camera was manufactured - Subdir: 0x300a, Exif: -
|
||||
// 0x1805 - 0x3002 - 8 -
|
||||
// int TAG_SELF_TIMER_TIME = 0x1806; // 4 32-bit integer giving self-timer time in milliseconds - Subdir: 0x3002, Exif: -
|
||||
// int TAG_TARGET_DISTANCE_SETTING = 0x1807; // 4 32-bit float giving target distance in mm - Subdir: 0x3002, Exif: -
|
||||
// int TAG_SERIAL_NUMBER = 0x180b; // 4 The camera body number for EOS models. eg) 00560012345 - Subdir: 0x3004, Exif: 0x0c
|
||||
int TAG_TIME_STAMP = DATA_TYPE_DWORD | 0x180e; // 12 32-bit integer giving the time in seconds when the picture was taken, followed by a 32-bit timezone in seconds - Subdir: 0x300a, Exif: -
|
||||
int TAG_IMAGE_INFO = DATA_TYPE_DWORD | 0x1810; // 28 Data block containing image information, including rotation - Subdir: 0x300a, Exif: -
|
||||
// 0x1812 - 0x3004 - 40 -
|
||||
int TAG_FLASH_INFO = DATA_TYPE_DWORD | 0x1813; // 8 Two 32-bit floats: The flash guide number and the flash threshold - Subdir: 0x3002, Exif: -
|
||||
int TAG_MEASURED_EV = DATA_TYPE_DWORD | 0x1814; // 4 32-bit float giving the measured EV - Subdir: 0x3003, Exif: -
|
||||
int TAG_FILE_NUMBER = DATA_TYPE_DWORD | 0x1817; // 4 32-bit integer giving the number of this file. eg) 1181834 - Subdir: 0x300a, Exif: 0x08
|
||||
// int TAG_EXPOSURE_INFO = DATA_TYPE_DWORD | 0x1818; // 12 Three 32-bit floats: Exposure compensation, Tv, Av - Subdir: 0x3002, Exif: -
|
||||
// 0x1819 - 0x300b - 64 -
|
||||
int TAG_CANON_MODEL_ID = DATA_TYPE_DWORD | 0x1834; // 4 Unsigned 32-bit integer giving unique model ID - Subdir: 0x300b, Exif: 0x10
|
||||
int TAG_DECODER_TABLE = DATA_TYPE_DWORD | 0x1835; // 16 RAW decoder table information - Subdir: 0x300b, Exif: -
|
||||
int TAG_SERIAL_NUMBER_FORMAT = DATA_TYPE_DWORD | 0x183b; // 4 32-bit integer (0x90000000=format 1, 0xa0000000=format 2) - Subdir: 0x300b, Exif: 0x15
|
||||
|
||||
// UNDEFINED (Mixed Data Records)
|
||||
int TAG_RAW_DATA = DATA_TYPE_UNDEFINED | 0x2005; // The raw data itself (the bulk of the CRW file) - Subdir: root
|
||||
int TAG_JPEG_PREVIEW = DATA_TYPE_UNDEFINED | 0x2007; // The embedded JPEG image (2048x1360 pixels for the 300D with Canon firmware) - Subdir: root
|
||||
int TAG_THUMBNAIL = DATA_TYPE_UNDEFINED | 0x2008; // Thumbnail image (JPEG, 160x120 pixels) - Subdir: root
|
||||
|
||||
// SubDirectory Blocks
|
||||
int TAG_IMAGE_DESCRIPTION = DATA_TYPE_HEAP_1 | 0x2804; // The image description subdirectory - Subdir: 0x300a
|
||||
// int TAG_CAMERA_OBJECT = 0x2807; // The camera object subdirectory - Subdir: 0x300a
|
||||
// int TAG_SHOOTING_RECORD = 0x3002; // The shooting record subdirectory - Subdir: 0x300a
|
||||
// int TAG_MEASURED_INFO = 0x3003; // The measured information subdirectory - Subdir: 0x300a
|
||||
int TAG_CAMERA_SPECIFICATION = DATA_TYPE_HEAP_2 | 0x3004; // The camera specification subdirectory - Subdir: 0x2807
|
||||
|
||||
int TAG_IMAGE_PROPERTIES = DATA_TYPE_HEAP_2 | 0x300a; // The main subdirectory containing all meta information - Subdir: root
|
||||
int TAG_EXIF_INFORMATION = DATA_TYPE_HEAP_2 | 0x300b; // The subdirectory containing most of the JPEG/TIFF Exif information - Subdir: 0x300a
|
||||
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.twelvemonkeys.imageio.plugins.crw.ciff;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.twelvemonkeys.imageio.metadata.AbstractDirectory;
|
||||
|
||||
/**
|
||||
* CIFFDirectory
|
||||
*/
|
||||
public final class CIFFDirectory extends AbstractDirectory {
|
||||
|
||||
CIFFDirectory(Collection<CIFFEntry> entries) {
|
||||
super(entries);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CIFFEntry getEntryById(Object identifier) {
|
||||
return (CIFFEntry) super.getEntryById(identifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CIFFEntry getEntryByFieldName(String fieldName) {
|
||||
return (CIFFEntry) super.getEntryByFieldName(fieldName);
|
||||
}
|
||||
|
||||
public CIFFDirectory getSubDirectory(int tagId) {
|
||||
CIFFEntry entry = getEntryById(tagId);
|
||||
return entry == null ? null : (CIFFDirectory) entry.getValue();
|
||||
}
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
package com.twelvemonkeys.imageio.plugins.crw.ciff;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import com.twelvemonkeys.imageio.metadata.AbstractEntry;
|
||||
import com.twelvemonkeys.lang.StringUtil;
|
||||
|
||||
/**
|
||||
* CIFFEntry
|
||||
*/
|
||||
public final class CIFFEntry extends AbstractEntry {
|
||||
|
||||
private final long offset;
|
||||
private final long length;
|
||||
|
||||
/**
|
||||
* Creates a CIFFEntry for "in-record" storage.
|
||||
*
|
||||
* @param identifier
|
||||
* @param value
|
||||
*/
|
||||
CIFFEntry(int identifier, Object value) {
|
||||
super(identifier, value);
|
||||
this.offset = -1;
|
||||
this.length = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a CIFFEntry for "in-heap" storage.
|
||||
*
|
||||
* @param identifier
|
||||
* @param value
|
||||
*/
|
||||
CIFFEntry(int identifier, Object value, long offset, long length) {
|
||||
super(identifier, value);
|
||||
this.offset = offset;
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
int tagId() {
|
||||
return (int) getIdentifier();
|
||||
}
|
||||
|
||||
boolean isHeapStorage() {
|
||||
return offset != -1 && length != -1;
|
||||
}
|
||||
|
||||
public long offset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
public long length() {
|
||||
return length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFieldName() {
|
||||
switch ((Integer) getIdentifier()) {
|
||||
case CIFF.TAG_RAW_DATA:
|
||||
return "RawData";
|
||||
case CIFF.TAG_JPEG_PREVIEW:
|
||||
return "JPEGPreview";
|
||||
case CIFF.TAG_THUMBNAIL:
|
||||
return "Thumbnail";
|
||||
case CIFF.TAG_IMAGE_PROPERTIES:
|
||||
return "ImageProperties";
|
||||
case CIFF.TAG_EXIF_INFORMATION:
|
||||
return "ExifInformation";
|
||||
default:
|
||||
Field[] fields = CIFF.class.getFields();
|
||||
|
||||
for (Field field : fields) {
|
||||
try {
|
||||
if (field.getType() == Integer.TYPE && field.getName().startsWith("TAG_")) {
|
||||
if (field.get(null).equals(getIdentifier())) {
|
||||
return StringUtil.lispToCamel(field.getName().substring(4).replace("_", "-").toLowerCase(), true);
|
||||
}
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
// Should never happen, but in case, abort
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected String getNativeIdentifier() {
|
||||
return String.format("0x%04x", (Integer) getIdentifier());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeName() {
|
||||
int dataType = tagId() & 0x3800;
|
||||
|
||||
switch (dataType) {
|
||||
case CIFF.DATA_TYPE_BYTE:
|
||||
return "BYTE";
|
||||
case CIFF.DATA_TYPE_ASCII:
|
||||
return "ASCII";
|
||||
case CIFF.DATA_TYPE_WORD:
|
||||
return "SHORT";
|
||||
case CIFF.DATA_TYPE_DWORD:
|
||||
return "LONG";
|
||||
case CIFF.DATA_TYPE_UNDEFINED:
|
||||
return "UNDEFINED";
|
||||
case CIFF.DATA_TYPE_HEAP_1:
|
||||
case CIFF.DATA_TYPE_HEAP_2:
|
||||
return super.getTypeName();
|
||||
default:
|
||||
throw new IllegalStateException(String.format("Unsupported data type: 0x%04x", dataType));
|
||||
}
|
||||
}
|
||||
}
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
package com.twelvemonkeys.imageio.plugins.crw.ciff;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
|
||||
import com.twelvemonkeys.imageio.metadata.MetadataReader;
|
||||
|
||||
/**
|
||||
* CIFFReader
|
||||
*/
|
||||
public final class CIFFReader extends MetadataReader {
|
||||
|
||||
@Override
|
||||
public CIFFDirectory read(ImageInputStream input) throws IOException {
|
||||
long start = readCIFFHeader(input);
|
||||
|
||||
// Spec: "No information regarding the length of the heap is given within the actual heap data structure itself"
|
||||
return readHeap(input, start, input.length() - start); // TODO: If length is unknown, we'll have to search for it...
|
||||
}
|
||||
|
||||
private int readCIFFHeader(ImageInputStream input) throws IOException {
|
||||
byte[] bom = new byte[2];
|
||||
input.readFully(bom);
|
||||
|
||||
// CIFF byte order mark II (Intel) or MM (Motorola), just like TIFF
|
||||
if (bom[0] == 'I' && bom[1] == 'I') {
|
||||
input.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
}
|
||||
else if (bom[0] == 'M' && bom[1] == 'M') {
|
||||
input.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
}
|
||||
else {
|
||||
throw new IIOException("No CIFF byte order mark found, expected 'II' or 'MM'");
|
||||
}
|
||||
|
||||
int size = input.readInt();
|
||||
if (size != CIFF.HEADER_SIZE) {
|
||||
// TODO: Other sizes?
|
||||
throw new IIOException(String.format("Unexpected CIFF header size, expected %d: %d ", CIFF.HEADER_SIZE, size));
|
||||
}
|
||||
|
||||
byte[] typeInfo = new byte[8];
|
||||
input.readFully(typeInfo);
|
||||
|
||||
byte[] type = Arrays.copyOfRange(typeInfo, 0, 4);
|
||||
if (!Arrays.equals(CIFF.TYPE_HEAP, type)) {
|
||||
throw new IIOException(String.format("Unexpected CIFF type, expected 'HEAP': '%s'", new String(type, StandardCharsets.US_ASCII)));
|
||||
}
|
||||
|
||||
byte[] subtype = Arrays.copyOfRange(typeInfo, 4, 8);
|
||||
if (!(Arrays.equals(CIFF.SUBTYPE_ARCH, subtype) || Arrays.equals(CIFF.SUBTYPE_CCDR, subtype)
|
||||
|| Arrays.equals(CIFF.SUBTYPE_JPGM, subtype) ||Arrays.equals(CIFF.SUBTYPE_TIFP, subtype))) {
|
||||
throw new IIOException(String.format("Unsupported CIFF subtype, expected 'ARCH', 'CCDR', 'JPGM' or 'TIFP': '%s'",
|
||||
new String(subtype, StandardCharsets.US_ASCII)));
|
||||
}
|
||||
|
||||
// Version 1.2
|
||||
long version = input.readUnsignedInt();
|
||||
if (version != CIFF.VERSION_1_2) {
|
||||
throw new IIOException(String.format("Unsupported CIFF version, expected 1.2: %d.%d", version >> 16, version & 0xffff));
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
private CIFFDirectory readHeap(ImageInputStream input, long heapOffset, long heapLength) throws IOException {
|
||||
input.seek(heapOffset + heapLength - 4);
|
||||
long offsetTableOffset = input.readUnsignedInt();
|
||||
|
||||
return readOffsetTable(input, heapOffset, offsetTableOffset + heapOffset);
|
||||
}
|
||||
|
||||
private CIFFDirectory readOffsetTable(ImageInputStream input, long heapOffset, long tableOffset) throws IOException {
|
||||
input.seek(tableOffset);
|
||||
|
||||
// DC_UINT16 numRecords;/* the number tblArray elements */
|
||||
// DC_RECORD_ENTRY tblArray[1];/* Array of the record entries */
|
||||
int count = input.readUnsignedShort(); // 0 entries is allowed
|
||||
CIFFEntry[] entries = new CIFFEntry[count];
|
||||
|
||||
// TYPECODE typeCode;/* type code of the record */
|
||||
// DC_UINT32 length;/* record length */
|
||||
// DC_UINT32 offset;/* offset of the record in the heap*/
|
||||
for (int i = 0; i < count; i++) {
|
||||
short typeCode = input.readShort();
|
||||
|
||||
long entryLength = input.readUnsignedInt();
|
||||
long entryOffset = input.readUnsignedInt();
|
||||
|
||||
int recordType = typeCode & 0xc000;
|
||||
int dataType = typeCode & 0x3800;
|
||||
int idCode = typeCode & 0x7ff;
|
||||
|
||||
// typeIdCode = dataType + idCode
|
||||
int typeIdCode = typeCode & 0x3fff;
|
||||
|
||||
|
||||
if (recordType == CIFF.STORAGE_RECORD) {
|
||||
Object value = null;
|
||||
// TODO: Even for these records, we need to know the data structure for each tag... :-P
|
||||
switch (dataType) {
|
||||
case CIFF.DATA_TYPE_BYTE:
|
||||
value = (byte) entryLength & 0xff;
|
||||
break;
|
||||
case CIFF.DATA_TYPE_ASCII:
|
||||
ByteBuffer buffer = ByteBuffer.allocate(8);
|
||||
buffer.putInt((int) entryLength);
|
||||
buffer.putInt((int) entryOffset);
|
||||
|
||||
value = toNullTerminatedStrings(buffer.array());
|
||||
break;
|
||||
case CIFF.DATA_TYPE_WORD:
|
||||
value = (short) entryLength & 0xffff;
|
||||
break;
|
||||
case CIFF.DATA_TYPE_DWORD:
|
||||
value = (int) entryLength;
|
||||
break;
|
||||
case CIFF.DATA_TYPE_UNDEFINED:
|
||||
value = entryLength << 32L | entryOffset;
|
||||
break;
|
||||
}
|
||||
|
||||
entries[i] = new CIFFEntry(typeIdCode, value);
|
||||
}
|
||||
else {
|
||||
entries[i] = new CIFFEntry(typeIdCode, new long[] {heapOffset + entryOffset, entryLength}, heapOffset + entryOffset, entryLength);
|
||||
}
|
||||
}
|
||||
|
||||
// Fill inn sub entries
|
||||
input.mark();
|
||||
try {
|
||||
for (int i = 0; i < count; i++) {
|
||||
CIFFEntry entry = entries[i];
|
||||
int dataType = entry.tagId() & 0x3800;
|
||||
|
||||
Object value;
|
||||
if (entry.isHeapStorage()) {
|
||||
switch (dataType) {
|
||||
case CIFF.DATA_TYPE_HEAP_1:
|
||||
case CIFF.DATA_TYPE_HEAP_2:
|
||||
value = readHeap(input, entry.offset(), entry.length());
|
||||
break;
|
||||
|
||||
case CIFF.DATA_TYPE_BYTE:
|
||||
case CIFF.DATA_TYPE_UNDEFINED:
|
||||
case CIFF.DATA_TYPE_ASCII:
|
||||
byte[] bytes = new byte[(int) entry.length()];
|
||||
input.seek(entry.offset());
|
||||
input.readFully(bytes);
|
||||
value = dataType == CIFF.DATA_TYPE_ASCII ? toNullTerminatedStrings(bytes) : bytes;
|
||||
break;
|
||||
|
||||
case CIFF.DATA_TYPE_WORD:
|
||||
short[] shorts = new short[(int) (entry.length() / 2)];
|
||||
input.seek(entry.offset());
|
||||
input.readFully(shorts, 0, shorts.length);
|
||||
value = shorts;
|
||||
break;
|
||||
|
||||
case CIFF.DATA_TYPE_DWORD:
|
||||
int[] ints = new int[(int) (entry.length() / 4)];
|
||||
input.seek(entry.offset());
|
||||
input.readFully(ints, 0, ints.length);
|
||||
value = ints;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new IIOException(String.format("Unsupported data type: 0x%04x", dataType));
|
||||
}
|
||||
|
||||
entries[i] = new CIFFEntry(entry.tagId(), value, entry.offset(), entry.length());
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
input.reset();
|
||||
}
|
||||
|
||||
return new CIFFDirectory(asList(entries));
|
||||
}
|
||||
|
||||
private String[] toNullTerminatedStrings(byte[] buffer) {
|
||||
int len = buffer.length;
|
||||
|
||||
while (len > 0 && buffer[len - 1] == 0) {
|
||||
len--;
|
||||
}
|
||||
|
||||
return new String(buffer, 0, len, StandardCharsets.US_ASCII).split("\u0000");
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
com.twelvemonkeys.imageio.plugins.crw.CRWImageReaderSpi
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 "TwelveMonkeys" 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 OWNER 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.crw;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import java.awt.*;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* CRWImageReaderTest
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: CRWImageReaderTest.java,v 1.0 07.04.14 21:52 haraldk Exp$
|
||||
*/
|
||||
@Ignore
|
||||
public class CRWImageReaderTest extends ImageReaderAbstractTest<CRWImageReader> {
|
||||
@Override
|
||||
protected List<TestData> getTestData() {
|
||||
return Arrays.asList(
|
||||
new TestData(getClassLoaderResource("/crw/RAW_CANON_G1.CRW"), new Dimension(640, 480)), // Canon G1
|
||||
new TestData(getClassLoaderResource("/crw/RAW_CANON_300D.CRW"), new Dimension(3888, 2592)) // Canon EOS 300D
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ImageReaderSpi createProvider() {
|
||||
return new CRWImageReaderSpi();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getFormatNames() {
|
||||
return Arrays.asList("crw");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getSuffixes() {
|
||||
return Arrays.asList("crw");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getMIMETypes() {
|
||||
return Arrays.asList("image/x-canon-raw");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Known issue: Subsampled reading not supported")
|
||||
@Override
|
||||
public void testReadWithSubsampleParamPixels() throws IOException {
|
||||
super.testReadWithSubsampleParamPixels();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Known issue: Source region reading not supported")
|
||||
@Override
|
||||
public void testReadWithSourceRegionParamEqualImage() throws IOException {
|
||||
super.testReadWithSourceRegionParamEqualImage();
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2014, 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 "TwelveMonkeys" 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 OWNER 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.
|
||||
-->
|
||||
|
||||
<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/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>imageio</artifactId>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>imageio-dng</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: DNG plugin</name>
|
||||
<description>ImageIO plugin for Adobe Digital Negative and TIFF/EP (DNG).</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
<classifier>tests</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-metadata</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<version>${project.version}</version>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-jpeg</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,127 @@
|
||||
package com.twelvemonkeys.imageio.plugins.dng;
|
||||
|
||||
/**
|
||||
* DNG
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: DNG.java,v 1.0 03.10.14 10:49 haraldk Exp$
|
||||
*/
|
||||
interface DNG {
|
||||
// TODO: Some (all?) of these tags are defined by TIFF/EP, should we reflect that in package/class names?
|
||||
|
||||
/** CFA (Color Filter Array). */
|
||||
int PHOTOMETRIC_CFA = 32803;
|
||||
/** LinearRaw. */
|
||||
int PHOTOMETRIC_LINEAR_RAW = 34892;
|
||||
|
||||
/**
|
||||
* Lossy JPEG.
|
||||
* <p/>
|
||||
* Lossy JPEG (34892) is allowed for IFDs that use PhotometricInterpretation = 34892
|
||||
* (LinearRaw) and 8-bit integer data. This new compression code is required to let the DNG
|
||||
* reader know to use a lossy JPEG decoder rather than a lossless JPEG decoder for this
|
||||
* combination of PhotometricInterpretation and BitsPerSample.
|
||||
*/
|
||||
int COMPRESSION_LOSSY_JPEG = 34892;
|
||||
|
||||
/**
|
||||
* CFARepeatPatternDim
|
||||
* <p/>
|
||||
* This tag encodes the number of pixels horizontally and vertically that are needed to uniquely define the repeat
|
||||
* pattern of the color filter array (CFA) pattern used in the color image sensor. It is mandatory when
|
||||
* PhotometricInterpretation = 32803, and there are no defaults allowed. It is optional when
|
||||
* PhotometricInterpretation = 2 or 6 and SensingMethod = 2, where it can be used to indicate the original sensor
|
||||
* sampling positions.
|
||||
*/
|
||||
int TAG_CFA_REPEAT_PATTERN_DIM = 33421;
|
||||
/**
|
||||
* Indicates the color filter array (CFA) geometric pattern of the image sensor
|
||||
* when a one-chip color area sensor is used.
|
||||
* NOTE: This tag (defined in TIFF/EP) is different from the CFAPattern defined in normal TIFF.
|
||||
*/
|
||||
int TAG_CFA_PATTERN = 33422;
|
||||
|
||||
/** Indicates the image sensor type on the camera or input device. */
|
||||
int TAG_SENSING_METHOD = 37399;
|
||||
|
||||
// From http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/cfapattern.html
|
||||
byte CFA_PATTERN_RED = 0;
|
||||
byte CFA_PATTERN_GREEN = 1;
|
||||
byte CFA_PATTERN_BLUE = 2;
|
||||
byte CFA_PATTERN_CYAN = 3;
|
||||
byte CFA_PATTERN_MAGENTA = 4;
|
||||
byte CFA_PATTERN_YELLOW = 5;
|
||||
byte CFA_PATTERN_WHITE = 6; // ???? Should be KEY?
|
||||
|
||||
/**
|
||||
* This tag encodes the DNG four-tier version number. For files compliant with this version of
|
||||
* the DNG specification (1.4.0.0), this tag should contain the bytes: 1, 4, 0, 0.
|
||||
*/
|
||||
int TAG_DNG_VERSION = 50706;
|
||||
int TAG_DNG_BACKWARD_VERSION = 50707;
|
||||
|
||||
/** UniqueCameraModel defines a unique, non-localized name for the camera model that created the image in the raw file. */
|
||||
int TAG_UNIQUE_CAMERA_MODEL = 50708;
|
||||
int TAG_LOCALIZED_CAMERA_MODEL = 50709;
|
||||
|
||||
/** CFA plane to RGB mapping (default: [0, 1, 2]). */
|
||||
int TAG_CFA_PLANE_COLOR = 50710;
|
||||
/** CFA spatial layout (default: 1). */
|
||||
int TAG_CFA_LAYOUT = 50711;
|
||||
|
||||
/** 1 = Rectangular (or square) layout. */
|
||||
int CFA_LAYOUT_RECTANGULAR = 1;
|
||||
/** 2 = Staggered layout A: even columns are offset down by 1/2 row. */
|
||||
int CFA_LAYOUT_STAGGERED_A = 2;
|
||||
/** 3 = Staggered layout B: even columns are offset up by 1/2 row. */
|
||||
int CFA_LAYOUT_STAGGERED_B = 3;
|
||||
/** 4 = Staggered layout C: even rows are offset right by 1/2 column. */
|
||||
int CFA_LAYOUT_STAGGERED_C = 4;
|
||||
/** 5 = Staggered layout D: even rows are offset left by 1/2 column. */
|
||||
int CFA_LAYOUT_STAGGERED_D = 5;
|
||||
/** 6 = Staggered layout E: even rows are offset up by 1/2 row, even columns are offset left by 1/2 column. */
|
||||
int CFA_LAYOUT_STAGGERED_E = 6;
|
||||
/** 7 = Staggered layout F: even rows are offset up by 1/2 row, even columns are offset right by 1/2 column. */
|
||||
int CFA_LAYOUT_STAGGERED_F = 7;
|
||||
/** 8 = Staggered layout G: even rows are offset down by 1/2 row, even columns are offset left by 1/2 column. */
|
||||
int CFA_LAYOUT_STAGGERED_G = 8;
|
||||
/** 9 = Staggered layout H: even rows are offset down by 1/2 row, even columns are offset right by 1/2 column. */
|
||||
int CFA_LAYOUT_STAGGERED_H = 9;
|
||||
|
||||
/** LinearizationTable describes a lookup table that maps stored values into linear values. */
|
||||
int TAG_LINEARIZATION_TABLE = 50712;
|
||||
|
||||
/** This tag specifies repeat pattern size for the BlackLevel tag. Default: [1, 1]. */
|
||||
int TAG_BLACK_LEVEL_REPEAT_DIM = 50713;
|
||||
|
||||
/**
|
||||
* This tag specifies the zero light (a.k.a. thermal black or black current) encoding level,
|
||||
* as a repeating pattern. Default: 0.
|
||||
*/
|
||||
int TAG_BLACK_LEVEL = 50714;
|
||||
|
||||
// TODO: Rest of DNG tags.
|
||||
// ...
|
||||
|
||||
/**
|
||||
* Horizontal Difference X2.
|
||||
* Same as Horizontal Difference except the pixel two to the left is used rather than the pixel one to the left.
|
||||
*/
|
||||
int PREDICTOR_HORIZONTAL_X2 = 34892;
|
||||
/**
|
||||
* Horizontal Difference X4.
|
||||
* Same as Horizontal Difference except the pixel four to the left is used rather than the pixel one to the left.
|
||||
*/
|
||||
int PREDICTOR_HORIZONTAL_X4 = 34893;
|
||||
/**
|
||||
* Floating Point X2.
|
||||
* Same as Floating Point except the pixel two to the left is used rather than the pixel one to the left.
|
||||
*/
|
||||
int PREDICTOR_FLOATINGPOINT_X2 = 34894;
|
||||
/**
|
||||
* Floating Point X4.
|
||||
* Same as Floating Point except the pixel four to the left is used rather than the pixel one to the left
|
||||
*/
|
||||
int PREDICTOR_FLOATINGPOINT_X4 = 34895;
|
||||
}
|
||||
+1051
File diff suppressed because it is too large
Load Diff
+104
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 "TwelveMonkeys" 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 OWNER 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.dng;
|
||||
|
||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
|
||||
import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
|
||||
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* CR2ImageReaderSpi
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: CR2ImageReaderSpi.java,v 1.0 07.04.14 21:26 haraldk Exp$
|
||||
*/
|
||||
public final class DNGImageReaderSpi extends ImageReaderSpiBase {
|
||||
public DNGImageReaderSpi() {
|
||||
super(new DNGProviderInfo());
|
||||
}
|
||||
|
||||
public boolean canDecodeInput(final Object pSource) throws IOException {
|
||||
if (!(pSource instanceof ImageInputStream)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ImageInputStream stream = (ImageInputStream) pSource;
|
||||
|
||||
stream.mark();
|
||||
try {
|
||||
byte[] bom = new byte[2];
|
||||
stream.readFully(bom);
|
||||
|
||||
ByteOrder originalOrder = stream.getByteOrder();
|
||||
|
||||
try {
|
||||
if (bom[0] == 'I' && bom[1] == 'I') {
|
||||
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
}
|
||||
else if (bom[0] == 'M' && bom[1] == 'M') {
|
||||
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
||||
int tiffMagic = stream.readUnsignedShort();
|
||||
if (tiffMagic != TIFF.TIFF_MAGIC) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: This is not different from a normal TIFF...
|
||||
|
||||
return true;
|
||||
}
|
||||
finally {
|
||||
stream.setByteOrder(originalOrder);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
stream.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageReader createReaderInstance(Object extension) throws IOException {
|
||||
return new DNGImageReader(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription(Locale locale) {
|
||||
return "Adobe Digital Negative (DNG) format Reader";
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oleg Ermolaev
|
||||
* 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 "TwelveMonkeys" 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 OWNER 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.dng;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
||||
|
||||
/**
|
||||
* @author Oleg Ermolaev Date: 04.05.2018 1:50
|
||||
*/
|
||||
class DNGProviderInfo extends ReaderWriterProviderInfo {
|
||||
protected DNGProviderInfo() {
|
||||
super(
|
||||
DNGProviderInfo.class,
|
||||
new String[]{"dng", "NDG"},
|
||||
new String[]{"dng"},
|
||||
new String[]{
|
||||
"image/x-adobe-dng", // TODO: Look up
|
||||
},
|
||||
"com.twelvemonkeys.imageio.plugins.dng.DNGImageReader",
|
||||
new String[]{"com.twelvemonkeys.imageio.plugins.dng.DNGImageReaderSpi"},
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 "TwelveMonkeys" 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 OWNER 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.dng;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import java.awt.*;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* CR2ImageReaderTest
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: CR2ImageReaderTest.java,v 1.0 07.04.14 21:52 haraldk Exp$
|
||||
*/
|
||||
@Ignore
|
||||
public class DNGImageReaderTest extends ImageReaderAbstractTest<DNGImageReader> {
|
||||
@Override
|
||||
protected List<TestData> getTestData() {
|
||||
return Arrays.asList(
|
||||
new TestData(getClassLoaderResource("/dng/L1004220.DNG"),
|
||||
// Uncompressed RGB (thumbnail), Ucompressed CFA
|
||||
// new Dimension(320, 216),
|
||||
new Dimension(5216, 3472)),
|
||||
new TestData(getClassLoaderResource("/dng/IMG_2224.dng"),
|
||||
// Uncompressed RGB (thumbnail), JPEG Lossless CFA, JPEG DCT YCbCr
|
||||
// new Dimension(256, 171),
|
||||
new Dimension(3516, 2328),
|
||||
new Dimension(1024, 683))
|
||||
// new TestData(getClassLoaderResource("/dng/test.dng"), new Dimension(2, 2)) // Only JPEG Lossless CFA
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ImageReaderSpi createProvider() {
|
||||
return new DNGImageReaderSpi();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getFormatNames() {
|
||||
return Arrays.asList("dng");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getSuffixes() {
|
||||
return Arrays.asList("dng");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getMIMETypes() {
|
||||
return Arrays.asList("image/x-dng");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Known issue: Subsampled reading not supported")
|
||||
@Override
|
||||
public void testReadWithSubsampleParamPixels() throws IOException {
|
||||
super.testReadWithSubsampleParamPixels();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Known issue: Source region reading not supported")
|
||||
@Override
|
||||
public void testReadWithSourceRegionParamEqualImage() throws IOException {
|
||||
super.testReadWithSourceRegionParamEqualImage();
|
||||
}
|
||||
}
|
||||
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -16,11 +16,23 @@
|
||||
<project.jpms.module.name>com.twelvemonkeys.imageio.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>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
|
||||
+1
-1
@@ -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
|
||||
);
|
||||
|
||||
@@ -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>
|
||||
@@ -16,6 +16,18 @@
|
||||
<project.jpms.module.name>com.twelvemonkeys.imageio.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>
|
||||
|
||||
+3
-5
@@ -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
|
||||
);
|
||||
|
||||
@@ -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>
|
||||
|
||||
+16
@@ -86,6 +86,22 @@ final class JPEGLosslessDecoderWrapper {
|
||||
int width = decoder.getDimX();
|
||||
int height = decoder.getDimY();
|
||||
|
||||
if (SliceContext.isPresent()) { //QnD
|
||||
final int[][] unsliced = new int[1][];
|
||||
final int componentCount = decoder.getNumComponents();
|
||||
final Slice slice = SliceContext.get();
|
||||
unsliced[0] = slice.unslice(decoded, componentCount, height);
|
||||
switch (decoder.getPrecision()) {
|
||||
case 8:
|
||||
return to8Bit1ComponentGrayScale(unsliced, width * componentCount, height);
|
||||
case 10:
|
||||
case 12:
|
||||
case 14:
|
||||
case 16:
|
||||
return to16Bit1ComponentGrayScale(unsliced, decoder.getPrecision(), width * componentCount, height);
|
||||
}
|
||||
}
|
||||
|
||||
// Single component, assumed to be Gray
|
||||
if (decoder.getNumComponents() == 1) {
|
||||
switch (decoder.getPrecision()) {
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oleg Ermolaev
|
||||
* 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 "TwelveMonkeys" 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 OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.jpeg;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* For CR2 RAW image.
|
||||
*
|
||||
* @author Oleg Ermolaev Date: 05.05.2018 2:04
|
||||
*/
|
||||
public class Slice {
|
||||
public static final int FIRST_WIDTH_COUNT_INDEX = 0;
|
||||
public static final int FIRST_WIDTH_INDEX = 1;
|
||||
public static final int LAST_WIDTH_INDEX = 2;
|
||||
|
||||
private final int firstWidthCount;
|
||||
private final int firstWidth;
|
||||
private final int lastWidth;
|
||||
|
||||
public Slice(int firstWidthCount, int firstWidth, int lastWidth) {
|
||||
this.firstWidthCount = firstWidthCount;
|
||||
this.firstWidth = firstWidth;
|
||||
this.lastWidth = lastWidth;
|
||||
}
|
||||
|
||||
public static Slice createSlice(long[] values) throws IIOException {
|
||||
if (values == null || values.length != 3) {
|
||||
throw new IIOException("Unexpected slices array: " + Arrays.toString(values));
|
||||
}
|
||||
final long firstWidthCount = values[FIRST_WIDTH_COUNT_INDEX];
|
||||
final long firstWidth = values[FIRST_WIDTH_INDEX];
|
||||
final long lastWidth = values[LAST_WIDTH_INDEX];
|
||||
if (!(0 < firstWidthCount && firstWidthCount <= Integer.MAX_VALUE) ||
|
||||
!(0 < firstWidth && firstWidth <= Integer.MAX_VALUE) ||
|
||||
!(0 < lastWidth && lastWidth <= Integer.MAX_VALUE) ||
|
||||
firstWidthCount * firstWidth + lastWidth > Integer.MAX_VALUE) {
|
||||
throw new IIOException("Unexpected slices array: " + Arrays.toString(values));
|
||||
}
|
||||
return new Slice((int) firstWidthCount, (int) firstWidth, (int) lastWidth);
|
||||
}
|
||||
|
||||
public int getFirstWidthCount() {
|
||||
return firstWidthCount;
|
||||
}
|
||||
|
||||
public int getFirstWidth() {
|
||||
return firstWidth;
|
||||
}
|
||||
|
||||
public int getLastWidth() {
|
||||
return lastWidth;
|
||||
}
|
||||
|
||||
private int getWidth() {
|
||||
return firstWidthCount * firstWidth + lastWidth;
|
||||
}
|
||||
|
||||
public int[] unslice(int[][] data, int componentCount, int height) throws IIOException {
|
||||
final int width = getWidth();
|
||||
final int[] result = new int[width * height];
|
||||
|
||||
for (int componentIndex = 0; componentIndex < componentCount; componentIndex++) {
|
||||
if (result.length != data[componentIndex].length * componentCount) {
|
||||
throw new IIOException(String.format("Invalid array size for component #%d", componentIndex));
|
||||
}
|
||||
}
|
||||
|
||||
int position = 0;
|
||||
int currentWidth = firstWidth / componentCount;
|
||||
for (int sliceIndex = 0; sliceIndex < firstWidthCount + 1; ++sliceIndex) {
|
||||
if (sliceIndex == firstWidthCount) {
|
||||
currentWidth = lastWidth / componentCount;
|
||||
}
|
||||
final int sliceOffset = sliceIndex * firstWidth;
|
||||
for (int y = 0; y < height; ++y) {
|
||||
final int yOffset = y * width;
|
||||
for (int x = 0; x < currentWidth; ++x) {
|
||||
final int xOffset = x * componentCount;
|
||||
for (int componentIndex = 0; componentIndex < componentCount; componentIndex++) {
|
||||
result[sliceOffset + yOffset + xOffset + componentIndex] = data[componentIndex][position];
|
||||
}
|
||||
position++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oleg Ermolaev
|
||||
* 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 "TwelveMonkeys" 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 OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.jpeg;
|
||||
|
||||
/**
|
||||
* QnD
|
||||
*
|
||||
* @author Oleg Ermolaev Date: 05.05.2018 2:13
|
||||
*/
|
||||
public class SliceContext {
|
||||
private static final ThreadLocal<Slice> CONTEXT = new ThreadLocal<>();
|
||||
|
||||
public static boolean isPresent() {
|
||||
return get() != null;
|
||||
}
|
||||
|
||||
public static Slice get() {
|
||||
return CONTEXT.get();
|
||||
}
|
||||
|
||||
public static void set(Slice slice) {
|
||||
CONTEXT.set(slice);
|
||||
}
|
||||
|
||||
public static void remove() {
|
||||
CONTEXT.remove();
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
+1
-1
@@ -60,7 +60,7 @@ public final class TIFFWriter extends MetadataWriter {
|
||||
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 {
|
||||
public boolean write(final Collection<? extends Entry> entries, final ImageOutputStream stream) throws IOException {
|
||||
return write(new IFD(entries), stream);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2014, 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 "TwelveMonkeys" 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 OWNER 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.
|
||||
-->
|
||||
|
||||
<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/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>imageio</artifactId>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>imageio-nef</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: NEF plugin</name>
|
||||
<description>ImageIO plugin for Nikon Electronic File (NEF).</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
<classifier>tests</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-metadata</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<version>${project.version}</version>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-jpeg</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
+764
@@ -0,0 +1,764 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 "TwelveMonkeys" 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 OWNER 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.nef;
|
||||
|
||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||
import com.twelvemonkeys.imageio.metadata.CompoundDirectory;
|
||||
import com.twelvemonkeys.imageio.metadata.Directory;
|
||||
import com.twelvemonkeys.imageio.metadata.Entry;
|
||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
|
||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFFReader;
|
||||
import com.twelvemonkeys.imageio.stream.SubImageInputStream;
|
||||
|
||||
import javax.imageio.*;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.*;
|
||||
import java.io.DataInput;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.zip.Inflater;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
|
||||
/**
|
||||
* Nikon NEF RAW ImageReader
|
||||
* <p/>
|
||||
* Acknowledgement:
|
||||
* This ImageReader is based on the excellent work of Laurent Clevy, and would probably not exist without it.
|
||||
*
|
||||
* @see <a href="http://lclevy.free.fr/nef/">Nikon Electronic File (NEF) file format description</a>
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: NEFImageReader.java,v 1.0 07.04.14 21:31 haraldk Exp$
|
||||
*/
|
||||
public final class NEFImageReader extends ImageReaderBase {
|
||||
// See http://lclevy.free.fr/nef/
|
||||
// TODO: Avoid duped code from TIFFImageReader
|
||||
// TODO: Probably a good idea to move some of the getAsShort/Int/Long/Array to TIFF/EXIF metadata module
|
||||
// TODO: Automatic EXIF rotation, if we find a good way to do that for JPEG/EXIF/TIFF and keeping the metadata sane...
|
||||
|
||||
static final boolean DEBUG = true; //"true".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.imageio.plugins.nef.debug"));
|
||||
private static final ImageTypeSpecifier THUMB_SPEC = ImageTypeSpecifier.createInterleaved(ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[]{0, 1, 2}, DataBuffer.TYPE_BYTE, false, false);
|
||||
|
||||
// The Makernote has the NikonImagePreview tag (0x0011) which contains a thumbnail image (in lossy jpeg).
|
||||
// IFD#0 also contains a thumbnail image in uncompressed TIFF, size is 160x120.
|
||||
// Thumbnail is always in IFD0..
|
||||
private final static int THUMBNAIL_IFD = 0;
|
||||
|
||||
private CompoundDirectory IFDs;
|
||||
private List<Directory> subIFDs;
|
||||
private Directory currentIFD;
|
||||
|
||||
NEFImageReader(final ImageReaderSpi provider) {
|
||||
super(provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resetMembers() {
|
||||
IFDs = null;
|
||||
subIFDs = null;
|
||||
currentIFD = null;
|
||||
}
|
||||
|
||||
private void readMetadata() throws IOException {
|
||||
if (imageInput == null) {
|
||||
throw new IllegalStateException("input not set");
|
||||
}
|
||||
|
||||
if (IFDs == null) {
|
||||
imageInput.seek(0);
|
||||
|
||||
IFDs = (CompoundDirectory) new TIFFReader().read(imageInput); // NOTE: Sets byte order as a side effect
|
||||
|
||||
// Pull up the sub-ifds now
|
||||
Entry subIFDEntry = IFDs.getEntryById(TIFF.TAG_SUB_IFD);
|
||||
|
||||
if (subIFDEntry != null) {
|
||||
Object subIFD = subIFDEntry.getValue();
|
||||
|
||||
if (subIFD instanceof Directory) {
|
||||
subIFDs = Collections.singletonList((Directory) subIFD);
|
||||
}
|
||||
else {
|
||||
Directory[] directories = (Directory[]) subIFD;
|
||||
|
||||
if (directories.length != 2) {
|
||||
throw new IIOException("Unexpected number of SubIFDs in NEF: " + directories.length);
|
||||
}
|
||||
|
||||
subIFDs = Arrays.asList(directories);
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new IIOException("Unexpected number of SubIFDs in NEF: " + 0);
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
System.err.println("Byte order: " + imageInput.getByteOrder());
|
||||
System.err.println("Number of IFDs: " + IFDs.directoryCount());
|
||||
|
||||
for (int i = 0; i < IFDs.directoryCount(); i++) {
|
||||
System.err.printf("IFD %d: %s\n", i, IFDs.getDirectory(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void readIFD(final int ifdIndex) throws IOException {
|
||||
readMetadata();
|
||||
|
||||
if (ifdIndex < 0) {
|
||||
throw new IndexOutOfBoundsException("index < minIndex");
|
||||
}
|
||||
else {
|
||||
int numIFDs = IFDs.directoryCount() + subIFDs.size();
|
||||
|
||||
if (ifdIndex >= numIFDs) {
|
||||
throw new IndexOutOfBoundsException("index >= numIFDs (" + ifdIndex + " >= " + numIFDs + ")");
|
||||
}
|
||||
}
|
||||
|
||||
// Depth first (...but a DNG should only contain one IFD with subIFDs)
|
||||
if (ifdIndex == 0) {
|
||||
currentIFD = IFDs.getDirectory(ifdIndex);
|
||||
}
|
||||
else if (ifdIndex <= subIFDs.size()) {
|
||||
currentIFD = subIFDs.get(ifdIndex - 1);
|
||||
}
|
||||
else {
|
||||
currentIFD = IFDs.getDirectory(ifdIndex - subIFDs.size());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumImages(final boolean allowSearch) throws IOException {
|
||||
readMetadata();
|
||||
|
||||
return subIFDs.size(); // IFD0 is always thumbnail
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumThumbnails(int imageIndex) throws IOException {
|
||||
readMetadata();
|
||||
checkBounds(imageIndex);
|
||||
|
||||
return imageIndex == 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean readerSupportsThumbnails() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getThumbnailWidth(int imageIndex, int thumbnailIndex) throws IOException {
|
||||
readIFD(THUMBNAIL_IFD);
|
||||
|
||||
if (imageIndex != 0) {
|
||||
throw new IndexOutOfBoundsException("No thumbnail for imageIndex: " + imageIndex);
|
||||
}
|
||||
if (thumbnailIndex != 0) {
|
||||
throw new IndexOutOfBoundsException("thumbnailIndex out of bounds: " + thumbnailIndex);
|
||||
}
|
||||
|
||||
return getValueAsInt(TIFF.TAG_IMAGE_WIDTH, "ImageWidth");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getThumbnailHeight(int imageIndex, int thumbnailIndex) throws IOException {
|
||||
readIFD(THUMBNAIL_IFD);
|
||||
|
||||
if (imageIndex != 0) {
|
||||
throw new IndexOutOfBoundsException("No thumbnail for imageIndex: " + imageIndex);
|
||||
}
|
||||
if (thumbnailIndex != 0) {
|
||||
throw new IndexOutOfBoundsException("thumbnailIndex out of bounds: " + thumbnailIndex);
|
||||
}
|
||||
|
||||
return getValueAsInt(TIFF.TAG_IMAGE_HEIGHT, "ImageHeight");
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedImage readThumbnail(int imageIndex, int thumbnailIndex) throws IOException {
|
||||
readIFD(THUMBNAIL_IFD);
|
||||
|
||||
if (imageIndex != 0) {
|
||||
throw new IndexOutOfBoundsException("No thumbnail for imageIndex: " + imageIndex);
|
||||
}
|
||||
if (thumbnailIndex != 0) {
|
||||
throw new IndexOutOfBoundsException("thumbnailIndex out of bounds: " + thumbnailIndex);
|
||||
}
|
||||
|
||||
// Read uncompressed RGB
|
||||
int imageWidth = getValueAsInt(TIFF.TAG_IMAGE_WIDTH, "ImageWidth");
|
||||
int imageHeight = getValueAsInt(TIFF.TAG_IMAGE_HEIGHT, "ImageHeight");
|
||||
|
||||
// NEF thumbnail simplification: single strip
|
||||
long stripOffset = getValueAsLong(TIFF.TAG_STRIP_OFFSETS, "StripOffsets");
|
||||
long stripCount = getValueAsLong(TIFF.TAG_STRIP_BYTE_COUNTS, "StripByteCounts");
|
||||
|
||||
BufferedImage thumbnail = THUMB_SPEC.createBufferedImage(imageWidth, imageHeight);
|
||||
|
||||
WritableRaster raster = thumbnail.getRaster();
|
||||
DataBufferByte dataBuffer = (DataBufferByte) raster.getDataBuffer();
|
||||
|
||||
imageInput.seek(stripOffset);
|
||||
ImageInputStream stream = new SubImageInputStream(imageInput, stripCount);
|
||||
|
||||
try {
|
||||
stream.readFully(dataBuffer.getData());
|
||||
}
|
||||
finally {
|
||||
stream.close();
|
||||
}
|
||||
|
||||
return thumbnail;
|
||||
}
|
||||
|
||||
private long[] getValueAsLongArray(final int tag, final String tagName, boolean required) throws IIOException {
|
||||
Entry entry = currentIFD.getEntryById(tag);
|
||||
if (entry == null) {
|
||||
if (required) {
|
||||
throw new IIOException("Missing TIFF tag " + tagName);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
long[] value;
|
||||
|
||||
if (entry.valueCount() == 1) {
|
||||
// For single entries, this will be a boxed type
|
||||
value = new long[] {((Number) entry.getValue()).longValue()};
|
||||
}
|
||||
else if (entry.getValue() instanceof short[]) {
|
||||
short[] shorts = (short[]) entry.getValue();
|
||||
value = new long[shorts.length];
|
||||
|
||||
for (int i = 0, length = value.length; i < length; i++) {
|
||||
value[i] = shorts[i];
|
||||
}
|
||||
}
|
||||
else if (entry.getValue() instanceof int[]) {
|
||||
int[] ints = (int[]) entry.getValue();
|
||||
value = new long[ints.length];
|
||||
|
||||
for (int i = 0, length = value.length; i < length; i++) {
|
||||
value[i] = ints[i];
|
||||
}
|
||||
}
|
||||
else if (entry.getValue() instanceof long[]) {
|
||||
value = (long[]) entry.getValue();
|
||||
}
|
||||
else {
|
||||
throw new IIOException(String.format("Unsupported %s type: %s (%s)", tagName, entry.getTypeName(), entry.getValue().getClass()));
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private Number getValueAsNumberWithDefault(final int tag, final String tagName, final Number defaultValue) throws IIOException {
|
||||
Entry entry = currentIFD.getEntryById(tag);
|
||||
|
||||
if (entry == null) {
|
||||
if (defaultValue != null) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
throw new IIOException("Missing TIFF tag: " + (tagName != null ? tagName : tag));
|
||||
}
|
||||
|
||||
return (Number) entry.getValue();
|
||||
}
|
||||
|
||||
private long getValueAsLongWithDefault(final int tag, final String tagName, final Long defaultValue) throws IIOException {
|
||||
return getValueAsNumberWithDefault(tag, tagName, defaultValue).longValue();
|
||||
}
|
||||
|
||||
private long getValueAsLongWithDefault(final int tag, final Long defaultValue) throws IIOException {
|
||||
return getValueAsLongWithDefault(tag, null, defaultValue);
|
||||
}
|
||||
|
||||
private long getValueAsLong(final int tag, String tagName) throws IIOException {
|
||||
return getValueAsLongWithDefault(tag, tagName, null);
|
||||
}
|
||||
|
||||
private int getValueAsIntWithDefault(final int tag, final String tagName, final Integer defaultValue) throws IIOException {
|
||||
return getValueAsNumberWithDefault(tag, tagName, defaultValue).intValue();
|
||||
}
|
||||
|
||||
private int getValueAsIntWithDefault(final int tag, Integer defaultValue) throws IIOException {
|
||||
return getValueAsIntWithDefault(tag, null, defaultValue);
|
||||
}
|
||||
|
||||
private int getValueAsInt(final int tag, String tagName) throws IIOException {
|
||||
return getValueAsIntWithDefault(tag, tagName, null);
|
||||
}
|
||||
|
||||
private int imageIndexToIFDNumber(int imageIndex) {
|
||||
return imageIndex >= THUMBNAIL_IFD ? imageIndex + 1 : imageIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth(int imageIndex) throws IOException {
|
||||
readIFD(imageIndexToIFDNumber(imageIndex));
|
||||
|
||||
return getValueAsInt(TIFF.TAG_IMAGE_WIDTH, "ImageWidth");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight(int imageIndex) throws IOException {
|
||||
readIFD(imageIndexToIFDNumber(imageIndex));
|
||||
|
||||
return getValueAsInt(TIFF.TAG_IMAGE_HEIGHT, "ImageHeight");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IOException {
|
||||
readIFD(imageIndexToIFDNumber(imageIndex));
|
||||
|
||||
int photometricInterpretation = getValueAsInt(TIFF.TAG_PHOTOMETRIC_INTERPRETATION, "PhotometricInterpretation");
|
||||
long[] bitsPerSample = getValueAsLongArray(TIFF.TAG_BITS_PER_SAMPLE, "BitsPerSample", true);
|
||||
int bitDepth = (int) bitsPerSample[0]; // Assume all equal!
|
||||
|
||||
ColorSpace cs;
|
||||
|
||||
if (photometricInterpretation == 2) {
|
||||
cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
|
||||
}
|
||||
else {
|
||||
cs = ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB);
|
||||
}
|
||||
|
||||
if (bitDepth == 8) {
|
||||
return Arrays.asList(ImageTypeSpecifier.createInterleaved(cs, new int [] {0, 1, 2}, DataBuffer.TYPE_BYTE, false, false)).iterator();
|
||||
}
|
||||
else if (bitDepth > 8 && bitDepth <= 16) {
|
||||
return Arrays.asList(ImageTypeSpecifier.createInterleaved(cs, new int [] {0, 1, 2}, DataBuffer.TYPE_USHORT, false, false)).iterator();
|
||||
}
|
||||
|
||||
throw new IIOException("Unsupported bit depth: " + bitDepth);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedImage read(int imageIndex, ImageReadParam param) throws IOException {
|
||||
readIFD(imageIndexToIFDNumber(imageIndex));
|
||||
|
||||
int compression = getValueAsInt(TIFF.TAG_COMPRESSION, "Compression");
|
||||
int width;
|
||||
int height;
|
||||
|
||||
ImageInputStream stream;
|
||||
switch (compression) {
|
||||
case 1: // Uncompressed
|
||||
width = getValueAsInt(TIFF.TAG_IMAGE_WIDTH, "ImageWidth");
|
||||
height = getValueAsInt(TIFF.TAG_IMAGE_HEIGHT, "ImageHeight");
|
||||
|
||||
// TODO: Read as uncompressed TIFF (share code with TIFFImageReader?)
|
||||
// TODO: Remove duped code!!
|
||||
BufferedImage destination = getDestination(param, getImageTypes(imageIndex), width, height);
|
||||
|
||||
// NOTE: We handle strips as tiles of tileWidth == width by tileHeight == rowsPerStrip
|
||||
// Strips are top/down, tiles are left/right, top/down
|
||||
int stripTileWidth = width;
|
||||
long rowsPerStrip = getValueAsLongWithDefault(TIFF.TAG_ROWS_PER_STRIP, (1l << 32) - 1);
|
||||
int stripTileHeight = rowsPerStrip < height ? (int) rowsPerStrip : height;
|
||||
|
||||
long[] stripTileOffsets = getValueAsLongArray(TIFF.TAG_TILE_OFFSETS, "TileOffsets", false);
|
||||
long[] stripTileByteCounts;
|
||||
|
||||
if (stripTileOffsets != null) {
|
||||
stripTileByteCounts = getValueAsLongArray(TIFF.TAG_TILE_BYTE_COUNTS, "TileByteCounts", false);
|
||||
if (stripTileByteCounts == null) {
|
||||
processWarningOccurred("Missing TileByteCounts for tiled TIFF with compression: " + compression);
|
||||
}
|
||||
|
||||
stripTileWidth = getValueAsInt(TIFF.TAG_TILE_WIDTH, "TileWidth");
|
||||
stripTileHeight = getValueAsInt(TIFF.TAG_TILE_HEIGTH, "TileHeight");
|
||||
}
|
||||
else {
|
||||
stripTileOffsets = getValueAsLongArray(TIFF.TAG_STRIP_OFFSETS, "StripOffsets", true);
|
||||
stripTileByteCounts = getValueAsLongArray(TIFF.TAG_STRIP_BYTE_COUNTS, "StripByteCounts", false);
|
||||
if (stripTileByteCounts == null) {
|
||||
processWarningOccurred("Missing StripByteCounts for TIFF with compression: " + compression);
|
||||
}
|
||||
|
||||
// NOTE: This is really against the spec, but libTiff seems to handle it. TIFF 6.0 says:
|
||||
// "Do not use both strip- oriented and tile-oriented fields in the same TIFF file".
|
||||
stripTileWidth = getValueAsIntWithDefault(TIFF.TAG_TILE_WIDTH, "TileWidth", stripTileWidth);
|
||||
stripTileHeight = getValueAsIntWithDefault(TIFF.TAG_TILE_HEIGTH, "TileHeight", stripTileHeight);
|
||||
}
|
||||
|
||||
int tilesAcross = (width + stripTileWidth - 1) / stripTileWidth;
|
||||
int tilesDown = (height + stripTileHeight - 1) / stripTileHeight;
|
||||
|
||||
|
||||
|
||||
ImageTypeSpecifier rawType = getRawImageType(imageIndex);
|
||||
checkReadParamBandSettings(param, rawType.getNumBands(), destination.getSampleModel().getNumBands());
|
||||
|
||||
final Rectangle srcRegion = new Rectangle();
|
||||
final Rectangle dstRegion = new Rectangle();
|
||||
computeRegions(param, width, height, destination, srcRegion, dstRegion);
|
||||
|
||||
int xSub = param != null ? param.getSourceXSubsampling() : 1;
|
||||
int ySub = param != null ? param.getSourceYSubsampling() : 1;
|
||||
|
||||
WritableRaster destRaster = clipToRect(destination.getRaster(), dstRegion, param != null ? param.getDestinationBands() : null);
|
||||
|
||||
final int interpretation = getValueAsInt(TIFF.TAG_PHOTOMETRIC_INTERPRETATION, "PhotometricInterpretation");
|
||||
final int predictor = getValueAsIntWithDefault(TIFF.TAG_PREDICTOR, 1);
|
||||
// final int planarConfiguration = getValueAsIntWithDefault(TIFF.TAG_PLANAR_CONFIGURATION, TIFFBaseline.PLANARCONFIG_CHUNKY);
|
||||
// final int numBands = planarConfiguration == TIFFExtension.PLANARCONFIG_PLANAR ? 1 : rawType.getNumBands();
|
||||
final int numBands = rawType.getNumBands();
|
||||
|
||||
WritableRaster rowRaster = rawType.getColorModel().createCompatibleWritableRaster(stripTileWidth, 1);
|
||||
int row = 0;
|
||||
|
||||
// General uncompressed/compressed reading
|
||||
for (int y = 0; y < tilesDown; y++) {
|
||||
int col = 0;
|
||||
int rowsInTile = Math.min(stripTileHeight, height - row);
|
||||
|
||||
for (int x = 0; x < tilesAcross; x++) {
|
||||
int colsInTile = Math.min(stripTileWidth, width - col);
|
||||
int i = y * tilesAcross + x;
|
||||
|
||||
imageInput.seek(stripTileOffsets[i]);
|
||||
|
||||
// Read a full strip/tile
|
||||
Raster clippedRow = clipRowToRect(rowRaster, srcRegion,
|
||||
param != null ? param.getSourceBands() : null,
|
||||
param != null ? param.getSourceXSubsampling() : 1);
|
||||
readStripTileData(clippedRow, srcRegion, xSub, ySub, numBands, interpretation, destRaster, col, row, colsInTile, rowsInTile, imageInput);
|
||||
|
||||
if (abortRequested()) {
|
||||
break;
|
||||
}
|
||||
|
||||
col += colsInTile;
|
||||
}
|
||||
|
||||
processImageProgress(100f * row / height);
|
||||
|
||||
if (abortRequested()) {
|
||||
processReadAborted();
|
||||
break;
|
||||
}
|
||||
|
||||
row += rowsInTile;
|
||||
}
|
||||
return getDestination(param, getImageTypes(imageIndex), width, height);
|
||||
|
||||
case 6: // Old-style JPEG
|
||||
long jpegFormatStart = getValueAsLong(TIFF.TAG_JPEG_INTERCHANGE_FORMAT, "JPEGInterchangeFormat");
|
||||
long jpegFormatLength = getValueAsLong(TIFF.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, "JPEGInterchangeFormatLength");
|
||||
imageInput.seek(jpegFormatStart);
|
||||
|
||||
stream = new SubImageInputStream(imageInput, jpegFormatLength);
|
||||
Iterator<ImageReader> readers = ImageIO.getImageReaders(stream); // TODO: Prefer default JPEGImageReader
|
||||
if (!readers.hasNext()) {
|
||||
throw new IIOException("Could not find delegate reader for JPEG format!");
|
||||
}
|
||||
|
||||
ImageReader reader = readers.next();
|
||||
|
||||
try {
|
||||
reader.setInput(stream);
|
||||
return reader.read(0, param);
|
||||
}
|
||||
finally {
|
||||
reader.dispose(); // TODO: Don't dispose until this instance is disposed
|
||||
}
|
||||
|
||||
case 34713: // Nikon NEF compressed
|
||||
width = getValueAsInt(TIFF.TAG_IMAGE_WIDTH, "ImageWidth");
|
||||
height = getValueAsInt(TIFF.TAG_IMAGE_HEIGHT, "ImageHeight");
|
||||
|
||||
// TODO: Read Nikon NEF compressed RAW
|
||||
|
||||
return param != null ? param.getDestination() : null;
|
||||
|
||||
default:
|
||||
throw new IIOException("Unsupported compression for NEF: " + compression);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
NEFImageReader reader = new NEFImageReader(new NEFImageReaderSpi());
|
||||
|
||||
for (String arg : args) {
|
||||
ImageInputStream stream = ImageIO.createImageInputStream(new File(arg));
|
||||
|
||||
reader.setInput(stream);
|
||||
|
||||
int numImages = reader.getNumImages(true);
|
||||
for (int i = 0; i < numImages; i++) {
|
||||
int numThumbnails = reader.getNumThumbnails(i);
|
||||
for (int n = 0; n < numThumbnails; n++) {
|
||||
showIt(reader.readThumbnail(i, n), arg + " image thumbnail" + n);
|
||||
}
|
||||
|
||||
showIt(reader.read(i), arg + " image " + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// DUPED CODE BELOW //// DUPED CODE BELOW //// DUPED CODE BELOW //// DUPED CODE BELOW //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
private int getBitsPerSample() throws IIOException {
|
||||
long[] value = getValueAsLongArray(TIFF.TAG_BITS_PER_SAMPLE, "BitsPerSample", false);
|
||||
|
||||
if (value == null || value.length == 0) {
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
int bitsPerSample = (int) value[0];
|
||||
|
||||
for (int i = 1; i < value.length; i++) {
|
||||
if (value[i] != bitsPerSample) {
|
||||
throw new IIOException("Variable BitsPerSample not supported: " + Arrays.toString(value));
|
||||
}
|
||||
}
|
||||
|
||||
return bitsPerSample;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
&& bands == null /* TODO: Compare bands with that of raster */) {
|
||||
return raster;
|
||||
}
|
||||
|
||||
return raster.createChild(rect.x / xSub, 0, rect.width / xSub, 1, 0, 0, bands);
|
||||
}
|
||||
|
||||
private WritableRaster clipToRect(final WritableRaster raster, final Rectangle rect, final int[] bands) {
|
||||
if (rect.contains(raster.getMinX(), raster.getMinY(), raster.getWidth(), raster.getHeight())
|
||||
&& bands == null /* TODO: Compare bands with that of raster */) {
|
||||
return raster;
|
||||
}
|
||||
|
||||
return raster.createWritableChild(rect.x, rect.y, rect.width, rect.height, 0, 0, bands);
|
||||
}
|
||||
|
||||
private void readStripTileData(final Raster tileRowRaster, final Rectangle srcRegion, final int xSub, final int ySub,
|
||||
final int numBands, final int interpretation,
|
||||
final WritableRaster raster, final int startCol, final int startRow,
|
||||
final int colsInTile, final int rowsInTile, final DataInput input)
|
||||
throws IOException {
|
||||
|
||||
switch (tileRowRaster.getTransferType()) {
|
||||
case DataBuffer.TYPE_BYTE:
|
||||
byte[] rowDataByte = ((DataBufferByte) tileRowRaster.getDataBuffer()).getData();
|
||||
|
||||
for (int row = startRow; row < startRow + rowsInTile; row++) {
|
||||
if (row >= srcRegion.y + srcRegion.height) {
|
||||
break; // We're done with this tile
|
||||
}
|
||||
|
||||
input.readFully(rowDataByte);
|
||||
|
||||
if (row % ySub == 0 && row >= srcRegion.y) {
|
||||
normalizeBlack(interpretation, rowDataByte);
|
||||
|
||||
// Subsample horizontal
|
||||
if (xSub != 1) {
|
||||
for (int x = srcRegion.x / xSub * numBands; x < ((srcRegion.x + srcRegion.width) / xSub) * numBands; x += numBands) {
|
||||
for (int b = 0; b < numBands; b++) {
|
||||
rowDataByte[x + b] = rowDataByte[x * xSub + b];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
raster.setDataElements(startCol, (row - srcRegion.y) / ySub, tileRowRaster);
|
||||
}
|
||||
// Else skip data
|
||||
}
|
||||
|
||||
break;
|
||||
case DataBuffer.TYPE_USHORT:
|
||||
short[] rowDataShort = ((DataBufferUShort) tileRowRaster.getDataBuffer()).getData();
|
||||
|
||||
for (int row = startRow; row < startRow + rowsInTile; row++) {
|
||||
if (row >= srcRegion.y + srcRegion.height) {
|
||||
break; // We're done with this tile
|
||||
}
|
||||
|
||||
readFully(input, rowDataShort);
|
||||
|
||||
if (row >= srcRegion.y) {
|
||||
normalizeBlack(interpretation, rowDataShort);
|
||||
|
||||
// Subsample horizontal
|
||||
if (xSub != 1) {
|
||||
for (int x = srcRegion.x / xSub * numBands; x < ((srcRegion.x + srcRegion.width) / xSub) * numBands; x += numBands) {
|
||||
for (int b = 0; b < numBands; b++) {
|
||||
rowDataShort[x + b] = rowDataShort[x * xSub + b];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
raster.setDataElements(startCol, row - srcRegion.y, tileRowRaster);
|
||||
}
|
||||
// Else skip data
|
||||
}
|
||||
|
||||
break;
|
||||
case DataBuffer.TYPE_INT:
|
||||
int[] rowDataInt = ((DataBufferInt) tileRowRaster.getDataBuffer()).getData();
|
||||
|
||||
for (int row = startRow; row < startRow + rowsInTile; row++) {
|
||||
if (row >= srcRegion.y + srcRegion.height) {
|
||||
break; // We're done with this tile
|
||||
}
|
||||
|
||||
readFully(input, rowDataInt);
|
||||
|
||||
if (row >= srcRegion.y) {
|
||||
normalizeBlack(interpretation, rowDataInt);
|
||||
|
||||
// Subsample horizontal
|
||||
if (xSub != 1) {
|
||||
for (int x = srcRegion.x / xSub * numBands; x < ((srcRegion.x + srcRegion.width) / xSub) * numBands; x += numBands) {
|
||||
for (int b = 0; b < numBands; b++) {
|
||||
rowDataInt[x + b] = rowDataInt[x * xSub + b];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
raster.setDataElements(startCol, row - srcRegion.y, tileRowRaster);
|
||||
}
|
||||
// Else skip data
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Candidate util method (with off/len + possibly byte order)
|
||||
private void readFully(final DataInput input, final int[] rowDataInt) throws IOException {
|
||||
if (input instanceof ImageInputStream) {
|
||||
ImageInputStream imageInputStream = (ImageInputStream) input;
|
||||
imageInputStream.readFully(rowDataInt, 0, rowDataInt.length);
|
||||
}
|
||||
else {
|
||||
for (int k = 0; k < rowDataInt.length; k++) {
|
||||
rowDataInt[k] = input.readInt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Candidate util method (with off/len + possibly byte order)
|
||||
private void readFully(final DataInput input, final short[] rowDataShort) throws IOException {
|
||||
if (input instanceof ImageInputStream) {
|
||||
ImageInputStream imageInputStream = (ImageInputStream) input;
|
||||
imageInputStream.readFully(rowDataShort, 0, rowDataShort.length);
|
||||
}
|
||||
else {
|
||||
for (int k = 0; k < rowDataShort.length; k++) {
|
||||
rowDataShort[k] = input.readShort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void normalizeBlack(int photometricInterpretation, short[] data) {
|
||||
if (photometricInterpretation == 0 /*TIFFBaseline.PHOTOMETRIC_WHITE_IS_ZERO*/) {
|
||||
// Inverse values
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
data[i] = (short) (0xffff - data[i] & 0xffff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void normalizeBlack(int photometricInterpretation, int[] data) {
|
||||
if (photometricInterpretation == 0 /*TIFFBaseline.PHOTOMETRIC_WHITE_IS_ZERO*/) {
|
||||
// Inverse values
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
data[i] = (0xffffffff - data[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void normalizeBlack(int photometricInterpretation, byte[] data) {
|
||||
if (photometricInterpretation == 0/*TIFFBaseline.PHOTOMETRIC_WHITE_IS_ZERO*/) {
|
||||
// Inverse values
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
data[i] = (byte) (0xff - data[i] & 0xff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private InputStream createDecompressorStream(final int compression, final int width, final InputStream stream) throws IOException {
|
||||
switch (compression) {
|
||||
// case TIFFBaseline.COMPRESSION_NONE:
|
||||
case 1:
|
||||
return stream;
|
||||
// case TIFFBaseline.COMPRESSION_PACKBITS:
|
||||
// case TIFFExtension.COMPRESSION_ZLIB:
|
||||
case 8:
|
||||
// TIFFphotoshop.pdf (aka TIFF specification, supplement 2) says ZLIB (8) and DEFLATE (32946) algorithms are identical
|
||||
return new InflaterInputStream(stream, new Inflater(), 1024);
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported TIFF compression: " + compression);
|
||||
}
|
||||
}
|
||||
|
||||
private InputStream createUnpredictorStream(final int predictor, final int width, final int samplesPerPixel, final int bitsPerSample, final InputStream stream, final ByteOrder byteOrder) throws IOException {
|
||||
switch (predictor) {
|
||||
// case TIFFBaseline.PREDICTOR_NONE:
|
||||
case 1:
|
||||
return stream;
|
||||
// case TIFFExtension.PREDICTOR_HORIZONTAL_DIFFERENCING:
|
||||
case 2:
|
||||
// return new HorizontalDeDifferencingStream(stream, width, samplesPerPixel, bitsPerSample, byteOrder);
|
||||
// case TIFFExtension.PREDICTOR_HORIZONTAL_FLOATINGPOINT:
|
||||
case 3:
|
||||
throw new IIOException("Unsupported TIFF Predictor value: " + predictor);
|
||||
default:
|
||||
throw new IIOException("Unknown TIFF Predictor value: " + predictor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 "TwelveMonkeys" 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 OWNER 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.nef;
|
||||
|
||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
|
||||
import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
|
||||
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* CR2ImageReaderSpi
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: CR2ImageReaderSpi.java,v 1.0 07.04.14 21:26 haraldk Exp$
|
||||
*/
|
||||
public final class NEFImageReaderSpi extends ImageReaderSpiBase {
|
||||
public NEFImageReaderSpi() {
|
||||
super(new NEFProviderInfo());
|
||||
}
|
||||
|
||||
public boolean canDecodeInput(final Object pSource) throws IOException {
|
||||
if (!(pSource instanceof ImageInputStream)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ImageInputStream stream = (ImageInputStream) pSource;
|
||||
|
||||
stream.mark();
|
||||
try {
|
||||
byte[] bom = new byte[2];
|
||||
stream.readFully(bom);
|
||||
|
||||
ByteOrder originalOrder = stream.getByteOrder();
|
||||
|
||||
try {
|
||||
if (bom[0] == 'I' && bom[1] == 'I') {
|
||||
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
}
|
||||
else if (bom[0] == 'M' && bom[1] == 'M') {
|
||||
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
||||
int tiffMagic = stream.readUnsignedShort();
|
||||
if (tiffMagic != TIFF.TIFF_MAGIC) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: This is not different from a normal TIFF...
|
||||
|
||||
return true;
|
||||
}
|
||||
finally {
|
||||
stream.setByteOrder(originalOrder);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
stream.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageReader createReaderInstance(Object extension) throws IOException {
|
||||
return new NEFImageReader(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription(Locale locale) {
|
||||
return "Adobe Digital Negative (DNG) format Reader";
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oleg Ermolaev
|
||||
* 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 "TwelveMonkeys" 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 OWNER 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.nef;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
||||
|
||||
/**
|
||||
* @author Oleg Ermolaev Date: 04.05.2018 1:50
|
||||
*/
|
||||
class NEFProviderInfo extends ReaderWriterProviderInfo {
|
||||
protected NEFProviderInfo() {
|
||||
super(
|
||||
NEFProviderInfo.class,
|
||||
new String[]{"nef", "NEF"},
|
||||
new String[]{"nef"},
|
||||
new String[]{
|
||||
"image/x-nikon-nef", // TODO: Look up
|
||||
},
|
||||
"com.twelvemonkeys.imageio.plugins.nef.NEFImageReader",
|
||||
new String[]{"com.twelvemonkeys.imageio.plugins.nef.NEFImageReaderSpi"},
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 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 "TwelveMonkeys" 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 OWNER 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.nef;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import java.awt.*;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* CR2ImageReaderTest
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: CR2ImageReaderTest.java,v 1.0 07.04.14 21:52 haraldk Exp$
|
||||
*/
|
||||
@Ignore
|
||||
public class NEFImageReaderTest extends ImageReaderAbstractTest<NEFImageReader> {
|
||||
@Override
|
||||
protected List<TestData> getTestData() {
|
||||
return Arrays.asList(
|
||||
new TestData(
|
||||
getClassLoaderResource("/nef/foo.nef"),
|
||||
new Dimension(2, 2)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ImageReaderSpi createProvider() {
|
||||
return new NEFImageReaderSpi();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getFormatNames() {
|
||||
return Arrays.asList("nef");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getSuffixes() {
|
||||
return Arrays.asList("nef");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getMIMETypes() {
|
||||
return Arrays.asList("image/x-nef");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Known issue: Subsampled reading not supported")
|
||||
@Override
|
||||
public void testReadWithSubsampleParamPixels() throws IOException {
|
||||
super.testReadWithSubsampleParamPixels();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Known issue: Source region reading not supported")
|
||||
@Override
|
||||
public void testReadWithSourceRegionParamEqualImage() throws IOException {
|
||||
super.testReadWithSourceRegionParamEqualImage();
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
+16
-6
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+26
@@ -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();
|
||||
|
||||
+76
-8
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-4
@@ -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);
|
||||
|
||||
+2
-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;
|
||||
|
||||
+69
@@ -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()));
|
||||
}
|
||||
}
|
||||
+400
@@ -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"));
|
||||
}
|
||||
}
|
||||
+77
@@ -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";
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -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
|
||||
);
|
||||
|
||||
+1
-1
@@ -67,7 +67,7 @@ final class PSDVersionInfo extends PSDImageResource {
|
||||
|
||||
writer = PSDUtil.readUnicodeString(pInput);
|
||||
reader = PSDUtil.readUnicodeString(pInput);
|
||||
|
||||
|
||||
fileVersion = pInput.readInt();
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
com.twelvemonkeys.imageio.plugins.psd.PSDImageWriterSpi
|
||||
+37
@@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
+5
-12
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -16,6 +16,19 @@
|
||||
<project.jpms.module.name>com.twelvemonkeys.imageio.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>
|
||||
|
||||
@@ -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</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: TIFF plugin</name>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user