Compare commits

..

80 Commits

Author SHA1 Message Date
Harald Kuhr 4993331a5d [maven-release-plugin] prepare release twelvemonkeys-3.6.4 2021-03-08 14:31:01 +01:00
Harald Kuhr af0a3889db #595 Avoid infinite loop on corrupted JPEG stream
(cherry picked from commit ba0bb7b903)
2021-03-08 14:24:12 +01:00
Harald Kuhr 6acdfd3be6 Readme updates, mentioning JPEG lossless and built-in support (closes #471).
(cherry picked from commit d03dc28764)
2021-03-08 14:24:11 +01:00
Harald Kuhr cfb664a76a Updated version numbers.
(cherry picked from commit 20a785ea5e)
2021-03-08 14:23:19 +01:00
Harald Kuhr 86d95e1f02 [maven-release-plugin] prepare for next development iteration 2021-02-26 18:51:06 +01:00
Harald Kuhr 54dd9b6d7b [maven-release-plugin] prepare release twelvemonkeys-3.6.3 2021-02-26 18:50:59 +01:00
Harald Kuhr 4e10fc019e #588 Clipping path from JPEG with multiple APP13 segments
(cherry picked from commit 970f4f3a7e)
2021-02-26 18:42:42 +01:00
Harald Kuhr 1295951ead Fix SGI source subsampling + test optimizations.
(cherry picked from commit 6d192968d1)
2021-02-26 18:42:38 +01:00
Harald Kuhr d5e664cdcc JPEG Exif/thumbnail fixes.
(cherry picked from commit fbc738f2d4)
2021-02-26 18:39:43 +01:00
Harald Kuhr efe5f3c34a No longer reads thumbnails, as part of the readWithOrientation method.
(cherry picked from commit 80c595cea8)
2021-02-26 18:33:47 +01:00
Harald Kuhr 2488f6f67c JPEG Exif/thumbnail fixes.
(cherry picked from commit fbc738f2d4)
2021-02-26 18:33:46 +01:00
Harald Kuhr 6a66d2e059 More standard key mapping, more correct fit size. Nicer color!
(cherry picked from commit 3e3acf3332)
2021-02-26 18:33:44 +01:00
Koen De Groote ebd5533879 Upgraded the Apache Batik library from 1.12 to 1.14 due to fixed CVEs.
(cherry picked from commit 72cd3aade3)
2021-02-26 18:32:21 +01:00
Harald Kuhr e6e4e96309 Update README.md
Removed JDK 7 from recommended build.

(cherry picked from commit 88bd9cd2ba)
2021-02-26 18:32:20 +01:00
Harald Kuhr aadc62dde9 Removed XWD plugin that will be in 3.7.
Fixed some incorrect code escaping.

(cherry picked from commit 5ee8678a29)
2021-02-26 18:32:20 +01:00
Harald Kuhr 24cbe57240 Updated README with latest version numbers.
(cherry picked from commit fb1937ae63)
2021-02-26 18:32:20 +01:00
Harald Kuhr f7d8ae0cd2 [maven-release-plugin] prepare for next development iteration 2021-01-23 17:09:08 +01:00
Harald Kuhr 5da934e11b [maven-release-plugin] prepare release twelvemonkeys-3.6.2 2021-01-23 17:08:59 +01:00
Harald Kuhr 51297ad496 #582: Fix for missing Exif thumbnail, now only issues warning.
(cherry picked from commit de02e3d7e0)
2021-01-23 17:01:14 +01:00
Harald Kuhr 80a534cd62 Fix some corner cases in BufferedImageInputStream.
(cherry picked from commit 8a1a90dafd)
2021-01-23 17:01:08 +01:00
Harald Kuhr 24130d466d #579 More reliable CCITT compression type detection
(cherry picked from commit 253f04066b)
2021-01-23 17:01:05 +01:00
Harald Kuhr 7559686782 StandardCharsets.US_ASCII instead of Charset.forName("ascii")
(cherry picked from commit 74902b3fb4)
2021-01-23 17:00:58 +01:00
Harald Kuhr b6988c37a7 #577 Fix TGA subsampling + bonus metadata fix and palette conversion.
(cherry picked from commit af1a6492d4)
2021-01-23 17:00:48 +01:00
Harald Kuhr bbffb1d416 BufferedImageInputStream performance optimizations.
(cherry picked from commit c7d2f422b8)
2021-01-23 17:00:31 +01:00
Harald Kuhr c68de3bc92 Updated links to latest version.
(cherry picked from commit 25150b421c)
2021-01-23 16:59:58 +01:00
Harald Kuhr a12b6044c6 Add XWD to BOM.
(cherry picked from commit 94031a2913)
2021-01-23 16:59:48 +01:00
Harald Kuhr 9a0e2d9659 [maven-release-plugin] prepare for next development iteration 2020-11-19 22:40:02 +01:00
Harald Kuhr b904f8952f [maven-release-plugin] prepare release twelvemonkeys-3.6.1 2020-11-19 22:39:53 +01:00
Harald Kuhr 187c952b8e Setting SNAPSHOT versions. 2020-11-19 22:07:30 +01:00
Harald Kuhr ca86605923 #574 Better test data.
(cherry picked from commit 1d4f681b8f)
2020-11-19 21:50:40 +01:00
Harald Kuhr 8bc863298f #574 Fix for possible OOME in Exif metadata.
(cherry picked from commit eda2cd76db)
2020-11-19 21:50:39 +01:00
Harald Kuhr 3447d1782c Some minor code clean-up.
(cherry picked from commit 4adc60a6c6)
2020-11-19 21:50:35 +01:00
Harald Kuhr bf245fde5f #330 ImageReaderBase.getDestination now throws IIOException for too large dimension/size.
(cherry picked from commit 0d5577a9a4)
2020-11-19 21:50:31 +01:00
Harald Kuhr c0748dcfd7 #330 Now correctly calculates scanline for 1 & 4 bits
(cherry picked from commit 918f92aba7)
2020-11-19 21:50:26 +01:00
Harald Kuhr c293516201 #330 Now correctly uses USHORT instead of SHORT for 16 bit DIB.
(cherry picked from commit 7a24d55be7)
2020-11-19 21:50:26 +01:00
Harald Kuhr f6dae36b7e #330 Now guards against buffer overruns in RLE decoder.
(cherry picked from commit a84cc1c060)
2020-11-19 21:50:26 +01:00
Harald Kuhr a14b481e9e #330 Minor improvements to avoid RuntimeExceptions.
(cherry picked from commit 31cb79d2b9)
2020-11-19 21:50:22 +01:00
Harald Kuhr d9c1a39c37 Fixed Maven Central link URL to more relevant URL.
(cherry picked from commit d995e7baa0)
2020-11-19 21:50:17 +01:00
Harald Kuhr e9d9f99bb0 Fixed Maven Central link URL
(cherry picked from commit e7fe6d5c22)
2020-11-19 21:50:13 +01:00
Harald Kuhr b9749b94b0 Release notes already on the Github page.
(cherry picked from commit 918b698e50)
2020-11-19 21:50:13 +01:00
Harald Kuhr 4996dff6e4 ...and again.
(cherry picked from commit 2427b2323f)
2020-11-19 21:50:13 +01:00
Harald Kuhr b1a2244c7f ...and again.
(cherry picked from commit 0a8222fea3)
2020-11-19 21:50:08 +01:00
Harald Kuhr c6fe747ca3 Fixed metadata support (not all formats have it yet).
(cherry picked from commit 60a00b89ae)
2020-11-19 21:50:04 +01:00
Harald Kuhr e1af4d7da9 Removed empty lines. Added missing BMP info.
(cherry picked from commit 4c88efa19d)
2020-11-19 21:50:03 +01:00
Harald Kuhr 33556cc0ec New & improved README with tables and link to Wiki!
(cherry picked from commit 17d65a1f6f)
2020-11-19 21:50:03 +01:00
Harald Kuhr eaf13b102f Added PayPal donation link. Go use it! :-)
(cherry picked from commit fcd03eb903)
2020-11-19 21:49:59 +01:00
Harald Kuhr 74718f1ffb Now correctly uses Image*Input*Stream instead of ImageOutputStream...
(cherry picked from commit 4e69efce28)
2020-11-19 21:49:55 +01:00
Harald Kuhr bd3700ea59 ...and fix the broken test.
(cherry picked from commit 16caec4a22)
2020-11-19 21:49:50 +01:00
Harald Kuhr 8fccd9445f Minor improvements and better test cases.
(cherry picked from commit 08282ea09d)
2020-11-19 21:49:49 +01:00
Harald Kuhr cf0ed8f95c Update README.md
(cherry picked from commit a16fce0749)
2020-11-19 21:49:49 +01:00
Harald Kuhr c12e4e5646 Fixed URL now works, ideally should point to correct branch...
(cherry picked from commit 26e2fa0168)
2020-11-19 21:49:03 +01:00
Harald Kuhr 287b73c732 More standard way for getting vendor name and version info.
(cherry picked from commit 120deb3ad4)
2020-11-19 21:49:01 +01:00
Harald Kuhr 24271b8cad NetBPM clean-up, fixes and better tests.
(cherry picked from commit 0a9e2df5de)
2020-11-19 21:48:59 +01:00
Harald Kuhr 3e2f54ee7c Verify that RGB data is correct.
(cherry picked from commit 6ffcb88872)
2020-11-19 21:48:59 +01:00
Harald Kuhr 2511b2d0cd Added test to verify how to write CMYK JPEG without ICC profile.
(cherry picked from commit 960e764c7b)
2020-11-19 21:48:58 +01:00
Harald Kuhr a15d54c92c Code clean-up.
(cherry picked from commit d88f27b251)
2020-11-19 21:48:56 +01:00
Harald Kuhr 47b7c4b16c Added missing tests.
(cherry picked from commit e5b3e9755e)
2020-11-19 21:48:55 +01:00
Harald Kuhr 9e1b01a7fd ImageWriterAbstractTest refactorings.
(cherry picked from commit 6c34fb211f)
2020-11-19 21:48:55 +01:00
Harald Kuhr 769acc8726 ImageReaderAbstractTest refactorings.
(cherry picked from commit 9fdbc3b1fc)
2020-11-19 21:48:15 +01:00
Harald Kuhr 1ace3a6d5f Getting rid of more JUnit deprecation.
(cherry picked from commit 622c6f40d4)
2020-11-19 21:44:36 +01:00
Harald Kuhr a06eb53cd2 Dependabot broke my build...
(cherry picked from commit 107da17ca9)
2020-11-19 21:44:36 +01:00
dependabot[bot] 1e1a640a6c Bump junit from 4.7 to 4.13.1 in /sandbox
Bumps [junit](https://github.com/junit-team/junit4) from 4.7 to 4.13.1.
- [Release notes](https://github.com/junit-team/junit4/releases)
- [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.13.1.md)
- [Commits](https://github.com/junit-team/junit4/compare/r4.7...r4.13.1)

Signed-off-by: dependabot[bot] <support@github.com>
(cherry picked from commit 19c62ac7da)
2020-11-19 21:44:35 +01:00
dependabot[bot] 2444bc5ad4 Bump junit from 4.7 to 4.13.1 in /common
Bumps [junit](https://github.com/junit-team/junit4) from 4.7 to 4.13.1.
- [Release notes](https://github.com/junit-team/junit4/releases)
- [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.13.1.md)
- [Commits](https://github.com/junit-team/junit4/compare/r4.7...r4.13.1)

Signed-off-by: dependabot[bot] <support@github.com>
(cherry picked from commit a4d20a4af4)
2020-11-19 21:42:48 +01:00
dependabot[bot] 2e656a45f9 Bump junit from 4.7 to 4.13.1 in /imageio
Bumps [junit](https://github.com/junit-team/junit4) from 4.7 to 4.13.1.
- [Release notes](https://github.com/junit-team/junit4/releases)
- [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.13.1.md)
- [Commits](https://github.com/junit-team/junit4/compare/r4.7...r4.13.1)

Signed-off-by: dependabot[bot] <support@github.com>
(cherry picked from commit 0643d5910a)
2020-11-19 21:42:32 +01:00
dependabot[bot] 657928f4a5 Bump junit from 4.7 to 4.13.1 in /contrib
Bumps [junit](https://github.com/junit-team/junit4) from 4.7 to 4.13.1.
- [Release notes](https://github.com/junit-team/junit4/releases)
- [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.13.1.md)
- [Commits](https://github.com/junit-team/junit4/compare/r4.7...r4.13.1)

Signed-off-by: dependabot[bot] <support@github.com>
(cherry picked from commit c78a456985)
2020-11-19 21:42:32 +01:00
dependabot[bot] 08f7e070dc Bump junit from 4.7 to 4.13.1 in /servlet
Bumps [junit](https://github.com/junit-team/junit4) from 4.7 to 4.13.1.
- [Release notes](https://github.com/junit-team/junit4/releases)
- [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.13.1.md)
- [Commits](https://github.com/junit-team/junit4/compare/r4.7...r4.13.1)

Signed-off-by: dependabot[bot] <support@github.com>
(cherry picked from commit 27017576d3)
2020-11-19 21:42:32 +01:00
Harald Kuhr b205226f0c Better output when debugging readers.
(cherry picked from commit 021aba1a98)
2020-11-19 21:42:32 +01:00
Harald Kuhr 821df11d90 Removed work in progress...
(cherry picked from commit a0b68adff3)
2020-11-19 21:42:31 +01:00
Harald Kuhr a62a838a0d Fixed a minor dependency issue. All test-jar dependencies now has correct test scope.
(cherry picked from commit fa4586663c)
2020-11-19 21:42:31 +01:00
Harald Kuhr 75ff0f265f Better PFM support.
(cherry picked from commit 623d13a517)
2020-11-19 21:42:31 +01:00
Harald Kuhr 15c7cfe9a6 Code clean-up.
(cherry picked from commit a7ebc1b52f)
2020-11-19 21:39:58 +01:00
Harald Kuhr 1286077b02 Added PNMImageWriterTest
(cherry picked from commit f54f4370c0)
2020-11-19 21:39:56 +01:00
Harald Kuhr 5757743db7 Add missing tests.
(cherry picked from commit 5040e9fe8a)
2020-11-19 21:39:55 +01:00
Harald Kuhr fbaa13d48d Minor language fix.
(cherry picked from commit fc72cd34a1)
2020-11-19 21:39:53 +01:00
Harald Kuhr f12df442e9 Added section about re-packaging and Shade plugin.
(cherry picked from commit 6d71a3d306)
2020-11-19 21:39:51 +01:00
Harald Kuhr 0c0712ab30 Comment fix
(cherry picked from commit 86f8cf52a5)
2020-11-19 21:39:50 +01:00
Harald Kuhr 9267842788 #556 PICTImageReaderSpi no longer claim to decode known formats
(cherry picked from commit bda6544a5f)
2020-11-19 21:39:48 +01:00
Harald Kuhr bcffeb04ec #466 TGA extension size fix for 3ds max files
(cherry picked from commit 49c7cd1979)
2020-11-19 21:39:47 +01:00
Harald Kuhr d1a1bab18c Code clean-up.
(cherry picked from commit 9dae58d5a6)
2020-11-19 21:39:47 +01:00
Harald Kuhr 6b5e75a22b Update readme to 3.6.
(cherry picked from commit ed14b97199)
2020-11-19 21:39:47 +01:00
292 changed files with 2556 additions and 20794 deletions
-1
View File
@@ -1 +0,0 @@
github: haraldk
-53
View File
@@ -1,53 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: Reported bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**Version information**
1. The version of the TwelveMonkeys ImageIO library in use.
For example: 4.0.0
2. The *exact* output of `java --version` (or `java -version` for older Java releases).
For example:
java version "1.8.0_271"
Java(TM) SE Runtime Environment (build 1.8.0_271-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.271-b09, mixed mode)
3. Extra information about OS version, server version, standalone program or web application packaging, executable wrapper, etc. Please state exact version numbers where applicable.
**To Reproduce**
Steps to reproduce the behavior:
1. Compile the below sample code
2. Download the sample image file
3. Run the code with the sample file
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Example code**
Preferably as a failing JUnit test, or a standalone program with a `main` method that showcases the problem.
Less is more. Don't add your entire project, only the code required to reproduce the problem. 😀
**Sample file(s)**
Attach any sample files needed to reproduce the problem. Use a ZIP-file if the format is not directly supported by GitHub.
**Stak trace**
Always include the stack trace you experience.
**Screenshots**
If applicable, add screenshots to help explain your problem.
Do not add screenshots of code or stack traces. 😀
**Additional context**
Add any other context about the problem here.
-20
View File
@@ -1,20 +0,0 @@
---
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.
@@ -1,13 +0,0 @@
---
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.
@@ -1,17 +0,0 @@
**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
+8 -8
View File
@@ -1,14 +1,14 @@
dist: trusty
language: java
jdk:
- oraclejdk8 # Legacy
- oraclejdk11 # LTS
- oraclejdk15 # Latest
jobs:
include:
# Extra job, testing legacy CMM option
- jdk: oraclejdk8
env: MAVEN_OPTS=-Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider
- oraclejdk8
# Oracle JDK 7 no longer supported, we use env matrix to test various CMM providers
# - oraclejdk7
# Some JPEGImageReader tests fail on OpenJDK, need to investigate/fix before enabling
# - openjdk7
env:
- MAVEN_OPTS=-Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider
- MAVEN_OPTS=""
cache:
directories:
- $HOME/.m2
+75 -78
View File
@@ -23,35 +23,34 @@ The goal is to create a set of efficient and robust ImageIO plug-ins, that can b
| ------ | -------- | ----------- |:----:|:-----:| -------- | ----- |
| Batik | **SVG** | Scalable Vector Graphics | ✔ | - | - | Requires [Batik](https://xmlgraphics.apache.org/batik/)
| | WMF | MS Windows Metafile | ✔ | - | - | Requires [Batik](https://xmlgraphics.apache.org/batik/)
| [BMP](https://github.com/haraldk/TwelveMonkeys/wiki/BMP-Plugin) | **BMP** | MS Windows and IBM OS/2 Device Independent Bitmap | ✔ | ✔ | [Native](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/bmp_metadata.html) & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| [BMP](https://github.com/haraldk/TwelveMonkeys/wiki/BMP-Plugin) | **BMP** | MS Windows and IBM OS/2 Device Independent Bitmap | ✔ | ✔ | Native & Standard |
| | CUR | MS Windows Cursor Format | ✔ | - | - |
| | ICO | MS Windows Icon Format | ✔ | ✔ | - |
| [HDR](https://github.com/haraldk/TwelveMonkeys/wiki/HDR-Plugin) | HDR | Radiance High Dynamic Range RGBE Format | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| [HDR](https://github.com/haraldk/TwelveMonkeys/wiki/HDR-Plugin) | HDR | Radiance High Dynamic Range RGBE Format | ✔ | - | Standard |
| [ICNS](https://github.com/haraldk/TwelveMonkeys/wiki/ICNS-Plugin) | ICNS | Apple Icon Image | ✔ | ✔ | - |
| [IFF](https://github.com/haraldk/TwelveMonkeys/wiki/IFF-Plugin) | IFF | Commodore Amiga/Electronic Arts Interchange File Format | ✔ | ✔ | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| [JPEG](https://github.com/haraldk/TwelveMonkeys/wiki/JPEG-Plugin) | **JPEG** | Joint Photographers Expert Group | ✔ | ✔ | [Native](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/jpeg_metadata.html#image) & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| | JPEG Lossless | | ✔ | - | [Native](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/jpeg_metadata.html#image) & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| [PCX](https://github.com/haraldk/TwelveMonkeys/wiki/PCX-Plugin) | PCX | ZSoft Paintbrush Format | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| | DCX | Multi-page PCX fax document | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| [PICT](https://github.com/haraldk/TwelveMonkeys/wiki/PICT-Plugin) | PICT | Apple QuickTime Picture Format | ✔ | | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| | PNTG | Apple MacPaint Picture Format | ✔ | | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| [PNM](https://github.com/haraldk/TwelveMonkeys/wiki/PNM-Plugin) | PAM | NetPBM Portable Any Map | ✔ | | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| | PBM | NetPBM Portable Bit Map | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| | PGM | NetPBM Portable Grey Map | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| | PPM | NetPBM Portable Pix Map | ✔ | | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| | PFM | Portable Float Map | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| [PSD](https://github.com/haraldk/TwelveMonkeys/wiki/PSD-Plugin) | **PSD** | Adobe Photoshop Document | ✔ | - | Native & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| | PSB | Adobe Photoshop Large Document | ✔ | - | Native & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| [SGI](https://github.com/haraldk/TwelveMonkeys/wiki/SGI-Plugin) | SGI | Silicon Graphics Image Format | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| [TGA](https://github.com/haraldk/TwelveMonkeys/wiki/TGA-Plugin) | TGA | Truevision TGA Image Format | ✔ | ✔ | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| [IFF](https://github.com/haraldk/TwelveMonkeys/wiki/IFF-Plugin) | IFF | Commodore Amiga/Electronic Arts Interchange File Format | ✔ | ✔ | Standard |
| [JPEG](https://github.com/haraldk/TwelveMonkeys/wiki/JPEG-Plugin) | **JPEG** | Joint Photographers Expert Group | ✔ | ✔ | Native & Standard |
| | **JPEG Lossless** | | ✔ | - | Native & Standard |
| [PCX](https://github.com/haraldk/TwelveMonkeys/wiki/PCX-Plugin) | PCX | ZSoft Paintbrush Format | ✔ | - | Standard |
| | DCX | Multi-page PCX fax document | ✔ | - | Standard |
| [PICT](https://github.com/haraldk/TwelveMonkeys/wiki/PICT-Plugin) | PICT | Apple Mac Paint Picture Format | ✔ | - | - |
| [PNM](https://github.com/haraldk/TwelveMonkeys/wiki/PNM-Plugin) | PAM | NetPBM Portable Any Map | ✔ | ✔ | Standard |
| | PBM | NetPBM Portable Bit Map | ✔ | - | Standard |
| | PGM | NetPBM Portable Grey Map | ✔ | - | Standard |
| | PPM | NetPBM Portable Pix Map | ✔ | | Standard |
| | PFM | Portable Float Map | ✔ | - | Standard |
| [PSD](https://github.com/haraldk/TwelveMonkeys/wiki/PSD-Plugin) | **PSD** | Adobe Photoshop Document | ✔ | - | Native & Standard |
| | PSB | Adobe Photoshop Large Document | ✔ | - | Native & Standard |
| [SGI](https://github.com/haraldk/TwelveMonkeys/wiki/SGI-Plugin) | SGI | Silicon Graphics Image Format | ✔ | - | Standard |
| [TGA](https://github.com/haraldk/TwelveMonkeys/wiki/TGA-Plugin) | TGA | Truevision TGA Image Format | ✔ | | Standard |
|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) |
| 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) |
| [TIFF](https://github.com/haraldk/TwelveMonkeys/wiki/TIFF-Plugin) | **TIFF** | Aldus/Adobe Tagged Image File Format | ✔ | ✔ | Native & Standard |
| | BigTIFF | | ✔ | - | Native & Standard |
| [WebP](https://github.com/haraldk/TwelveMonkeys/wiki/WebP-Plugin) | **WebP** | Google WebP Format | ✔ | - | Standard | In progress
| XWD | XWD | X11 Window Dump Format | ✔ | - | Standard |
**Important note on using Batik:** *Please read [The Apache™ XML Graphics Project - Security](https://xmlgraphics.apache.org/security.html),
**Important note on using Batik:** *Please read [The Apache™ XML Graphics Project - Security](http://xmlgraphics.apache.org/security.html),
and make sure you use version 1.14 or later.*
Note that GIF, PNG and WBMP formats are already supported through the ImageIO API, using the
@@ -166,7 +165,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](https://docs.oracle.com/javase/7/docs/technotes/guides/imageio/spec/imageio_guideTOC.fm.html)
[Java Image I/O API Guide](http://docs.oracle.com/javase/7/docs/technotes/guides/imageio/spec/imageio_guideTOC.fm.html)
from Oracle.
#### Adobe Clipping Path support
@@ -220,14 +219,14 @@ BufferedImage output = ditherer.filter(input, null);
## Building
Download the project (using [Git](https://git-scm.com/downloads)):
Download the project (using [Git](http://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](https://maven.apache.org/download.cgi)):
Build the project (using [Maven](http://maven.apache.org/download.cgi)):
$ mvn package
@@ -274,12 +273,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.7.0</version>
<version>3.6.3</version>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-tiff</artifactId>
<version>3.7.0</version>
<version>3.6.3</version>
</dependency>
<!--
@@ -289,7 +288,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.7.0</version>
<version>3.6.3</version>
</dependency>
</dependencies>
```
@@ -298,13 +297,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.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
twelvemonkeys-common-lang-3.6.3.jar
twelvemonkeys-common-io-3.6.3.jar
twelvemonkeys-common-image-3.6.3.jar
twelvemonkeys-imageio-core-3.6.3.jar
twelvemonkeys-imageio-metadata-3.6.3.jar
twelvemonkeys-imageio-jpeg-3.6.3.jar
twelvemonkeys-imageio-tiff-3.6.3.jar
#### Deploying the plugins in a web app
@@ -370,79 +369,77 @@ Other "fat" JAR bundlers will probably have similar mechanisms to merge entries
### Links to prebuilt binaries
##### Latest version (3.7.0)
##### Latest version (3.6.3)
Requires Java 7 or later.
Common dependencies
* [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)
* [common-lang-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-lang/3.6.3/common-lang-3.6.3.jar)
* [common-io-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-io/3.6.3/common-io-3.6.3.jar)
* [common-image-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-image/3.6.3/common-image-3.6.3.jar)
ImageIO dependencies
* [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-core-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-core/3.6.3/imageio-core-3.6.3.jar)
* [imageio-metadata-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-metadata/3.6.3/imageio-metadata-3.6.3.jar)
ImageIO plugins
* [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-bmp-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-bmp/3.6.3/imageio-bmp-3.6.3.jar)
* [imageio-hdr-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-hdr/3.6.3/imageio-hdr-3.6.3.jar)
* [imageio-icns-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-icns/3.6.3/imageio-icns-3.6.3.jar)
* [imageio-iff-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-iff/3.6.3/imageio-iff-3.6.3.jar)
* [imageio-jpeg-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jpeg/3.6.3/imageio-jpeg-3.6.3.jar)
* [imageio-pcx-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pcx/3.6.3/imageio-pcx-3.6.3.jar)
* [imageio-pict-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pict/3.6.3/imageio-pict-3.6.3.jar)
* [imageio-pnm-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pnm/3.6.3/imageio-pnm-3.6.3.jar)
* [imageio-psd-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-psd/3.6.3/imageio-psd-3.6.3.jar)
* [imageio-sgi-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-sgi/3.6.3/imageio-sgi-3.6.3.jar)
* [imageio-tga-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tga/3.6.3/imageio-tga-3.6.3.jar)
* [imageio-thumbsdb-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-thumbsdb/3.6.3/imageio-thumbsdb-3.6.3.jar)
* [imageio-tiff-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tiff/3.6.3/imageio-tiff-3.6.3.jar)
ImageIO plugins requiring 3rd party libs
* [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)
* [imageio-batik-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-batik/3.6.3/imageio-batik-3.6.3.jar)
Photoshop Path support for ImageIO
* [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)
* [imageio-clippath-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-clippath/3.6.3/imageio-clippath-3.6.3.jar)
Servlet support
* [servlet-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/servlet/servlet/3.7.0/servlet-3.7.0.jar)
* [servlet-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/servlet/servlet/3.6.3/servlet-3.6.3.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](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)
* [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)
ImageIO dependencies
* [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-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 plugins
* [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-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 plugins requiring 3rd party libs
* [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)
* [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)
Servlet support
* [servlet-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/servlet/servlet/3.0.2/servlet-3.0.2.jar)
* [servlet-3.0.2.jar](http://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](https://opensource.org/licenses/BSD-3-Clause):
This project is provided under the OSI approved [BSD license](http://opensource.org/licenses/BSD-3-Clause):
Copyright (c) 2008-2020, Harald Kuhr
All rights reserved.
@@ -497,7 +494,7 @@ a: The TwelveMonkeys ImageIO project contains plug-ins for ImageIO. ImageIO uses
All you have have to do, is to make sure you have the TwelveMonkeys JARs in your classpath.
You can read more about the registry and the lookup mechanism in the [IIORegistry API doc](https://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](http://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
+1 -16
View File
@@ -5,7 +5,7 @@
<parent>
<groupId>com.twelvemonkeys</groupId>
<artifactId>twelvemonkeys</artifactId>
<version>3.8.0-SNAPSHOT</version>
<version>3.6.4</version>
</parent>
<groupId>com.twelvemonkeys.bom</groupId>
@@ -58,16 +58,6 @@
<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>
@@ -88,11 +78,6 @@
<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>
+1 -5
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.common</groupId>
<artifactId>common</artifactId>
<version>3.8.0-SNAPSHOT</version>
<version>3.6.4</version>
</parent>
<artifactId>common-image</artifactId>
<packaging>jar</packaging>
@@ -13,10 +13,6 @@
The TwelveMonkeys Common Image support
</description>
<properties>
<project.jpms.module.name>com.twelvemonkeys.common.image</project.jpms.module.name>
</properties>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
+1 -5
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.common</groupId>
<artifactId>common</artifactId>
<version>3.8.0-SNAPSHOT</version>
<version>3.6.4</version>
</parent>
<artifactId>common-io</artifactId>
<packaging>jar</packaging>
@@ -13,10 +13,6 @@
The TwelveMonkeys Common IO support
</description>
<properties>
<project.jpms.module.name>com.twelvemonkeys.common.io</project.jpms.module.name>
</properties>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
@@ -346,7 +346,7 @@ public final class FileUtil {
/**
* Gets the file (type) extension of the given file.
* A file extension is the part of the filename, after the last occurrence
* A file extension is the part of the filename, after the last occurence
* of a period {@code '.'}.
* If the filename contains no period, {@code null} is returned.
*
@@ -32,13 +32,13 @@ package com.twelvemonkeys.io.enc;
import com.twelvemonkeys.io.FileUtil;
import com.twelvemonkeys.lang.ObjectAbstractTest;
import org.junit.Test;
import java.io.*;
import java.util.Arrays;
import java.util.Random;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
@@ -73,7 +73,7 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
}
}
private byte[] createData(final int pLength) {
private byte[] createData(final int pLength) throws Exception {
byte[] bytes = new byte[pLength];
RANDOM.nextBytes(bytes);
return bytes;
@@ -82,8 +82,9 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
private void runStreamTest(final int pLength) throws Exception {
byte[] data = createData(pLength);
ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
OutputStream out = new EncoderStream(outBytes, createEncoder(), true);
try (OutputStream out = new EncoderStream(outBytes, createEncoder(), true)) {
try {
// Provoke failure for encoders that doesn't take array offset properly into account
int off = (data.length + 1) / 2;
out.write(data, 0, off);
@@ -91,6 +92,9 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
out.write(data, off, data.length - off);
}
}
finally {
out.close();
}
byte[] encoded = outBytes.toByteArray();
@@ -98,7 +102,7 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
// System.err.println("encoded: " + Arrays.toString(encoded));
byte[] decoded = FileUtil.read(new DecoderStream(new ByteArrayInputStream(encoded), createCompatibleDecoder()));
assertArrayEquals(data, decoded);
assertTrue(Arrays.equals(data, decoded));
InputStream in = new DecoderStream(new ByteArrayInputStream(encoded), createCompatibleDecoder());
outBytes = new ByteArrayOutputStream();
@@ -112,7 +116,7 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
}
decoded = outBytes.toByteArray();
assertArrayEquals(data, decoded);
assertTrue(Arrays.equals(data, decoded));
}
@Test
@@ -125,6 +129,10 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
e.printStackTrace();
fail(e.getMessage() + ": " + i);
}
catch (Exception e) {
e.printStackTrace();
fail(e.getMessage() + ": " + i);
}
}
for (int i = 100; i < 2000; i += 250) {
@@ -135,6 +143,10 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
e.printStackTrace();
fail(e.getMessage() + ": " + i);
}
catch (Exception e) {
e.printStackTrace();
fail(e.getMessage() + ": " + i);
}
}
for (int i = 2000; i < 80000; i += 1000) {
@@ -145,8 +157,14 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
e.printStackTrace();
fail(e.getMessage() + ": " + i);
}
catch (Exception e) {
e.printStackTrace();
fail(e.getMessage() + ": " + i);
}
}
}
// TODO: Test that the transition from byte[] to ByteBuffer didn't introduce bugs when writing to a wrapped array with offset.
// TODO: Test that the transition from byte[] to ByteBuffer didn't introduce bugs when writing to a wrapped array with offset.
}
+1 -5
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.common</groupId>
<artifactId>common</artifactId>
<version>3.8.0-SNAPSHOT</version>
<version>3.6.4</version>
</parent>
<artifactId>common-lang</artifactId>
<packaging>jar</packaging>
@@ -13,8 +13,4 @@
The TwelveMonkeys Common Language support
</description>
<properties>
<project.jpms.module.name>com.twelvemonkeys.common.lang</project.jpms.module.name>
</properties>
</project>
@@ -330,7 +330,7 @@ abstract class AbstractDecoratedMap<K, V> extends AbstractMap<K, V> implements M
}
/**
* A simple Map.Entry implementation.
* A simple Map.Entry implementaton.
*/
static class BasicEntry<K, V> implements Entry<K, V>, Serializable {
K mKey;
@@ -34,7 +34,7 @@ import java.io.Serializable;
import java.util.*;
/**
* A {@code Map} implementation that removes (expires) its elements after
* A {@code Map} implementation that removes (exipres) its elements after
* a given period. The map is by default backed by a {@link java.util.HashMap},
* or can be instantiated with any given {@code Map} as backing.
* <p>
@@ -67,7 +67,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
protected long expiryTime = 60000L; // 1 minute
//////////////////////
private volatile long nextExpiryTime = Long.MAX_VALUE;
private volatile long nextExpiryTime;
//////////////////////
/**
@@ -178,7 +178,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
* @return {@code true} if this map contains no key-value mappings.
*/
public boolean isEmpty() {
return size() <= 0;
return (size() <= 0);
}
/**
@@ -208,7 +208,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
* @see #containsKey(java.lang.Object)
*/
public V get(Object pKey) {
TimedEntry entry = (TimedEntry) entries.get(pKey);
TimedEntry<K, V> entry = (TimedEntry<K, V>) entries.get(pKey);
if (entry == null) {
return null;
@@ -236,7 +236,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
* {@code null} values.
*/
public V put(K pKey, V pValue) {
TimedEntry entry = (TimedEntry) entries.get(pKey);
TimedEntry<K, V> entry = (TimedEntry<K, V>) entries.get(pKey);
V oldValue;
if (entry == null) {
@@ -272,7 +272,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
* {@code null} values.
*/
public V remove(Object pKey) {
TimedEntry entry = (TimedEntry) entries.remove(pKey);
TimedEntry<K, V> entry = (TimedEntry<K, V>) entries.remove(pKey);
return (entry != null) ? entry.getValue() : null;
}
@@ -284,12 +284,13 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
init();
}
/*protected*/ TimedEntry createEntry(K pKey, V pValue) {
return new TimedEntry(pKey, pValue);
/*protected*/ TimedEntry<K, V> createEntry(K pKey, V pValue) {
return new TimedEntry<K, V>(pKey, pValue);
}
/**
* Removes any expired mappings.
*
*/
protected void removeExpiredEntries() {
// Remove any expired elements
@@ -311,7 +312,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
long next = Long.MAX_VALUE;
nextExpiryTime = next; // Avoid multiple runs...
for (Iterator<Entry<K, V>> iterator = new EntryIterator(); iterator.hasNext();) {
TimedEntry entry = (TimedEntry) iterator.next();
TimedEntry<K, V> entry = (TimedEntry<K, V>) iterator.next();
////
long expires = entry.expires();
if (expires < next) {
@@ -375,7 +376,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
while (mNext == null && mIterator.hasNext()) {
Entry<K, Entry<K, V>> entry = mIterator.next();
TimedEntry timed = (TimedEntry) entry.getValue();
TimedEntry<K, V> timed = (TimedEntry<K, V>) entry.getValue();
if (timed.isExpiredBy(mNow)) {
// Remove from map, and continue
@@ -424,26 +425,17 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
/**
* Keeps track of timed objects
*/
private class TimedEntry extends BasicEntry<K, V> {
private class TimedEntry<K, V> extends BasicEntry<K, V> {
private long mTimestamp;
TimedEntry(K pKey, V pValue) {
super(pKey, pValue);
updateTimestamp();
mTimestamp = System.currentTimeMillis();
}
public V setValue(V pValue) {
updateTimestamp();
return super.setValue(pValue);
}
private void updateTimestamp() {
mTimestamp = System.currentTimeMillis();
long expires = expires();
if (expires < nextExpiryTime) {
nextExpiryTime = expires;
}
return super.setValue(pValue);
}
final boolean isExpired() {
@@ -30,7 +30,7 @@
package com.twelvemonkeys.lang;
import static org.junit.Assert.*;
import org.junit.Test;
import java.awt.*;
import java.sql.Timestamp;
@@ -41,7 +41,7 @@ import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* StringUtilTestCase
@@ -76,24 +76,24 @@ public class StringUtilTest {
assertNull(StringUtil.valueOf(null));
}
@SuppressWarnings("ConstantConditions")
@Test
public void testToUpperCase() {
String str = StringUtil.toUpperCase(TEST_STRING);
assertNotNull(str);
assertEquals(TEST_STRING.toUpperCase(), str);
assertNull(StringUtil.toUpperCase(null));
str = StringUtil.toUpperCase(null);
assertNull(str);
}
@SuppressWarnings("ConstantConditions")
@Test
public void testToLowerCase() {
String str = StringUtil.toLowerCase(TEST_STRING);
assertNotNull(str);
assertEquals(TEST_STRING.toLowerCase(), str);
assertNull(StringUtil.toLowerCase(null));
str = StringUtil.toLowerCase(null);
assertNull(str);
}
@Test
@@ -113,7 +113,6 @@ public class StringUtilTest {
assertFalse(StringUtil.isEmpty(new String[]{WHITESPACE_STRING, TEST_STRING}));
}
@SuppressWarnings("ConstantConditions")
@Test
public void testContains() {
assertTrue(StringUtil.contains(TEST_STRING, TEST_STRING));
@@ -146,7 +145,6 @@ public class StringUtilTest {
assertFalse(StringUtil.containsIgnoreCase(null, null));
}
@SuppressWarnings("ConstantConditions")
@Test
public void testContainsChar() {
for (int i = 0; i < TEST_STRING.length(); i++) {
@@ -468,7 +466,7 @@ public class StringUtilTest {
assertEquals(TEST_STRING, StringUtil.ltrim(TEST_STRING));
assertEquals(TEST_STRING, StringUtil.ltrim(" " + TEST_STRING));
assertEquals(TEST_STRING, StringUtil.ltrim(WHITESPACE_STRING + TEST_STRING));
assertNotEquals(TEST_STRING, StringUtil.ltrim(TEST_STRING + WHITESPACE_STRING));
assertFalse(TEST_STRING.equals(StringUtil.ltrim(TEST_STRING + WHITESPACE_STRING)));
// TODO: Test is not complete
}
@@ -477,7 +475,7 @@ public class StringUtilTest {
assertEquals(TEST_STRING, StringUtil.rtrim(TEST_STRING));
assertEquals(TEST_STRING, StringUtil.rtrim(TEST_STRING + " "));
assertEquals(TEST_STRING, StringUtil.rtrim(TEST_STRING + WHITESPACE_STRING));
assertNotEquals(TEST_STRING, StringUtil.rtrim(WHITESPACE_STRING + TEST_STRING));
assertFalse(TEST_STRING.equals(StringUtil.rtrim(WHITESPACE_STRING + TEST_STRING)));
// TODO: Test is not complete
}
@@ -518,7 +516,7 @@ public class StringUtilTest {
public void testCaptialize() {
assertNull(StringUtil.capitalize(null));
assertEquals(TEST_STRING.toUpperCase(), StringUtil.capitalize(TEST_STRING.toUpperCase()));
assertEquals('A', StringUtil.capitalize("abc").charAt(0));
assertTrue(StringUtil.capitalize("abc").charAt(0) == 'A');
}
@Test
@@ -554,13 +552,13 @@ public class StringUtilTest {
public void testToDateWithFormatString() {
Calendar cal = new GregorianCalendar();
cal.clear();
cal.set(1976, Calendar.MARCH, 16); // Month is 0-based
cal.set(1976, 2, 16); // Month is 0-based
Date date = StringUtil.toDate("16.03.1976", "dd.MM.yyyy");
assertNotNull(date);
assertEquals(cal.getTime(), date);
cal.clear();
cal.set(2004, Calendar.MAY, 13, 23, 51, 3);
cal.set(2004, 4, 13, 23, 51, 3);
date = StringUtil.toDate("2004-5-13 23:51 (03)", "yyyy-MM-dd hh:mm (ss)");
assertNotNull(date);
assertEquals(cal.getTime(), date);
@@ -578,15 +576,15 @@ public class StringUtilTest {
public void testToDateWithFormat() {
Calendar cal = new GregorianCalendar();
cal.clear();
cal.set(1976, Calendar.MARCH, 16); // Month is 0-based
cal.set(1976, 2, 16); // Month is 0-based
Date date = StringUtil.toDate("16.03.1976", new SimpleDateFormat("dd.MM.yyyy"));
assertNotNull(date);
assertEquals(cal.getTime(), date);
cal.clear();
cal.set(2004, Calendar.MAY, 13, 23, 51);
DateFormat format = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, new Locale("no", "NO"));
date = StringUtil.toDate(format.format(cal.getTime()), format);
cal.set(2004, 4, 13, 23, 51);
date = StringUtil.toDate("13.5.04 23:51",
DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, new Locale("no", "NO")));
assertNotNull(date);
assertEquals(cal.getTime(), date);
@@ -603,9 +601,10 @@ public class StringUtilTest {
public void testToTimestamp() {
Calendar cal = new GregorianCalendar();
cal.clear();
cal.set(1976, Calendar.MARCH, 16, 21, 28, 4); // Month is 0-based
Timestamp date = StringUtil.toTimestamp("1976-03-16 21:28:04");
cal.set(1976, 2, 16, 21, 28, 4); // Month is 0-based
Date date = StringUtil.toTimestamp("1976-03-16 21:28:04");
assertNotNull(date);
assertTrue(date instanceof Timestamp);
assertEquals(cal.getTime(), date);
}
@@ -822,7 +821,7 @@ public class StringUtilTest {
assertTrue(StringUtil.isNumber("12345"));
assertTrue(StringUtil.isNumber(TEST_INTEGER.toString()));
assertTrue(StringUtil.isNumber("1234567890123456789012345678901234567890"));
assertTrue(StringUtil.isNumber(String.valueOf(Long.MAX_VALUE) + Long.MAX_VALUE));
assertTrue(StringUtil.isNumber(String.valueOf(Long.MAX_VALUE) + String.valueOf(Long.MAX_VALUE)));
assertFalse(StringUtil.isNumber("abc"));
assertFalse(StringUtil.isNumber(TEST_STRING));
}
@@ -832,7 +831,7 @@ public class StringUtilTest {
assertTrue(StringUtil.isNumber("-12345"));
assertTrue(StringUtil.isNumber('-' + TEST_INTEGER.toString()));
assertTrue(StringUtil.isNumber("-1234567890123456789012345678901234567890"));
assertTrue(StringUtil.isNumber('-' + String.valueOf(Long.MAX_VALUE) + Long.MAX_VALUE));
assertTrue(StringUtil.isNumber('-' + String.valueOf(Long.MAX_VALUE) + String.valueOf(Long.MAX_VALUE)));
assertFalse(StringUtil.isNumber("-abc"));
assertFalse(StringUtil.isNumber('-' + TEST_STRING));
}
@@ -557,7 +557,7 @@ public class TimeoutMapTest extends MapAbstractTest {
// NOTE: Only wait fist time, to avoid slooow tests
synchronized (this) {
try {
wait(60L);
wait(60l);
}
catch (InterruptedException e) {
}
@@ -591,7 +591,7 @@ public class TimeoutMapTest extends MapAbstractTest {
try {
wait(60l);
}
catch (InterruptedException ignore) {
catch (InterruptedException e) {
}
}
}
@@ -651,24 +651,5 @@ public class TimeoutMapTest extends MapAbstractTest {
assertTrue("Wrong entry removed, keySet().iterator() is broken.", !map.containsKey(removedKey));
assertTrue("Wrong entry removed, keySet().iterator() is broken.", map.containsKey(otherKey));
}
@Test
public void testContainsKeyOnEmptyMap() {
// See #600
Map<String, String> timeoutMap = new TimeoutMap<>(30);
assertFalse(timeoutMap.containsKey("xyz"));
timeoutMap.put("xyz", "xyz");
assertTrue(timeoutMap.containsKey("xyz"));
try {
Thread.sleep(50); // Let the item expire
}
catch (InterruptedException ignore) {
}
assertFalse(timeoutMap.containsKey("xyz"));
assertNull(timeoutMap.get("xyz"));
}
}
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys</groupId>
<artifactId>twelvemonkeys</artifactId>
<version>3.8.0-SNAPSHOT</version>
<version>3.6.4</version>
</parent>
<groupId>com.twelvemonkeys.common</groupId>
<artifactId>common</artifactId>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys</groupId>
<artifactId>twelvemonkeys</artifactId>
<version>3.8.0-SNAPSHOT</version>
<version>3.6.4</version>
</parent>
<groupId>com.twelvemonkeys.contrib</groupId>
<artifactId>contrib</artifactId>
+5 -7
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.8.0-SNAPSHOT</version>
<version>3.6.4</version>
</parent>
<artifactId>imageio-batik</artifactId>
<name>TwelveMonkeys :: ImageIO :: Batik Plugin</name>
@@ -14,12 +14,6 @@
See the <a href="http://xmlgraphics.apache.org/batik/">Batik Home page</a>
for more information.]]>
</description>
<properties>
<project.jpms.module.name>com.twelvemonkeys.imageio.batik</project.jpms.module.name>
<batik.version>1.14</batik.version>
</properties>
<build>
<plugins>
<plugin>
@@ -108,4 +102,8 @@
</exclusions>
</dependency>
</dependencies>
<properties>
<batik.version>1.14</batik.version>
</properties>
</project>
@@ -655,7 +655,7 @@ public class SVGImageReader extends ImageReaderBase {
if (allowExternalResources) {
return super.getExternalResourceSecurity(resourceURL, docURL);
}
return new EmbededExternalResourceSecurity(resourceURL);
return new NoLoadExternalResourceSecurity();
}
}
}
@@ -297,25 +297,6 @@ public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader>
}
}
@Test
public void testReadEmbeddedWithDisallowExternalResources() throws IOException{
// File using "data:" URLs for embedded resources
URL resource = getClassLoaderResource("/svg/embedded-data-resource.svg");
SVGImageReader reader = createReader();
TestData data = new TestData(resource, (Dimension) null);
try (ImageInputStream stream = data.getInputStream()) {
reader.setInput(stream);
SVGReadParam param = reader.getDefaultReadParam();
param.setAllowExternalResources(false);
reader.read(0, param);
}
finally {
reader.dispose();
}
}
@Test(expected = SecurityException.class)
public void testDisallowedExternalResources() throws URISyntaxException, IOException {
// system-property set to true in surefire-plugin-settings in the pom
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 92 KiB

+1 -5
View File
@@ -4,16 +4,12 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.8.0-SNAPSHOT</version>
<version>3.6.4</version>
</parent>
<artifactId>imageio-bmp</artifactId>
<name>TwelveMonkeys :: ImageIO :: BMP plugin</name>
<description>ImageIO plugin for Microsoft Device Independent Bitmap (BMP/DIB) format.</description>
<properties>
<project.jpms.module.name>com.twelvemonkeys.imageio.bmp</project.jpms.module.name>
</properties>
<dependencies>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
@@ -37,7 +37,7 @@ import java.io.DataInput;
import java.io.File;
import java.io.IOException;
import java.nio.ByteOrder;
import java.util.Collections;
import java.util.Arrays;
import java.util.Iterator;
import javax.imageio.IIOException;
@@ -205,7 +205,7 @@ public final class BMPImageReader extends ImageReaderBase {
checkBounds(pImageIndex);
// TODO: Better implementation, include INT_RGB types for 3BYTE_BGR and 4BYTE_ABGR for INT_ARGB
return Collections.singletonList(getRawImageType(pImageIndex)).iterator();
return Arrays.asList(getRawImageType(pImageIndex)).iterator();
}
@Override
@@ -410,13 +410,6 @@ public final class BMPImageReader extends ImageReaderBase {
private ImageReader initReaderDelegate(int compression) throws IOException {
ImageReader reader = getImageReaderDelegate(compression);
reader.reset();
// Install listener
ListenerDelegator listenerDelegator = new ListenerDelegator();
reader.addIIOReadWarningListener(listenerDelegator);
reader.addIIOReadProgressListener(listenerDelegator);
reader.addIIOReadUpdateListener(listenerDelegator);
imageInput.seek(pixelOffset);
reader.setInput(new SubImageInputStream(imageInput, header.getImageSize()));
@@ -457,6 +450,12 @@ public final class BMPImageReader extends ImageReaderBase {
ImageReader reader = readers.next();
// Install listener
ListenerDelegator listenerDelegator = new ListenerDelegator();
reader.addIIOReadWarningListener(listenerDelegator);
reader.addIIOReadProgressListener(listenerDelegator);
reader.addIIOReadUpdateListener(listenerDelegator);
// Cache for later use
switch (compression) {
case DIB.COMPRESSION_JPEG:
@@ -634,8 +633,7 @@ public final class BMPImageReader extends ImageReaderBase {
return new BMPMetadata(header, colors);
}
@SuppressWarnings("ConstantConditions")
public static void main(String[] args) {
public static void main(String[] args) throws IOException {
BMPImageReaderSpi provider = new BMPImageReaderSpi();
BMPImageReader reader = new BMPImageReader(provider);
@@ -688,7 +686,7 @@ public final class BMPImageReader extends ImageReaderBase {
}
}
@SuppressWarnings({ "unchecked", "UnusedDeclaration", "SameParameterValue" })
@SuppressWarnings({"unchecked", "UnusedDeclaration"})
static <T extends Throwable> void throwAs(final Class<T> pType, final Throwable pThrowable) throws T {
throw (T) pThrowable;
}
@@ -364,11 +364,7 @@ abstract class DIBHeader {
public String getBMPVersion() {
// This is to be compatible with the native metadata of the original com.sun....BMPMetadata
return size > DIB.BITMAP_INFO_HEADER_SIZE
? "BMP V2/V3 INFO"
: compression == DIB.COMPRESSION_BITFIELDS || compression == DIB.COMPRESSION_ALPHA_BITFIELDS
? "BMP v. 3.x NT"
: "BMP v. 3.x";
return compression == DIB.COMPRESSION_BITFIELDS ? "BMP v. 3.x NT" : "BMP v. 3.x";
}
}
@@ -342,7 +342,6 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
for (TestData data : getTestData()) {
if (data.getInput().toString().contains("pal8offs")) {
// Skip: Contains extra bogus PaletteEntry nodes
continue;
}
@@ -359,7 +358,6 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
System.err.println("WARNING: Reading " + data + " caused exception: " + e.getMessage());
continue;
}
IIOMetadata jreMetadata = jreReader.getImageMetadata(0);
assertTrue(metadata.isStandardMetadataFormatSupported());
@@ -372,7 +370,6 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
String absolutePath = data.toString();
String localPath = absolutePath.substring(absolutePath.lastIndexOf("test-classes") + 12);
// TODO: blauesglas_16_bitmask444 fails BMP Version for 11+
Node expectedTree = jreMetadata.getAsTree(format);
Node actualTree = metadata.getAsTree(format);
@@ -431,7 +428,6 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
}
}
@SuppressWarnings("RedundantIfStatement")
private boolean excludeEqualValueTest(final Node expected) {
if (expected.getLocalName().equals("ImageSize")) {
// JRE metadata returns 0, even if known in reader...
+1 -5
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.8.0-SNAPSHOT</version>
<version>3.6.4</version>
</parent>
<artifactId>imageio-clippath</artifactId>
<name>TwelveMonkeys :: ImageIO :: Photoshop Path Support</name>
@@ -12,10 +12,6 @@
Photoshop Clipping Path Support.
</description>
<properties>
<project.jpms.module.name>com.twelvemonkeys.imageio.clippath</project.jpms.module.name>
</properties>
<dependencies>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
+1 -5
View File
@@ -4,15 +4,11 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.8.0-SNAPSHOT</version>
<version>3.6.4</version>
</parent>
<artifactId>imageio-core</artifactId>
<name>TwelveMonkeys :: ImageIO :: Core</name>
<properties>
<project.jpms.module.name>com.twelvemonkeys.imageio.core</project.jpms.module.name>
</properties>
<dependencies>
<dependency>
<groupId>com.twelvemonkeys.common</groupId>
@@ -265,9 +265,8 @@ public abstract class ImageReaderBase extends ImageReader {
// - transferType is ok
// - bands are ok
// TODO: Test if color model is ok?
if (specifier.getSampleModel().getTransferType() == dest.getSampleModel().getTransferType()
&& Arrays.equals(specifier.getSampleModel().getSampleSize(), dest.getSampleModel().getSampleSize())
&& specifier.getNumBands() <= dest.getSampleModel().getNumBands()) {
if (specifier.getSampleModel().getTransferType() == dest.getSampleModel().getTransferType() &&
specifier.getNumBands() <= dest.getSampleModel().getNumBands()) {
found = true;
break;
}
@@ -61,7 +61,7 @@ public final class UInt32ColorModel extends ComponentColorModel {
// This class only supports DataBuffer.TYPE_INT, cast is safe
int[] ipixel = (int[]) pixel;
for (int c = 0, nc = normOffset; c < numComponents; c++, nc++) {
normComponents[nc] = ((float) (ipixel[c] & 0xFFFFFFFFL)) / ((float) ((1L << getComponentSize(c)) - 1));
normComponents[nc] = ((float) (ipixel[c] & 0xffffffffl)) / ((float) ((1l << getComponentSize(c)) - 1));
}
int numColorComponents = getNumColorComponents();
@@ -49,10 +49,10 @@ public abstract class ReaderWriterProviderInfo extends ProviderInfo {
private final String[] mimeTypes;
private final String readerClassName;
private final String[] readerSpiClassNames;
private final Class<?>[] inputTypes = new Class<?>[] {ImageInputStream.class};
private final Class[] inputTypes = new Class[] {ImageInputStream.class};
private final String writerClassName;
private final String[] writerSpiClassNames;
private final Class<?>[] outputTypes = new Class<?>[] {ImageOutputStream.class};
private final Class[] outputTypes = new Class[] {ImageOutputStream.class};
private final boolean supportsStandardStreamMetadata;
private final String nativeStreamMetadataFormatName;
private final String nativeStreamMetadataFormatClassName;
@@ -80,7 +80,7 @@ public abstract class ReaderWriterProviderInfo extends ProviderInfo {
final String writerClassName,
final String[] writerSpiClassNames,
final boolean supportsStandardStreamMetadata,
final String nativeStreamMetadataFormatName,
final String nativeStreameMetadataFormatName,
final String nativeStreamMetadataFormatClassName,
final String[] extraStreamMetadataFormatNames,
final String[] extraStreamMetadataFormatClassNames,
@@ -99,7 +99,7 @@ public abstract class ReaderWriterProviderInfo extends ProviderInfo {
this.writerClassName = writerClassName;
this.writerSpiClassNames = writerSpiClassNames;
this.supportsStandardStreamMetadata = supportsStandardStreamMetadata;
this.nativeStreamMetadataFormatName = nativeStreamMetadataFormatName;
this.nativeStreamMetadataFormatName = nativeStreameMetadataFormatName;
this.nativeStreamMetadataFormatClassName = nativeStreamMetadataFormatClassName;
this.extraStreamMetadataFormatNames = extraStreamMetadataFormatNames;
this.extraStreamMetadataFormatClassNames = extraStreamMetadataFormatClassNames;
@@ -1,259 +0,0 @@
/*
* Copyright (c) 2021, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.stream;
import javax.imageio.stream.ImageInputStreamImpl;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import static com.twelvemonkeys.lang.Validate.notNull;
import static java.lang.Math.max;
/**
* A buffered replacement for {@link javax.imageio.stream.FileImageInputStream}
* that provides greatly improved performance for shorter reads, like single
* byte or bit reads.
* As with {@code javax.imageio.stream.FileImageInputStream}, either
* {@link File} or {@link RandomAccessFile} can be used as input.
*
* @see javax.imageio.stream.FileImageInputStream
*/
// TODO: Create a memory-mapped version?
// Or not... From java.nio.channels.FileChannel.map:
// For most operating systems, mapping a file into memory is more
// expensive than reading or writing a few tens of kilobytes of data via
// the usual {@link #read read} and {@link #write write} methods. From the
// standpoint of performance it is generally only worth mapping relatively
// large files into memory.
public final class BufferedFileImageInputStream extends ImageInputStreamImpl {
static final int DEFAULT_BUFFER_SIZE = 8192;
private byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
private int bufferPos;
private int bufferLimit;
private final ByteBuffer integralCache = ByteBuffer.allocate(8);
private final byte[] integralCacheArray = integralCache.array();
private RandomAccessFile raf;
/**
* Constructs a <code>BufferedFileImageInputStream</code> that will read from a given <code>File</code>.
*
* @param file a <code>File</code> to read from.
* @throws IllegalArgumentException if <code>file</code> is <code>null</code>.
* @throws FileNotFoundException if <code>file</code> is a directory or cannot be opened for reading
* for any reason.
*/
public BufferedFileImageInputStream(final File file) throws FileNotFoundException {
this(new RandomAccessFile(notNull(file, "file"), "r"));
}
/**
* Constructs a <code>BufferedFileImageInputStream</code> that will read from a given <code>RandomAccessFile</code>.
*
* @param raf a <code>RandomAccessFile</code> to read from.
* @throws IllegalArgumentException if <code>raf</code> is <code>null</code>.
*/
public BufferedFileImageInputStream(final RandomAccessFile raf) {
this.raf = notNull(raf, "raf");
}
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
private boolean fillBuffer() throws IOException {
int length = raf.read(buffer, 0, buffer.length);
bufferPos = 0;
bufferLimit = max(length, 0);
return bufferLimit > 0;
}
private boolean bufferEmpty() {
return bufferPos >= bufferLimit;
}
@Override
public void setByteOrder(ByteOrder byteOrder) {
super.setByteOrder(byteOrder);
integralCache.order(byteOrder);
}
@Override
public int read() throws IOException {
checkClosed();
if (bufferEmpty() && !fillBuffer()) {
return -1;
}
bitOffset = 0;
streamPos++;
return buffer[bufferPos++] & 0xff;
}
@Override
public int read(final byte[] bytes, final int offset, final int length) throws IOException {
checkClosed();
bitOffset = 0;
if (bufferEmpty()) {
// Bypass buffer if buffer is empty for reads longer than buffer
if (length >= buffer.length) {
return readDirect(bytes, offset, length);
}
else if (!fillBuffer()) {
return -1;
}
}
int fromBuffer = readBuffered(bytes, offset, length);
if (length > fromBuffer) {
// Due to known bugs in certain JDK-bundled ImageIO plugins expecting read to behave as readFully,
// we'll read as much as possible from the buffer, and the rest directly after
return fromBuffer + max(0, readDirect(bytes, offset + fromBuffer, length - fromBuffer));
}
return fromBuffer;
}
private int readDirect(final byte[] bytes, final int offset, final int length) throws IOException {
// Invalidate the buffer, as its contents is no longer in sync with the stream's position.
bufferLimit = 0;
int read = raf.read(bytes, offset, length);
if (read > 0) {
streamPos += read;
}
return read;
}
private int readBuffered(final byte[] bytes, final int offset, final int length) {
// Read as much as possible from buffer
int available = Math.min(bufferLimit - bufferPos, length);
if (available > 0) {
System.arraycopy(buffer, bufferPos, bytes, offset, available);
bufferPos += available;
streamPos += available;
}
return available;
}
public long length() {
// WTF?! This method is allowed to throw IOException in the interface...
try {
checkClosed();
return raf.length();
}
catch (IOException ignore) {
}
return -1;
}
public void close() throws IOException {
super.close();
raf.close();
raf = null;
buffer = null;
}
// Need to override the readShort(), readInt() and readLong() methods,
// because the implementations in ImageInputStreamImpl expects the
// read(byte[], int, int) to always read the expected number of bytes,
// causing uninitialized values, alignment issues and EOFExceptions at
// random places...
// Notes:
// * readUnsignedXx() is covered by their signed counterparts
// * readChar() is covered by readShort()
// * readFloat() and readDouble() is covered by readInt() and readLong()
// respectively.
// * readLong() may be covered by two readInt()s, we'll override to be safe
@Override
public short readShort() throws IOException {
readFully(integralCacheArray, 0, 2);
return integralCache.getShort(0);
}
@Override
public int readInt() throws IOException {
readFully(integralCacheArray, 0, 4);
return integralCache.getInt(0);
}
@Override
public long readLong() throws IOException {
readFully(integralCacheArray, 0, 8);
return integralCache.getLong(0);
}
@Override
public void seek(long position) throws IOException {
checkClosed();
if (position < flushedPos) {
throw new IndexOutOfBoundsException("position < flushedPos!");
}
bitOffset = 0;
if (streamPos == position) {
return;
}
// Optimized to not invalidate buffer if new position is within current buffer
long newBufferPos = bufferPos + position - streamPos;
if (newBufferPos >= 0 && newBufferPos < bufferLimit) {
bufferPos = (int) newBufferPos;
}
else {
// Will invalidate buffer
bufferLimit = 0;
raf.seek(position);
}
streamPos = position;
}
}
@@ -1,104 +0,0 @@
/*
* Copyright (c) 2021, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.stream;
import com.twelvemonkeys.imageio.spi.ProviderInfo;
import javax.imageio.spi.ImageInputStreamSpi;
import javax.imageio.spi.ServiceRegistry;
import javax.imageio.stream.ImageInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Iterator;
import java.util.Locale;
/**
* BufferedFileImageInputStreamSpi
* Experimental
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: BufferedFileImageInputStreamSpi.java,v 1.0 May 15, 2008 2:14:59 PM haraldk Exp$
*/
public class BufferedFileImageInputStreamSpi extends ImageInputStreamSpi {
public BufferedFileImageInputStreamSpi() {
this(new StreamProviderInfo());
}
private BufferedFileImageInputStreamSpi(ProviderInfo providerInfo) {
super(providerInfo.getVendorName(), providerInfo.getVersion(), File.class);
}
@Override
public void onRegistration(final ServiceRegistry registry, final Class<?> category) {
Iterator<ImageInputStreamSpi> providers = registry.getServiceProviders(ImageInputStreamSpi.class, new FileInputFilter(), true);
while (providers.hasNext()) {
ImageInputStreamSpi provider = providers.next();
if (provider != this) {
registry.setOrdering(ImageInputStreamSpi.class, this, provider);
}
}
}
public ImageInputStream createInputStreamInstance(final Object input, final boolean pUseCache, final File pCacheDir) {
if (input instanceof File) {
try {
return new BufferedFileImageInputStream((File) input);
}
catch (FileNotFoundException e) {
// For consistency with the JRE bundled SPIs, we'll return null here,
// even though the spec does not say that's allowed.
// The problem is that the SPIs can only declare that they support an input type like a File,
// instead they should be allowed to inspect the instance, to see that the file does exist...
return null;
}
}
throw new IllegalArgumentException("Expected input of type File: " + input);
}
@Override
public boolean canUseCacheFile() {
return false;
}
public String getDescription(final Locale pLocale) {
return "Service provider that instantiates an ImageInputStream from a File";
}
private static class FileInputFilter implements ServiceRegistry.Filter {
@Override
public boolean filter(final Object provider) {
return ((ImageInputStreamSpi) provider).getInputClass() == File.class;
}
}
}
@@ -43,19 +43,15 @@ import static com.twelvemonkeys.lang.Validate.notNull;
* A buffered {@code ImageInputStream}.
* Experimental - seems to be effective for {@link javax.imageio.stream.FileImageInputStream}
* and {@link javax.imageio.stream.FileCacheImageInputStream} when doing a lot of single-byte reads
* (or short byte-array reads).
* (or short byte-array reads) on OS X at least.
* Code that uses the {@code readFully} methods are not affected by the issue.
* <p>
* NOTE: Invoking {@code close()} will <em>NOT</em> close the wrapped stream.
* </p>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: BufferedFileImageInputStream.java,v 1.0 May 15, 2008 4:36:49 PM haraldk Exp$
*
* @deprecated Use {@link BufferedFileImageInputStream} instead.
*/
@Deprecated
// TODO: Create a provider for this (wrapping the FileIIS and FileCacheIIS classes), and disable the Sun built-in spis?
// TODO: Test on other platforms, might be just an OS X issue
public final class BufferedImageInputStream extends ImageInputStreamImpl implements ImageInputStream {
static final int DEFAULT_BUFFER_SIZE = 8192;
@@ -1,95 +0,0 @@
/*
* Copyright (c) 2021, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.stream;
import com.twelvemonkeys.imageio.spi.ProviderInfo;
import javax.imageio.spi.ImageInputStreamSpi;
import javax.imageio.spi.ServiceRegistry;
import javax.imageio.stream.ImageInputStream;
import java.io.File;
import java.io.RandomAccessFile;
import java.util.Iterator;
import java.util.Locale;
/**
* BufferedRAFImageInputStreamSpi
* Experimental
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: BufferedRAFImageInputStreamSpi.java,v 1.0 May 15, 2008 2:14:59 PM haraldk Exp$
*/
public class BufferedRAFImageInputStreamSpi extends ImageInputStreamSpi {
public BufferedRAFImageInputStreamSpi() {
this(new StreamProviderInfo());
}
private BufferedRAFImageInputStreamSpi(ProviderInfo providerInfo) {
super(providerInfo.getVendorName(), providerInfo.getVersion(), RandomAccessFile.class);
}
@Override
public void onRegistration(final ServiceRegistry registry, final Class<?> category) {
Iterator<ImageInputStreamSpi> providers = registry.getServiceProviders(ImageInputStreamSpi.class, new RAFInputFilter(), true);
while (providers.hasNext()) {
ImageInputStreamSpi provider = providers.next();
if (provider != this) {
registry.setOrdering(ImageInputStreamSpi.class, this, provider);
}
}
}
public ImageInputStream createInputStreamInstance(final Object input, final boolean pUseCache, final File pCacheDir) {
if (input instanceof RandomAccessFile) {
return new BufferedFileImageInputStream((RandomAccessFile) input);
}
throw new IllegalArgumentException("Expected input of type RandomAccessFile: " + input);
}
@Override
public boolean canUseCacheFile() {
return false;
}
public String getDescription(final Locale pLocale) {
return "Service provider that instantiates an ImageInputStream from a RandomAccessFile";
}
private static class RAFInputFilter implements ServiceRegistry.Filter {
@Override
public boolean filter(final Object provider) {
return ((ImageInputStreamSpi) provider).getInputClass() == RandomAccessFile.class;
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, Harald Kuhr
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -34,6 +34,7 @@ import com.twelvemonkeys.imageio.spi.ProviderInfo;
import javax.imageio.spi.ImageInputStreamSpi;
import javax.imageio.stream.FileCacheImageInputStream;
import javax.imageio.stream.FileImageInputStream;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.MemoryCacheImageInputStream;
import java.io.File;
@@ -71,7 +72,7 @@ public class URLImageInputStreamSpi extends ImageInputStreamSpi {
// Special case for file protocol, a lot faster than FileCacheImageInputStream
if ("file".equals(url.getProtocol())) {
try {
return new BufferedFileImageInputStream(new File(url.toURI()));
return new BufferedImageInputStream(new FileImageInputStream(new File(url.toURI())));
}
catch (URISyntaxException ignore) {
// This should never happen, but if it does, we'll fall back to using the stream
@@ -80,29 +81,29 @@ public class URLImageInputStreamSpi extends ImageInputStreamSpi {
}
// Otherwise revert to cached
final InputStream urlStream = url.openStream();
final InputStream stream = url.openStream();
if (pUseCache) {
return new FileCacheImageInputStream(urlStream, pCacheDir) {
return new BufferedImageInputStream(new FileCacheImageInputStream(stream, pCacheDir) {
@Override
public void close() throws IOException {
try {
super.close();
}
finally {
urlStream.close(); // NOTE: If this line throws IOE, it will shadow the original..
stream.close(); // NOTE: If this line throws IOE, it will shadow the original..
}
}
};
});
}
else {
return new MemoryCacheImageInputStream(urlStream) {
return new MemoryCacheImageInputStream(stream) {
@Override
public void close() throws IOException {
try {
super.close();
}
finally {
urlStream.close(); // NOTE: If this line throws IOE, it will shadow the original..
stream.close(); // NOTE: If this line throws IOE, it will shadow the original..
}
}
};
@@ -223,12 +223,7 @@ public final class IIOUtil {
public static void subsampleRow(byte[] srcRow, int srcPos, int srcWidth,
byte[] destRow, int destPos,
int samplesPerPixel, int bitsPerSample, int samplePeriod) {
// Period == 1 is a no-op...
if (samplePeriod == 1) {
return;
}
Validate.isTrue(samplePeriod > 1, "samplePeriod must be > 1");
Validate.isTrue(samplePeriod > 1, "samplePeriod must be > 1"); // Period == 1 could be a no-op...
Validate.isTrue(bitsPerSample > 0 && bitsPerSample <= 8 && (bitsPerSample == 1 || bitsPerSample % 2 == 0),
"bitsPerSample must be > 0 and <= 8 and a power of 2");
Validate.isTrue(samplesPerPixel > 0, "samplesPerPixel must be > 0");
@@ -266,12 +261,7 @@ public final class IIOUtil {
public static void subsampleRow(short[] srcRow, int srcPos, int srcWidth,
short[] destRow, int destPos,
int samplesPerPixel, int bitsPerSample, int samplePeriod) {
// Period == 1 is a no-op...
if (samplePeriod == 1) {
return;
}
Validate.isTrue(samplePeriod > 1, "samplePeriod must be > 1");
Validate.isTrue(samplePeriod > 1, "samplePeriod must be > 1"); // Period == 1 could be a no-op...
Validate.isTrue(bitsPerSample > 0 && bitsPerSample <= 16 && (bitsPerSample == 1 || bitsPerSample % 2 == 0),
"bitsPerSample must be > 0 and <= 16 and a power of 2");
Validate.isTrue(samplesPerPixel > 0, "samplesPerPixel must be > 0");
@@ -288,12 +278,7 @@ public final class IIOUtil {
public static void subsampleRow(int[] srcRow, int srcPos, int srcWidth,
int[] destRow, int destPos,
int samplesPerPixel, int bitsPerSample, int samplePeriod) {
// Period == 1 is a no-op...
if (samplePeriod == 1) {
return;
}
Validate.isTrue(samplePeriod > 1, "samplePeriod must be > 1");
Validate.isTrue(samplePeriod > 1, "samplePeriod must be > 1"); // Period == 1 could be a no-op...
Validate.isTrue(bitsPerSample > 0 && bitsPerSample <= 32 && (bitsPerSample == 1 || bitsPerSample % 2 == 0),
"bitsPerSample must be > 0 and <= 32 and a power of 2");
Validate.isTrue(samplesPerPixel > 0, "samplesPerPixel must be > 0");
@@ -30,21 +30,14 @@
package com.twelvemonkeys.imageio.util;
import static com.twelvemonkeys.lang.Validate.isTrue;
import static com.twelvemonkeys.lang.Validate.notNull;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DirectColorModel;
import java.awt.image.IndexColorModel;
import java.awt.image.MultiPixelPackedSampleModel;
import java.awt.image.SampleModel;
import com.twelvemonkeys.imageio.color.DiscreteAlphaIndexColorModel;
import javax.imageio.ImageTypeSpecifier;
import java.awt.color.ColorSpace;
import java.awt.image.*;
import com.twelvemonkeys.imageio.color.DiscreteAlphaIndexColorModel;
import static com.twelvemonkeys.lang.Validate.isTrue;
import static com.twelvemonkeys.lang.Validate.notNull;
/**
* Factory class for creating {@code ImageTypeSpecifier}s.
@@ -176,36 +169,21 @@ public final class ImageTypeSpecifiers {
int numEntries = 1 << bits;
ColorModel colorModel;
byte[] r = new byte[numEntries];
byte[] g = new byte[numEntries];
byte[] b = new byte[numEntries];
if (ColorSpace.getInstance(ColorSpace.CS_GRAY).equals(colorSpace)) {
// For default gray, use linear response
byte[] gray = new byte[numEntries];
// Scale array values according to color profile..
for (int i = 0; i < numEntries; i++) {
float[] gray = new float[]{i / (float) (numEntries - 1)};
float[] rgb = colorSpace.toRGB(gray);
for (int i = 0; i < numEntries; i++) {
gray[i] = (byte) ((i * 255) / (numEntries - 1));
}
colorModel = new IndexColorModel(bits, numEntries, gray, gray, gray);
}
else {
byte[] r = new byte[numEntries];
byte[] g = new byte[numEntries];
byte[] b = new byte[numEntries];
// Scale array values according to color profile..
for (int i = 0; i < numEntries; i++) {
float[] gray = new float[] { i / (float) (numEntries - 1) };
float[] rgb = colorSpace.toRGB(gray);
r[i] = (byte) Math.round(rgb[0] * 255);
g[i] = (byte) Math.round(rgb[1] * 255);
b[i] = (byte) Math.round(rgb[2] * 255);
}
colorModel = new IndexColorModel(bits, numEntries, r, g, b);
r[i] = (byte) (rgb[0] * 255);
g[i] = (byte) (rgb[1] * 255);
b[i] = (byte) (rgb[2] * 255);
}
ColorModel colorModel = new IndexColorModel(bits, numEntries, r, g, b);
SampleModel sampleModel = new MultiPixelPackedSampleModel(dataType, 1, 1, bits);
return new ImageTypeSpecifier(colorModel, sampleModel);
@@ -223,7 +201,7 @@ public final class ImageTypeSpecifiers {
}
public static ImageTypeSpecifier createFromIndexColorModel(final IndexColorModel pColorModel) {
return new IndexedImageTypeSpecifier(pColorModel);
return IndexedImageTypeSpecifier.createFromIndexColorModel(pColorModel);
}
public static ImageTypeSpecifier createDiscreteAlphaIndexedFromIndexColorModel(final IndexColorModel pColorModel) {
@@ -30,13 +30,13 @@
package com.twelvemonkeys.imageio.util;
import static com.twelvemonkeys.lang.Validate.notNull;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.WritableRaster;
import javax.imageio.ImageTypeSpecifier;
import java.awt.image.BufferedImage;
import java.awt.image.IndexColorModel;
import java.awt.image.WritableRaster;
import java.util.Hashtable;
import static com.twelvemonkeys.lang.Validate.notNull;
/**
* IndexedImageTypeSpecifier
@@ -45,24 +45,27 @@ import javax.imageio.ImageTypeSpecifier;
* @author last modified by $Author: haraldk$
* @version $Id: IndexedImageTypeSpecifier.java,v 1.0 May 19, 2008 11:04:28 AM haraldk Exp$
*/
final class IndexedImageTypeSpecifier extends ImageTypeSpecifier {
IndexedImageTypeSpecifier(final ColorModel colorModel) {
// For some reason, we need a sample model, even though we won't use it
super(notNull(colorModel, "colorModel"), colorModel.createCompatibleSampleModel(1, 1));
}
final class IndexedImageTypeSpecifier {
private IndexedImageTypeSpecifier() {}
@Override
public final BufferedImage createBufferedImage(final int pWidth, final int pHeight) {
try {
// This is a fix for the super-method, that first creates a sample model, and then
// creates a raster from it, using Raster.createWritableRaster. The problem with
// that approach, is that it always creates a TYPE_CUSTOM BufferedImage for indexed images.
WritableRaster raster = colorModel.createCompatibleWritableRaster(pWidth, pHeight);
return new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);
}
catch (NegativeArraySizeException e) {
// Exception most likely thrown from a DataBuffer constructor
throw new IllegalArgumentException("Array size > Integer.MAX_VALUE!");
}
static ImageTypeSpecifier createFromIndexColorModel(final IndexColorModel pColorModel) {
// For some reason, we need a sample model
return new ImageTypeSpecifier(notNull(pColorModel, "colorModel"), pColorModel.createCompatibleSampleModel(1, 1)) {
@Override
public final BufferedImage createBufferedImage(final int pWidth, final int pHeight) {
try {
// This is a fix for the super-method, that first creates a sample model, and then
// creates a raster from it, using Raster.createWritableRaster. The problem with
// that approach, is that it always creates a TYPE_CUSTOM BufferedImage for indexed images.
WritableRaster raster = colorModel.createCompatibleWritableRaster(pWidth, pHeight);
return new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), new Hashtable());
}
catch (NegativeArraySizeException e) {
// Exception most likely thrown from a DataBuffer constructor
throw new IllegalArgumentException("Array size > Integer.MAX_VALUE!");
}
}
};
}
}
@@ -1,152 +0,0 @@
/*
* 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;
}
}
@@ -30,16 +30,15 @@
package com.twelvemonkeys.imageio.util;
import com.twelvemonkeys.imageio.color.UInt32ColorModel;
import javax.imageio.ImageTypeSpecifier;
import java.awt.color.ColorSpace;
import java.awt.image.BandedSampleModel;
import java.awt.image.DataBuffer;
import java.awt.image.PixelInterleavedSampleModel;
import java.awt.image.SampleModel;
import javax.imageio.ImageTypeSpecifier;
import com.twelvemonkeys.imageio.color.UInt32ColorModel;
/**
* ImageTypeSpecifier for interleaved 32 bit unsigned integral samples.
*
@@ -48,13 +47,11 @@ import com.twelvemonkeys.imageio.color.UInt32ColorModel;
* @author last modified by $Author: haraldk$
* @version $Id: UInt32ImageTypeSpecifier.java,v 1.0 24.01.11 17.51 haraldk Exp$
*/
final class UInt32ImageTypeSpecifier extends ImageTypeSpecifier {
private UInt32ImageTypeSpecifier(final ColorSpace cs, final boolean hasAlpha, final boolean isAlphaPremultiplied, final SampleModel sampleModel) {
super(new UInt32ColorModel(cs, hasAlpha, isAlphaPremultiplied), sampleModel);
}
final class UInt32ImageTypeSpecifier {
private UInt32ImageTypeSpecifier() {}
static ImageTypeSpecifier createInterleaved(final ColorSpace cs, final int[] bandOffsets, final boolean hasAlpha, final boolean isAlphaPremultiplied) {
return new UInt32ImageTypeSpecifier(
return create(
cs, hasAlpha, isAlphaPremultiplied,
new PixelInterleavedSampleModel(
DataBuffer.TYPE_INT, 1, 1,
@@ -66,7 +63,7 @@ final class UInt32ImageTypeSpecifier extends ImageTypeSpecifier {
}
static ImageTypeSpecifier createBanded(final ColorSpace cs, final int[] bandIndices, final int[] bandOffsets, final boolean hasAlpha, final boolean isAlphaPremultiplied) {
return new UInt32ImageTypeSpecifier(
return create(
cs, hasAlpha, isAlphaPremultiplied,
new BandedSampleModel(
DataBuffer.TYPE_INT, 1, 1, 1,
@@ -75,13 +72,7 @@ final class UInt32ImageTypeSpecifier extends ImageTypeSpecifier {
);
}
@Override
public boolean equals(final Object other) {
if (!(other instanceof UInt32ImageTypeSpecifier)) {
return false;
}
UInt32ImageTypeSpecifier that = (UInt32ImageTypeSpecifier) other;
return colorModel.equals(that.colorModel) && sampleModel.equals(that.sampleModel);
private static ImageTypeSpecifier create(final ColorSpace cs, final boolean hasAlpha, final boolean isAlphaPremultiplied, final SampleModel sampleModel) {
return new ImageTypeSpecifier(new UInt32ColorModel(cs, hasAlpha, isAlphaPremultiplied), sampleModel);
}
}
@@ -1,2 +0,0 @@
com.twelvemonkeys.imageio.stream.BufferedFileImageInputStreamSpi
com.twelvemonkeys.imageio.stream.BufferedRAFImageInputStreamSpi
@@ -1,30 +0,0 @@
package com.twelvemonkeys.imageio.stream;
import org.junit.Test;
import javax.imageio.spi.ImageInputStreamSpi;
import java.io.File;
import java.io.IOException;
import static org.junit.Assert.assertNull;
import static org.junit.Assume.assumeFalse;
public class BufferedFileImageInputStreamSpiTest extends ImageInputStreamSpiTest<File> {
@Override
protected ImageInputStreamSpi createProvider() {
return new BufferedFileImageInputStreamSpi();
}
@Override
protected File createInput() throws IOException {
return File.createTempFile("test-", ".tst");
}
@Test
public void testReturnNullWhenFileDoesNotExist() throws IOException {
// This is really stupid behavior, but it is consistent with the JRE bundled SPIs.
File input = new File("a-file-that-should-not-exist-ever.fnf");
assumeFalse("File should not exist: " + input.getPath(), input.exists());
assertNull(provider.createInputStreamInstance(input));
}
}
@@ -1,429 +0,0 @@
/*
* Copyright (c) 2020, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.stream;
import org.junit.Test;
import org.junit.function.ThrowingRunnable;
import javax.imageio.stream.ImageInputStream;
import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.file.Files;
import java.util.Random;
import static com.twelvemonkeys.imageio.stream.BufferedImageInputStreamTest.rangeEquals;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
/**
* BufferedFileImageInputStreamTestCase
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: BufferedFileImageInputStreamTestCase.java,v 1.0 Apr 21, 2009 10:58:48 AM haraldk Exp$
*/
public class BufferedFileImageInputStreamTest {
private final Random random = new Random(170984354357234566L);
private File randomDataToFile(byte[] data) throws IOException {
random.nextBytes(data);
File file = File.createTempFile("read", ".tmp");
Files.write(file.toPath(), data);
return file;
}
@Test
public void testCreate() throws IOException {
try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(File.createTempFile("empty", ".tmp"))) {
assertEquals("Data length should be same as stream length", 0, stream.length());
}
}
@Test
public void testCreateNullFile() throws IOException {
try {
new BufferedFileImageInputStream((File) null);
fail("Expected IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
assertNotNull("Null exception message", expected.getMessage());
String message = expected.getMessage().toLowerCase();
assertTrue("Exception message does not contain parameter name", message.contains("file"));
assertTrue("Exception message does not contain null", message.contains("null"));
}
}
@Test
public void testCreateNullRAF() {
try {
new BufferedFileImageInputStream((RandomAccessFile) null);
fail("Expected IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
assertNotNull("Null exception message", expected.getMessage());
String message = expected.getMessage().toLowerCase();
assertTrue("Exception message does not contain parameter name", message.contains("raf"));
assertTrue("Exception message does not contain null", message.contains("null"));
}
}
@Test
public void testRead() throws IOException {
byte[] data = new byte[1024 * 1024];
File file = randomDataToFile(data);
try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file)) {
assertEquals("File length should be same as stream length", file.length(), stream.length());
for (byte value : data) {
assertEquals("Wrong data read", value & 0xff, stream.read());
}
}
}
@Test
public void testReadArray() throws IOException {
byte[] data = new byte[1024 * 1024];
File file = randomDataToFile(data);
try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file)) {
assertEquals("File length should be same as stream length", file.length(), stream.length());
byte[] result = new byte[1024];
for (int i = 0; i < data.length / result.length; i++) {
stream.readFully(result);
assertTrue("Wrong data read: " + i, rangeEquals(data, i * result.length, result, 0, result.length));
}
}
}
@Test
public void testReadSkip() throws IOException {
byte[] data = new byte[1024 * 14];
File file = randomDataToFile(data);
try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file)) {
assertEquals("File length should be same as stream length", file.length(), stream.length());
byte[] result = new byte[7];
for (int i = 0; i < data.length / result.length; i += 2) {
stream.readFully(result);
stream.skipBytes(result.length);
assertTrue("Wrong data read: " + i, rangeEquals(data, i * result.length, result, 0, result.length));
}
}
}
@Test
public void testReadSeek() throws IOException {
byte[] data = new byte[1024 * 18];
File file = randomDataToFile(data);
try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file)) {
assertEquals("File length should be same as stream length", file.length(), stream.length());
byte[] result = new byte[9];
for (int i = 0; i < data.length / result.length; i++) {
// Read backwards
long newPos = stream.length() - result.length - i * result.length;
stream.seek(newPos);
assertEquals("Wrong stream position", newPos, stream.getStreamPosition());
stream.readFully(result);
assertTrue("Wrong data read: " + i, rangeEquals(data, (int) newPos, result, 0, result.length));
}
}
}
@Test
public void testReadOutsideDataSeek0Read() throws IOException {
byte[] data = new byte[256];
File file = randomDataToFile(data);
try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file)) {
assertEquals("File length should be same as stream length", file.length(), stream.length());
byte[] buffer = new byte[data.length * 2];
stream.read(buffer);
stream.seek(0);
assertNotEquals(-1, stream.read());
assertNotEquals(-1, stream.read(buffer));
}
}
@Test
public void testReadBitRandom() throws IOException {
byte[] bytes = new byte[8];
File file = randomDataToFile(bytes);
long value = ByteBuffer.wrap(bytes).getLong();
// Create stream
try (ImageInputStream stream = new BufferedFileImageInputStream(file)) {
for (int i = 1; i <= 64; i++) {
assertEquals(String.format("bit %d differ", i), (value << (i - 1L)) >>> 63L, stream.readBit());
}
}
}
@Test
public void testReadBitsRandom() throws IOException {
byte[] bytes = new byte[8];
File file = randomDataToFile(bytes);
long value = ByteBuffer.wrap(bytes).getLong();
// Create stream
try (ImageInputStream stream = new BufferedFileImageInputStream(file)) {
for (int i = 1; i <= 64; i++) {
stream.seek(0);
assertEquals(String.format("bit %d differ", i), value >>> (64L - i), stream.readBits(i));
assertEquals(i % 8, stream.getBitOffset());
}
}
}
@Test
public void testReadBitsRandomOffset() throws IOException {
byte[] bytes = new byte[8];
File file = randomDataToFile(bytes);
long value = ByteBuffer.wrap(bytes).getLong();
// Create stream
try (ImageInputStream stream = new BufferedFileImageInputStream(file)) {
for (int i = 1; i <= 60; i++) {
stream.seek(0);
stream.setBitOffset(i % 8);
assertEquals(String.format("bit %d differ", i), (value << (i % 8)) >>> (64L - i), stream.readBits(i));
assertEquals(i * 2 % 8, stream.getBitOffset());
}
}
}
@Test
public void testReadShort() throws IOException {
byte[] bytes = new byte[8743]; // Slightly more than one buffer size
File file = randomDataToFile(bytes);
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
try (final ImageInputStream stream = new BufferedFileImageInputStream(file)) {
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
for (int i = 0; i < bytes.length / 2; i++) {
assertEquals(buffer.getShort(), stream.readShort());
}
assertThrows(EOFException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
stream.readShort();
}
});
stream.seek(0);
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
buffer.position(0);
buffer.order(ByteOrder.LITTLE_ENDIAN);
for (int i = 0; i < bytes.length / 2; i++) {
assertEquals(buffer.getShort(), stream.readShort());
}
assertThrows(EOFException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
stream.readShort();
}
});
}
}
@Test
public void testReadInt() throws IOException {
byte[] bytes = new byte[8743]; // Slightly more than one buffer size
File file = randomDataToFile(bytes);
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
try (final ImageInputStream stream = new BufferedFileImageInputStream(file)) {
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
for (int i = 0; i < bytes.length / 4; i++) {
assertEquals(buffer.getInt(), stream.readInt());
}
assertThrows(EOFException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
stream.readInt();
}
});
stream.seek(0);
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
buffer.position(0);
buffer.order(ByteOrder.LITTLE_ENDIAN);
for (int i = 0; i < bytes.length / 4; i++) {
assertEquals(buffer.getInt(), stream.readInt());
}
assertThrows(EOFException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
stream.readInt();
}
});
}
}
@Test
public void testReadLong() throws IOException {
byte[] bytes = new byte[8743]; // Slightly more than one buffer size
File file = randomDataToFile(bytes);
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
try (final ImageInputStream stream = new BufferedFileImageInputStream(file)) {
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
for (int i = 0; i < bytes.length / 8; i++) {
assertEquals(buffer.getLong(), stream.readLong());
}
assertThrows(EOFException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
stream.readLong();
}
});
stream.seek(0);
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
buffer.position(0);
buffer.order(ByteOrder.LITTLE_ENDIAN);
for (int i = 0; i < bytes.length / 8; i++) {
assertEquals(buffer.getLong(), stream.readLong());
}
assertThrows(EOFException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
stream.readLong();
}
});
}
}
@Test
public void testSeekPastEOF() throws IOException {
byte[] bytes = new byte[9];
File file = randomDataToFile(bytes);
try (final ImageInputStream stream = new BufferedFileImageInputStream(file)) {
stream.seek(1000);
assertEquals(-1, stream.read());
assertEquals(-1, stream.read(new byte[1], 0, 1));
assertThrows(EOFException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
stream.readFully(new byte[1]);
}
});
assertThrows(EOFException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
stream.readByte();
}
});
assertThrows(EOFException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
stream.readShort();
}
});
assertThrows(EOFException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
stream.readInt();
}
});
assertThrows(EOFException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
stream.readLong();
}
});
stream.seek(0);
for (byte value : bytes) {
assertEquals(value, stream.readByte());
}
assertEquals(-1, stream.read());
}
}
@Test
public void testClose() throws IOException {
// Create wrapper stream
RandomAccessFile mock = mock(RandomAccessFile.class);
ImageInputStream stream = new BufferedFileImageInputStream(mock);
stream.close();
verify(mock, only()).close();
}
@Test
public void testWorkaroundForWBMPImageReaderExpectsReadToBehaveAsReadFully() throws IOException {
// See #606 for details.
// Bug in JDK WBMPImageReader, uses read(byte[], int, int) instead of readFully(byte[], int, int).
// Ie: Relies on read to return all bytes at once, without blocking
int size = BufferedFileImageInputStream.DEFAULT_BUFFER_SIZE * 7;
byte[] bytes = new byte[size];
File file = randomDataToFile(bytes);
try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file)) {
byte[] result = new byte[size];
int head = stream.read(result, 0, 12); // Provoke a buffered read
int len = stream.read(result, 12, size - 12); // Rest of buffer + direct read
assertEquals(size, len + head);
assertArrayEquals(bytes, result);
}
}
}
@@ -1,18 +0,0 @@
package com.twelvemonkeys.imageio.stream;
import javax.imageio.spi.ImageInputStreamSpi;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
public class BufferedRAFImageInputStreamSpiTest extends ImageInputStreamSpiTest<RandomAccessFile> {
@Override
protected ImageInputStreamSpi createProvider() {
return new BufferedRAFImageInputStreamSpi();
}
@Override
protected RandomAccessFile createInput() throws IOException {
return new RandomAccessFile(File.createTempFile("test-", ".tst"), "r");
}
}
@@ -39,11 +39,11 @@ import static com.twelvemonkeys.imageio.stream.BufferedImageInputStreamTest.rang
import static org.junit.Assert.*;
/**
* ByteArrayImageInputStreamTest
* ByteArrayImageInputStreamTestCase
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: ByteArrayImageInputStreamTest.java,v 1.0 Apr 21, 2009 10:58:48 AM haraldk Exp$
* @version $Id: ByteArrayImageInputStreamTestCase.java,v 1.0 Apr 21, 2009 10:58:48 AM haraldk Exp$
*/
public class ByteArrayImageInputStreamTest {
private final Random random = new Random(1709843507234566L);
@@ -11,14 +11,14 @@ import java.util.Locale;
import static org.junit.Assert.*;
abstract class ImageInputStreamSpiTest<T> {
protected final ImageInputStreamSpi provider = createProvider();
private final ImageInputStreamSpi provider = createProvider();
@SuppressWarnings("unchecked")
protected final Class<T> inputClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
private final Class<T> inputClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
protected abstract ImageInputStreamSpi createProvider();
protected abstract T createInput() throws IOException;
protected abstract T createInput();
@Test
public void testInputClass() {
@@ -97,6 +97,10 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
protected abstract List<String> getMIMETypes();
protected boolean allowsNullRawImageType() {
return false;
}
protected static void failBecause(String message, Throwable exception) {
throw new AssertionError(message, exception);
}
@@ -217,7 +221,6 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
image = reader.read(i);
}
catch (Exception e) {
e.printStackTrace();
failBecause(String.format("Image %s index %s could not be read: %s", data.getInput(), i, e), e);
}
@@ -280,6 +283,26 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
reader.dispose();
}
@Test
public void testReadNoInput() throws IOException {
ImageReader reader = createReader();
// Do not set input
BufferedImage image = null;
try {
image = reader.read(0);
fail("Read image with no input");
}
catch (IllegalStateException ignore) {
}
catch (IOException e) {
failBecause("Image could not be read", e);
}
assertNull(image);
reader.dispose();
}
@Test
public void testReRead() throws IOException {
ImageReader reader = createReader();
@@ -300,71 +323,69 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
reader.dispose();
}
@Test(expected = IllegalStateException.class)
public void testReadNoInput() throws IOException {
ImageReader reader = createReader();
// Do not set input
try {
reader.read(0);
fail("Read image with no input");
}
catch (IOException e) {
failBecause("Image could not be read", e);
}
}
@Test(expected = IndexOutOfBoundsException.class)
@Test
public void testReadIndexNegativeWithParam() throws IOException {
ImageReader reader = createReader();
TestData data = getTestData().get(0);
reader.setInput(data.getInputStream());
BufferedImage image = null;
try {
reader.read(-1, reader.getDefaultReadParam());
image = reader.read(-1, reader.getDefaultReadParam());
fail("Read image with illegal index");
}
catch (IndexOutOfBoundsException ignore) {
}
catch (IOException e) {
failBecause("Image could not be read", e);
}
finally {
reader.dispose();
}
assertNull(image);
reader.dispose();
}
@Test(expected = IndexOutOfBoundsException.class)
@Test
public void testReadIndexOutOfBoundsWithParam() throws IOException {
ImageReader reader = createReader();
TestData data = getTestData().get(0);
reader.setInput(data.getInputStream());
BufferedImage image = null;
try {
reader.read(Short.MAX_VALUE, reader.getDefaultReadParam());
image = reader.read(Short.MAX_VALUE, reader.getDefaultReadParam());
fail("Read image with index out of bounds");
}
catch (IndexOutOfBoundsException ignore) {
}
catch (IOException e) {
failBecause("Image could not be read", e);
}
finally {
reader.dispose();
}
assertNull(image);
reader.dispose();
}
@Test(expected = IllegalStateException.class)
@Test
public void testReadNoInputWithParam() throws IOException {
ImageReader reader = createReader();
// Do not set input
BufferedImage image = null;
try {
reader.read(0, reader.getDefaultReadParam());
image = reader.read(0, reader.getDefaultReadParam());
fail("Read image with no input");
}
catch (IllegalStateException ignore) {
}
catch (IOException e) {
failBecause("Image could not be read", e);
}
finally {
reader.dispose();
}
assertNull(image);
reader.dispose();
}
@Test
@@ -1356,6 +1377,9 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
reader.setInput(data.getInputStream());
ImageTypeSpecifier rawType = reader.getRawImageType(0);
if (rawType == null && allowsNullRawImageType()) {
continue;
}
assertNotNull(rawType);
Iterator<ImageTypeSpecifier> types = reader.getImageTypes(0);
@@ -1377,7 +1401,6 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
assertTrue("ImageTypeSpecifier from getRawImageType should be in the iterator from getImageTypes", rawFound);
}
reader.dispose();
}
@@ -1626,72 +1649,12 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
BufferedImage one = reader.read(0);
BufferedImage two = reader.read(0);
// Test for same BufferedImage instance
assertNotSame("Multiple reads return same (mutable) image", one, two);
// Test for same backing storage (array)
one.setRGB(0, 0, Color.BLACK.getRGB());
two.setRGB(0, 0, Color.WHITE.getRGB());
one.setRGB(0, 0, Color.BLUE.getRGB());
two.setRGB(0, 0, Color.RED.getRGB());
assertTrue(one.getRGB(0, 0) != two.getRGB(0, 0));
reader.dispose();
}
@Test
public void testReadThumbnails() throws IOException {
T reader = createReader();
if (reader.readerSupportsThumbnails()) {
for (TestData testData : getTestData()) {
try (ImageInputStream inputStream = testData.getInputStream()) {
reader.setInput(inputStream);
int numImages = reader.getNumImages(true);
for (int i = 0; i < numImages; i++) {
int numThumbnails = reader.getNumThumbnails(0);
for (int t = 0; t < numThumbnails; t++) {
BufferedImage thumbnail = reader.readThumbnail(0, t);
assertNotNull(thumbnail);
}
}
}
}
}
reader.dispose();
}
@Test
public void testThumbnailProgress() throws IOException {
T reader = createReader();
IIOReadProgressListener listener = mock(IIOReadProgressListener.class);
reader.addIIOReadProgressListener(listener);
if (reader.readerSupportsThumbnails()) {
for (TestData testData : getTestData()) {
try (ImageInputStream inputStream = testData.getInputStream()) {
reader.setInput(inputStream);
int numThumbnails = reader.getNumThumbnails(0);
for (int i = 0; i < numThumbnails; i++) {
reset(listener);
reader.readThumbnail(0, i);
InOrder order = inOrder(listener);
order.verify(listener).thumbnailStarted(reader, 0, i);
order.verify(listener, atLeastOnce()).thumbnailProgress(reader, 100f);
order.verify(listener).thumbnailComplete(reader);
}
}
}
}
reader.dispose();
}
@@ -30,20 +30,14 @@
package com.twelvemonkeys.imageio.util;
import static org.junit.Assert.assertEquals;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DirectColorModel;
import java.awt.image.IndexColorModel;
import javax.imageio.ImageTypeSpecifier;
import com.twelvemonkeys.lang.Validate;
import org.junit.Test;
import com.twelvemonkeys.lang.Validate;
import javax.imageio.ImageTypeSpecifier;
import java.awt.color.ColorSpace;
import java.awt.image.*;
import static org.junit.Assert.assertEquals;
public class ImageTypeSpecifiersTest {
@@ -547,7 +541,8 @@ public class ImageTypeSpecifiersTest {
}
@Test
public void testCreatePackedGrayscale1BPP() {
public void testCreatePackedGrayscale1() {
// TODO: Fails on Java 11, because IndexColorModel now has an overloaded equals that actually tests the color entries
assertEquals(
ImageTypeSpecifier.createGrayscale(1, DataBuffer.TYPE_BYTE, false),
ImageTypeSpecifiers.createPackedGrayscale(GRAY, 1, DataBuffer.TYPE_BYTE)
@@ -555,8 +550,8 @@ public class ImageTypeSpecifiersTest {
}
@Test
public void testCreatePackedGrayscale2BPP() {
// TODO: Fails on Java 11+, because IndexColorModel now has an overloaded equals that actually tests the color entries
public void testCreatePackedGrayscale2() {
// TODO: Fails on Java 11, because IndexColorModel now has an overloaded equals that actually tests the color entries
assertEquals(
ImageTypeSpecifier.createGrayscale(2, DataBuffer.TYPE_BYTE, false),
ImageTypeSpecifiers.createPackedGrayscale(GRAY, 2, DataBuffer.TYPE_BYTE)
@@ -564,8 +559,8 @@ public class ImageTypeSpecifiersTest {
}
@Test
public void testCreatePackedGrayscale4BPP() {
// TODO: Fails on Java 11+, because IndexColorModel now has an overloaded equals that actually tests the color entries
public void testCreatePackedGrayscale4() throws Exception {
// TODO: Fails on Java 11, because IndexColorModel now has an overloaded equals that actually tests the color entries
assertEquals(
ImageTypeSpecifier.createGrayscale(4, DataBuffer.TYPE_BYTE, false),
ImageTypeSpecifiers.createPackedGrayscale(GRAY, 4, DataBuffer.TYPE_BYTE)
@@ -658,7 +653,7 @@ public class ImageTypeSpecifiersTest {
for (int bits = 1; bits <= 8; bits <<= 1) {
int[] colors = createIntLut(1 << bits);
assertEquals(
new IndexedImageTypeSpecifier(new IndexColorModel(bits, colors.length, colors, 0, false, -1, DataBuffer.TYPE_BYTE)),
IndexedImageTypeSpecifier.createFromIndexColorModel(new IndexColorModel(bits, colors.length, colors, 0, false, -1, DataBuffer.TYPE_BYTE)),
ImageTypeSpecifiers.createIndexed(colors, false, -1, bits, DataBuffer.TYPE_BYTE)
);
}
@@ -668,7 +663,7 @@ public class ImageTypeSpecifiersTest {
public void testCreateIndexedIntArray16() {
int[] colors = createIntLut(1 << 16);
assertEquals(
new IndexedImageTypeSpecifier(new IndexColorModel(16, colors.length, colors, 0, false, -1, DataBuffer.TYPE_USHORT)),
IndexedImageTypeSpecifier.createFromIndexColorModel(new IndexColorModel(16, colors.length, colors, 0, false, -1, DataBuffer.TYPE_USHORT)),
ImageTypeSpecifiers.createIndexed(colors, false, -1, 16, DataBuffer.TYPE_USHORT)
);
@@ -680,7 +675,7 @@ public class ImageTypeSpecifiersTest {
int[] colors = createIntLut(1 << bits);
IndexColorModel colorModel = new IndexColorModel(bits, colors.length, colors, 0, false, -1, DataBuffer.TYPE_BYTE);
assertEquals(
new IndexedImageTypeSpecifier(colorModel),
IndexedImageTypeSpecifier.createFromIndexColorModel(colorModel),
ImageTypeSpecifiers.createFromIndexColorModel(colorModel)
);
}
@@ -691,7 +686,7 @@ public class ImageTypeSpecifiersTest {
int[] colors = createIntLut(1 << 16);
IndexColorModel colorModel = new IndexColorModel(16, colors.length, colors, 0, false, -1, DataBuffer.TYPE_USHORT);
assertEquals(
new IndexedImageTypeSpecifier(colorModel),
IndexedImageTypeSpecifier.createFromIndexColorModel(colorModel),
ImageTypeSpecifiers.createFromIndexColorModel(colorModel)
);
}
@@ -30,17 +30,14 @@
package com.twelvemonkeys.imageio.util;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import javax.imageio.ImageTypeSpecifier;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.IndexColorModel;
import javax.imageio.ImageTypeSpecifier;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* IndexedImageTypeSpecifierTestCase
@@ -54,43 +51,46 @@ public class IndexedImageTypeSpecifierTest {
public void testEquals() {
IndexColorModel cm = new IndexColorModel(1, 2, new int[]{0xffffff, 0x00}, 0, false, -1, DataBuffer.TYPE_BYTE);
ImageTypeSpecifier spec = new IndexedImageTypeSpecifier(cm);
ImageTypeSpecifier other = new IndexedImageTypeSpecifier(cm);
ImageTypeSpecifier different = new IndexedImageTypeSpecifier(new IndexColorModel(2, 2, new int[]{0xff00ff, 0x00, 0xff00ff, 0x00}, 0, false, -1, DataBuffer.TYPE_BYTE));
ImageTypeSpecifier spec = IndexedImageTypeSpecifier.createFromIndexColorModel(cm);
ImageTypeSpecifier other = IndexedImageTypeSpecifier.createFromIndexColorModel(cm);
ImageTypeSpecifier different = IndexedImageTypeSpecifier.createFromIndexColorModel(new IndexColorModel(2, 2, new int[]{0xff00ff, 0x00, 0xff00ff, 0x00}, 0, false, -1, DataBuffer.TYPE_BYTE));
assertEquals(spec, other);
assertEquals(other, spec);
assertEquals(spec.hashCode(), other.hashCode());
assertTrue(spec.equals(other));
assertTrue(other.equals(spec));
// TODO: There is still a problem that IndexColorModel does not override equals,
// so any model with the same number of bits, transparency, and transfer type will be treated as equal
assertNotEquals(other, different);
assertFalse(other.equals(different));
}
@Test
public void testHashCode() {
IndexColorModel cm = new IndexColorModel(1, 2, new int[]{0xffffff, 0x00}, 0, false, -1, DataBuffer.TYPE_BYTE);
ImageTypeSpecifier spec = new IndexedImageTypeSpecifier(cm);
ImageTypeSpecifier other = new IndexedImageTypeSpecifier(cm);
ImageTypeSpecifier different = new IndexedImageTypeSpecifier(new IndexColorModel(2, 2, new int[]{0xff00ff, 0x00, 0xff00ff, 0x00}, 0, false, -1, DataBuffer.TYPE_BYTE));
ImageTypeSpecifier spec = IndexedImageTypeSpecifier.createFromIndexColorModel(cm);
ImageTypeSpecifier other = IndexedImageTypeSpecifier.createFromIndexColorModel(cm);
ImageTypeSpecifier different = IndexedImageTypeSpecifier.createFromIndexColorModel(new IndexColorModel(2, 2, new int[]{0xff00ff, 0x00, 0xff00ff, 0x00}, 0, false, -1, DataBuffer.TYPE_BYTE));
// TODO: There is still a problem that IndexColorModel does not override hashCode,
// so any model with the same number of bits, transparency, and transfer type will have same hash
assertEquals(spec.hashCode(), other.hashCode());
assertNotEquals(spec.hashCode(), different.hashCode());
assertFalse(spec.hashCode() == different.hashCode());
}
@Test(expected = IllegalArgumentException.class)
public void testCreateNull() {
new IndexedImageTypeSpecifier(null);
IndexedImageTypeSpecifier.createFromIndexColorModel(null);
}
@Test
public void testCreateBufferedImageBinary() {
IndexColorModel cm = new IndexColorModel(1, 2, new int[]{0xffffff, 0x00}, 0, false, -1, DataBuffer.TYPE_BYTE);
ImageTypeSpecifier spec = new IndexedImageTypeSpecifier(cm);
ImageTypeSpecifier spec = IndexedImageTypeSpecifier.createFromIndexColorModel(cm);
BufferedImage image = spec.createBufferedImage(2, 2);
@@ -102,7 +102,7 @@ public class IndexedImageTypeSpecifierTest {
@Test
public void testCreateBufferedImageIndexed() {
IndexColorModel cm = new IndexColorModel(8, 256, new int[256], 0, false, -1, DataBuffer.TYPE_BYTE);
ImageTypeSpecifier spec = new IndexedImageTypeSpecifier(cm);
ImageTypeSpecifier spec = IndexedImageTypeSpecifier.createFromIndexColorModel(cm);
BufferedImage image = spec.createBufferedImage(2, 2);
@@ -1,199 +0,0 @@
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));
}
}
}
}
}
-65
View File
@@ -1,65 +0,0 @@
<?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>
@@ -1,492 +0,0 @@
/*
* 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);
}
}
}
}
@@ -1,110 +0,0 @@
/*
* 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";
}
}
@@ -1,60 +0,0 @@
/*
* 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 @@
com.twelvemonkeys.imageio.plugins.cr2.CR2ImageReaderSpi
@@ -1,101 +0,0 @@
/*
* 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();
}
}
@@ -1,7 +0,0 @@
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
-65
View File
@@ -1,65 +0,0 @@
<?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>
@@ -1,12 +0,0 @@
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;
}
@@ -1,388 +0,0 @@
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;
}
}
@@ -1,294 +0,0 @@
/*
* 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);
}
}
}
}
@@ -1,150 +0,0 @@
/*
* 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";
}
}
@@ -1,29 +0,0 @@
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
);
}
}
@@ -1,109 +0,0 @@
/*
* 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;
}
}
@@ -1,158 +0,0 @@
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
}
@@ -1,30 +0,0 @@
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();
}
}
@@ -1,115 +0,0 @@
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));
}
}
}
@@ -1,200 +0,0 @@
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 @@
com.twelvemonkeys.imageio.plugins.crw.CRWImageReaderSpi
@@ -1,92 +0,0 @@
/*
* 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();
}
}
-65
View File
@@ -1,65 +0,0 @@
<?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>
@@ -1,127 +0,0 @@
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;
}
@@ -1,104 +0,0 @@
/*
* 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";
}
}
@@ -1,61 +0,0 @@
/*
* 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
);
}
}
@@ -1,100 +0,0 @@
/*
* 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();
}
}
Binary file not shown.
+1 -5
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.8.0-SNAPSHOT</version>
<version>3.6.4</version>
</parent>
<artifactId>imageio-hdr</artifactId>
<name>TwelveMonkeys :: ImageIO :: HDR plugin</name>
@@ -12,10 +12,6 @@
ImageIO plugin for Radiance RGBE High Dynaimc Range format (HDR).
</description>
<properties>
<project.jpms.module.name>com.twelvemonkeys.imageio.hdr</project.jpms.module.name>
</properties>
<dependencies>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
+1 -5
View File
@@ -4,16 +4,12 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.8.0-SNAPSHOT</version>
<version>3.6.4</version>
</parent>
<artifactId>imageio-icns</artifactId>
<name>TwelveMonkeys :: ImageIO :: ICNS plugin</name>
<description>ImageIO plugin for Apple Icon Image (ICNS) format.</description>
<properties>
<project.jpms.module.name>com.twelvemonkeys.imageio.icns</project.jpms.module.name>
</properties>
<dependencies>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
+1 -5
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.8.0-SNAPSHOT</version>
<version>3.6.4</version>
</parent>
<artifactId>imageio-iff</artifactId>
<name>TwelveMonkeys :: ImageIO :: IFF plugin</name>
@@ -13,10 +13,6 @@
type ILBM and PBM format.
</description>
<properties>
<project.jpms.module.name>com.twelvemonkeys.imageio.iff</project.jpms.module.name>
</properties>
<dependencies>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
@@ -102,15 +102,14 @@ abstract class AbstractMultiPaletteChunk extends IFFChunk implements MultiPalett
}
@Override
void writeChunk(DataOutput pOutput) {
void writeChunk(DataOutput pOutput) throws IOException {
throw new UnsupportedOperationException("Method writeChunk not implemented");
}
@Override
public ColorModel getColorModel(final IndexColorModel colorModel, final int rowIndex, final boolean laced) {
if (rowIndex < lastRow || mutablePalette == null || originalPalette != null && originalPalette.get() != colorModel) {
originalPalette = new WeakReference<>(colorModel);
originalPalette = new WeakReference<IndexColorModel>(colorModel);
mutablePalette = new MutableIndexColorModel(colorModel);
if (initialChanges != null) {
@@ -30,12 +30,11 @@
package com.twelvemonkeys.imageio.plugins.iff;
import javax.imageio.IIOException;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import javax.imageio.IIOException;
/**
* BMHDChunk
*
@@ -130,8 +129,7 @@ final class BMHDChunk extends IFFChunk {
pageHeight = Math.min(pHeight, Short.MAX_VALUE);
}
@Override
void readChunk(final DataInput pInput) throws IOException {
void readChunk(DataInput pInput) throws IOException {
if (chunkLength != 20) {
throw new IIOException("Unknown BMHD chunk length: " + chunkLength);
}
@@ -150,8 +148,7 @@ final class BMHDChunk extends IFFChunk {
pageHeight = pInput.readShort();
}
@Override
void writeChunk(final DataOutput pOutput) throws IOException {
void writeChunk(DataOutput pOutput) throws IOException {
pOutput.writeInt(chunkId);
pOutput.writeInt(chunkLength);
@@ -170,7 +167,6 @@ final class BMHDChunk extends IFFChunk {
pOutput.writeShort(pageHeight);
}
@Override
public String toString() {
return super.toString()
+ " {w=" + width + ", h=" + height
@@ -32,6 +32,7 @@ package com.twelvemonkeys.imageio.plugins.iff;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
/**
* BODYChunk
@@ -44,13 +45,11 @@ final class BODYChunk extends IFFChunk {
super(IFF.CHUNK_BODY, pChunkLength);
}
@Override
void readChunk(final DataInput pInput) {
void readChunk(DataInput pInput) throws IOException {
throw new InternalError("BODY chunk should only be read from IFFImageReader");
}
@Override
void writeChunk(final DataOutput pOutput) {
void writeChunk(DataOutput pOutput) throws IOException {
throw new InternalError("BODY chunk should only be written from IFFImageWriter");
}
}
@@ -30,12 +30,11 @@
package com.twelvemonkeys.imageio.plugins.iff;
import javax.imageio.IIOException;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import javax.imageio.IIOException;
/**
* CAMGChunk
*
@@ -47,14 +46,13 @@ final class CAMGChunk extends IFFChunk {
// #define CAMG_HAM 0x800 /* hold and modify */
// #define CAMG_EHB 0x80 /* extra halfbrite */
int camg;
private int camg;
public CAMGChunk(int pLength) {
super(IFF.CHUNK_CAMG, pLength);
}
@Override
void readChunk(final DataInput pInput) throws IOException {
void readChunk(DataInput pInput) throws IOException {
if (chunkLength != 4) {
throw new IIOException("Unknown CAMG chunk length: " + chunkLength);
}
@@ -62,8 +60,7 @@ final class CAMGChunk extends IFFChunk {
camg = pInput.readInt();
}
@Override
void writeChunk(final DataOutput pOutput) {
void writeChunk(DataOutput pOutput) throws IOException {
throw new InternalError("Not implemented: writeChunk()");
}
@@ -83,7 +80,6 @@ final class CAMGChunk extends IFFChunk {
return (camg & 0x80) != 0;
}
@Override
public String toString() {
return super.toString() + " {mode=" + (isHAM() ? "HAM" : isEHB() ? "EHB" : "Normal") + "}";
}
@@ -30,6 +30,7 @@
package com.twelvemonkeys.imageio.plugins.iff;
import javax.imageio.IIOException;
import java.awt.image.BufferedImage;
import java.awt.image.IndexColorModel;
import java.awt.image.WritableRaster;
@@ -38,8 +39,6 @@ import java.io.DataOutput;
import java.io.IOException;
import java.util.Arrays;
import javax.imageio.IIOException;
/**
* CMAPChunk
*
@@ -69,7 +68,6 @@ final class CMAPChunk extends IFFChunk {
model = pModel;
}
@Override
void readChunk(final DataInput pInput) throws IOException {
int numColors = chunkLength / 3;
@@ -97,7 +95,6 @@ final class CMAPChunk extends IFFChunk {
}
}
@Override
void writeChunk(final DataOutput pOutput) throws IOException {
pOutput.writeInt(chunkId);
pOutput.writeInt(chunkLength);
@@ -115,7 +112,6 @@ final class CMAPChunk extends IFFChunk {
}
}
@Override
public String toString() {
return super.toString() + " {colorMap=" + model + "}";
}
@@ -35,10 +35,10 @@ import java.io.DataOutput;
import java.io.IOException;
/**
* GenericChunk
* UnknownChunk
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: GenericChunk.java,v 1.0 28.feb.2006 00:53:47 haku Exp$
* @version $Id: UnknownChunk.java,v 1.0 28.feb.2006 00:53:47 haku Exp$
*/
final class GenericChunk extends IFFChunk {
@@ -46,7 +46,7 @@ final class GenericChunk extends IFFChunk {
protected GenericChunk(int pChunkId, int pChunkLength) {
super(pChunkId, pChunkLength);
data = new byte[chunkLength];
data = new byte[pChunkLength <= 50 ? pChunkLength : 47];
}
protected GenericChunk(int pChunkId, byte[] pChunkData) {
@@ -54,15 +54,13 @@ final class GenericChunk extends IFFChunk {
data = pChunkData;
}
@Override
void readChunk(final DataInput pInput) throws IOException {
void readChunk(DataInput pInput) throws IOException {
pInput.readFully(data, 0, data.length);
skipData(pInput, chunkLength, data.length);
}
@Override
void writeChunk(final DataOutput pOutput) throws IOException {
void writeChunk(DataOutput pOutput) throws IOException {
pOutput.writeInt(chunkId);
pOutput.writeInt(chunkLength);
pOutput.write(data, 0, data.length);
@@ -72,7 +70,6 @@ final class GenericChunk extends IFFChunk {
}
}
@Override
public String toString() {
return super.toString() + " {value=\""
+ new String(data, 0, data.length <= 50 ? data.length : 47)
@@ -91,12 +91,6 @@ interface IFF {
/** EA IFF 85 Generic Copyright text chunk */
int CHUNK_COPY = ('(' << 24) + ('c' << 16) + (')' << 8) + ' ';
/** EA IFF 85 Generic annotation chunk (usually used for Software) */
int CHUNK_ANNO = ('A' << 24) + ('N' << 16) + ('N' << 8) + 'O';;
/** Third-party defined UTF-8 text. */
int CHUNK_UTF8 = ('U' << 24) + ('T' << 16) + ('F' << 8) + '8';
/** color cycling */
int CHUNK_CRNG = ('C' << 24) + ('R' << 16) + ('N' << 8) + 'G';
/** color cycling */
@@ -1,269 +0,0 @@
package com.twelvemonkeys.imageio.plugins.iff;
import com.twelvemonkeys.imageio.AbstractMetadata;
import javax.imageio.metadata.IIOMetadataNode;
import java.awt.*;
import java.awt.image.IndexColorModel;
import java.nio.charset.StandardCharsets;
import java.util.List;
import static com.twelvemonkeys.imageio.plugins.iff.IFF.*;
import static com.twelvemonkeys.lang.Validate.isTrue;
import static com.twelvemonkeys.lang.Validate.notNull;
final class IFFImageMetadata extends AbstractMetadata {
private final int formType;
private final BMHDChunk header;
private final IndexColorModel colorMap;
private final CAMGChunk viewPort;
private final List<GenericChunk> meta;
IFFImageMetadata(int formType, BMHDChunk header, IndexColorModel colorMap, CAMGChunk viewPort, List<GenericChunk> meta) {
this.formType = isTrue(validFormType(formType), formType, "Unknown IFF Form type: %s");
this.header = notNull(header, "header");
this.colorMap = colorMap;
this.viewPort = viewPort;
this.meta = meta;
}
private boolean validFormType(int formType) {
switch (formType) {
case TYPE_ACBM:
case TYPE_DEEP:
case TYPE_ILBM:
case TYPE_PBM:
case TYPE_RGB8:
case TYPE_RGBN:
return true;
default:
return false;
}
}
@Override
protected IIOMetadataNode getStandardChromaNode() {
IIOMetadataNode chroma = new IIOMetadataNode("Chroma");
IIOMetadataNode csType = new IIOMetadataNode("ColorSpaceType");
chroma.appendChild(csType);
switch (header.bitplanes) {
case 8:
if (colorMap == null) {
csType.setAttribute("name", "GRAY");
break;
}
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 24:
case 32:
csType.setAttribute("name", "RGB");
break;
default:
csType.setAttribute("name", "Unknown");
}
// NOTE: Channels in chroma node reflects channels in color model (see data node, for channels in data)
IIOMetadataNode numChannels = new IIOMetadataNode("NumChannels");
chroma.appendChild(numChannels);
if (colorMap == null && header.bitplanes == 8) {
numChannels.setAttribute("value", Integer.toString(1));
}
else if (header.bitplanes == 32) {
numChannels.setAttribute("value", Integer.toString(4));
}
else {
numChannels.setAttribute("value", Integer.toString(3));
}
IIOMetadataNode blackIsZero = new IIOMetadataNode("BlackIsZero");
chroma.appendChild(blackIsZero);
blackIsZero.setAttribute("value", "TRUE");
// NOTE: TGA files may contain a color map, even if true color...
// Not sure if this is a good idea to expose to the meta data,
// as it might be unexpected... Then again...
if (colorMap != null) {
IIOMetadataNode palette = new IIOMetadataNode("Palette");
chroma.appendChild(palette);
for (int i = 0; i < colorMap.getMapSize(); i++) {
IIOMetadataNode paletteEntry = new IIOMetadataNode("PaletteEntry");
palette.appendChild(paletteEntry);
paletteEntry.setAttribute("index", Integer.toString(i));
paletteEntry.setAttribute("red", Integer.toString(colorMap.getRed(i)));
paletteEntry.setAttribute("green", Integer.toString(colorMap.getGreen(i)));
paletteEntry.setAttribute("blue", Integer.toString(colorMap.getBlue(i)));
}
}
// TODO: Background color is the color of the transparent index in the color model?
// if (extensions != null && extensions.getBackgroundColor() != 0) {
// Color background = new Color(extensions.getBackgroundColor(), true);
//
// IIOMetadataNode backgroundColor = new IIOMetadataNode("BackgroundColor");
// chroma.appendChild(backgroundColor);
//
// backgroundColor.setAttribute("red", Integer.toString(background.getRed()));
// backgroundColor.setAttribute("green", Integer.toString(background.getGreen()));
// backgroundColor.setAttribute("blue", Integer.toString(background.getBlue()));
// }
return chroma;
}
@Override
protected IIOMetadataNode getStandardCompressionNode() {
if (header.compressionType == BMHDChunk.COMPRESSION_NONE) {
return null; // All defaults
}
IIOMetadataNode node = new IIOMetadataNode("Compression");
IIOMetadataNode compressionTypeName = new IIOMetadataNode("CompressionTypeName");
compressionTypeName.setAttribute("value", "RLE");
node.appendChild(compressionTypeName);
IIOMetadataNode lossless = new IIOMetadataNode("Lossless");
lossless.setAttribute("value", "TRUE");
node.appendChild(lossless);
return node;
}
@Override
protected IIOMetadataNode getStandardDataNode() {
IIOMetadataNode data = new IIOMetadataNode("Data");
// PlanarConfiguration
IIOMetadataNode planarConfiguration = new IIOMetadataNode("PlanarConfiguration");
switch (formType) {
case TYPE_PBM:
planarConfiguration.setAttribute("value", "PixelInterleaved");
break;
case TYPE_ILBM:
planarConfiguration.setAttribute("value", "PlaneInterleaved");
break;
default:
planarConfiguration.setAttribute("value", "Unknown " + IFFUtil.toChunkStr(formType));
break;
}
data.appendChild(planarConfiguration);
IIOMetadataNode sampleFormat = new IIOMetadataNode("SampleFormat");
sampleFormat.setAttribute("value", colorMap != null ? "Index" : "UnsignedIntegral");
data.appendChild(sampleFormat);
// BitsPerSample
IIOMetadataNode bitsPerSample = new IIOMetadataNode("BitsPerSample");
String value = bitsPerSampleValue(header.bitplanes);
bitsPerSample.setAttribute("value", value);
data.appendChild(bitsPerSample);
// SignificantBitsPerSample not in format
// SampleMSB not in format
return data;
}
private String bitsPerSampleValue(int bitplanes) {
switch (bitplanes) {
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
return Integer.toString(bitplanes);
case 24:
return "8 8 8";
case 32:
return "8 8 8 8";
default:
throw new IllegalArgumentException("Ubknown bit count: " + bitplanes);
}
}
@Override
protected IIOMetadataNode getStandardDimensionNode() {
if (viewPort == null) {
return null;
}
IIOMetadataNode dimension = new IIOMetadataNode("Dimension");
// PixelAspectRatio
IIOMetadataNode pixelAspectRatio = new IIOMetadataNode("PixelAspectRatio");
pixelAspectRatio.setAttribute("value", String.valueOf((viewPort.isHires() ? 2f : 1f) / (viewPort.isLaced() ? 2f : 1f)));
dimension.appendChild(pixelAspectRatio);
// TODO: HorizontalScreenSize?
// TODO: VerticalScreenSize?
return dimension;
}
@Override
protected IIOMetadataNode getStandardDocumentNode() {
IIOMetadataNode document = new IIOMetadataNode("Document");
IIOMetadataNode formatVersion = new IIOMetadataNode("FormatVersion");
document.appendChild(formatVersion);
formatVersion.setAttribute("value", "1.0");
return document;
}
@Override
protected IIOMetadataNode getStandardTextNode() {
if (meta.isEmpty()) {
return null;
}
IIOMetadataNode text = new IIOMetadataNode("Text");
// /Text/TextEntry@keyword = field name, /Text/TextEntry@value = field value.
for (GenericChunk chunk : meta) {
IIOMetadataNode node = new IIOMetadataNode("TextEntry");
node.setAttribute("keyword", IFFUtil.toChunkStr(chunk.chunkId));
node.setAttribute("value", new String(chunk.data, chunk.chunkId == IFF.CHUNK_UTF8 ? StandardCharsets.UTF_8 : StandardCharsets.US_ASCII));
text.appendChild(node);
}
return text;
}
@Override
protected IIOMetadataNode getStandardTransparencyNode() {
if ((colorMap == null || !colorMap.hasAlpha()) && header.bitplanes != 32) {
return null;
}
IIOMetadataNode transparency = new IIOMetadataNode("Transparency");
if (header.bitplanes == 32) {
IIOMetadataNode alpha = new IIOMetadataNode("Alpha");
alpha.setAttribute("value", "nonpremultiplied");
transparency.appendChild(alpha);
}
if (colorMap != null && colorMap.getTransparency() == Transparency.BITMASK) {
IIOMetadataNode transparentIndex = new IIOMetadataNode("TransparentIndex");
transparentIndex.setAttribute("value", Integer.toString(colorMap.getTransparentPixel()));
transparency.appendChild(transparentIndex);
}
return transparency;
}
}
@@ -32,13 +32,13 @@ package com.twelvemonkeys.imageio.plugins.iff;
import com.twelvemonkeys.image.ResampleOp;
import com.twelvemonkeys.imageio.ImageReaderBase;
import com.twelvemonkeys.imageio.stream.BufferedImageInputStream;
import com.twelvemonkeys.imageio.util.IIOUtil;
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
import com.twelvemonkeys.io.enc.DecoderStream;
import com.twelvemonkeys.io.enc.PackBitsDecoder;
import javax.imageio.*;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
@@ -47,7 +47,6 @@ import java.awt.image.*;
import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
@@ -97,7 +96,7 @@ import java.util.List;
* @see <a href="http://en.wikipedia.org/wiki/Interchange_File_Format">Wikipedia: IFF</a>
* @see <a href="http://en.wikipedia.org/wiki/ILBM">Wikipedia: IFF ILBM</a>
*/
public final class IFFImageReader extends ImageReaderBase {
public class IFFImageReader extends ImageReaderBase {
// http://home.comcast.net/~erniew/lwsdk/docs/filefmts/ilbm.html
// http://www.fileformat.info/format/iff/spec/7866a9f0e53c42309af667c5da3bd426/view.htm
// - Contains definitions of some "new" chunks, as well as alternative FORM types
@@ -112,14 +111,17 @@ public final class IFFImageReader extends ImageReaderBase {
private GRABChunk grab;
private CAMGChunk viewPort;
private MultiPalette paletteChange;
private final List<GenericChunk> meta = new ArrayList<>();
private int formType;
private long bodyStart;
private BufferedImage image;
private DataInputStream byteRunStream;
IFFImageReader(ImageReaderSpi pProvider) {
public IFFImageReader() {
super(new IFFImageReaderSpi());
}
protected IFFImageReader(ImageReaderSpi pProvider) {
super(pProvider);
}
@@ -131,7 +133,6 @@ public final class IFFImageReader extends ImageReaderBase {
}
}
@Override
protected void resetMembers() {
header = null;
colorMap = null;
@@ -139,7 +140,6 @@ public final class IFFImageReader extends ImageReaderBase {
body = null;
viewPort = null;
formType = 0;
meta.clear();
image = null;
byteRunStream = null;
@@ -258,6 +258,11 @@ public final class IFFImageReader extends ImageReaderBase {
// System.out.println(ctbl);
break;
case IFF.CHUNK_JUNK:
// Always skip junk chunks
IFFChunk.skipData(imageInput, length, 0);
break;
case IFF.CHUNK_BODY:
if (body != null) {
throw new IIOException("Multiple BODY chunks not allowed");
@@ -269,32 +274,18 @@ public final class IFFImageReader extends ImageReaderBase {
// NOTE: We don't read the body here, it's done later in the read(int, ImageReadParam) method
// Done reading meta
return;
case IFF.CHUNK_ANNO:
case IFF.CHUNK_AUTH:
case IFF.CHUNK_COPY:
case IFF.CHUNK_NAME:
case IFF.CHUNK_TEXT:
case IFF.CHUNK_UTF8:
GenericChunk generic = new GenericChunk(chunkId, length);
default:
// TODO: We probably want to store ANNO, TEXT, AUTH, COPY etc chunks as Metadata
// SHAM, ANNO, DEST, SPRT and more
IFFChunk generic = new GenericChunk(chunkId, length);
generic.readChunk(imageInput);
meta.add(generic);
// System.out.println(generic);
break;
case IFF.CHUNK_JUNK:
// Always skip junk chunks
default:
// TODO: SHAM, DEST, SPRT and more
// Everything else, we'll just skip
IFFChunk.skipData(imageInput, length, 0);
break;
}
}
}
@Override
public BufferedImage read(int pIndex, ImageReadParam pParam) throws IOException {
init(pIndex);
@@ -323,26 +314,16 @@ public final class IFFImageReader extends ImageReaderBase {
return result;
}
@Override
public int getWidth(int pIndex) throws IOException {
init(pIndex);
return header.width;
}
@Override
public int getHeight(int pIndex) throws IOException {
init(pIndex);
return header.height;
}
@Override
public IIOMetadata getImageMetadata(int imageIndex) throws IOException {
init(imageIndex);
return new IFFImageMetadata(formType, header, colorMap != null ? colorMap.getIndexColorModel(header, isEHB()) : null, viewPort, meta);
}
@Override
public Iterator<ImageTypeSpecifier> getImageTypes(int pIndex) throws IOException {
init(pIndex);
@@ -382,11 +363,12 @@ public final class IFFImageReader extends ImageReaderBase {
if (colorMap != null) {
IndexColorModel cm = colorMap.getIndexColorModel(header, isEHB());
specifier = ImageTypeSpecifiers.createFromIndexColorModel(cm);
break;
}
else {
specifier = ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY);
break;
}
break;
}
// NOTE: HAM modes falls through, as they are converted to RGB
case 24:
@@ -804,7 +786,7 @@ public final class IFFImageReader extends ImageReaderBase {
}
public static void main(String[] pArgs) throws IOException {
ImageReader reader = new IFFImageReader(new IFFImageReaderSpi());
ImageReader reader = new IFFImageReader();
boolean scale = false;
for (String arg : pArgs) {
@@ -818,7 +800,8 @@ public final class IFFImageReader extends ImageReaderBase {
continue;
}
try (ImageInputStream input = ImageIO.createImageInputStream(file)) {
try {
ImageInputStream input = new BufferedImageInputStream(ImageIO.createImageInputStream(file));
boolean canRead = reader.getOriginatingProvider().canDecodeInput(input);
if (canRead) {

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