Compare commits

..

81 Commits

Author SHA1 Message Date
Harald Kuhr 873420286d [maven-release-plugin] prepare for next development iteration 2021-03-08 14:31:07 +01:00
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
222 changed files with 1618 additions and 14269 deletions
+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
+53 -54
View File
@@ -23,32 +23,31 @@ 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) | In progress
| 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](http://xmlgraphics.apache.org/security.html),
@@ -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.6.4</version>
<version>3.6.3</version>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-tiff</artifactId>
<version>3.6.4</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.6.4</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.6.4.jar
twelvemonkeys-common-io-3.6.4.jar
twelvemonkeys-common-image-3.6.4.jar
twelvemonkeys-imageio-core-3.6.4.jar
twelvemonkeys-imageio-metadata-3.6.4.jar
twelvemonkeys-imageio-jpeg-3.6.4.jar
twelvemonkeys-imageio-tiff-3.6.4.jar
twelvemonkeys-common-lang-3.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,42 +369,42 @@ Other "fat" JAR bundlers will probably have similar mechanisms to merge entries
### Links to prebuilt binaries
##### Latest version (3.6.4)
##### Latest version (3.6.3)
Requires Java 7 or later.
Common dependencies
* [common-lang-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-lang/3.6.4/common-lang-3.6.4.jar)
* [common-io-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-io/3.6.4/common-io-3.6.4.jar)
* [common-image-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-image/3.6.4/common-image-3.6.4.jar)
* [common-lang-3.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.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-core/3.6.4/imageio-core-3.6.4.jar)
* [imageio-metadata-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-metadata/3.6.4/imageio-metadata-3.6.4.jar)
* [imageio-core-3.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.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-bmp/3.6.4/imageio-bmp-3.6.4.jar)
* [imageio-hdr-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-hdr/3.6.4/imageio-hdr-3.6.4.jar)
* [imageio-icns-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-icns/3.6.4/imageio-icns-3.6.4.jar)
* [imageio-iff-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-iff/3.6.4/imageio-iff-3.6.4.jar)
* [imageio-jpeg-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jpeg/3.6.4/imageio-jpeg-3.6.4.jar)
* [imageio-pcx-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pcx/3.6.4/imageio-pcx-3.6.4.jar)
* [imageio-pict-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pict/3.6.4/imageio-pict-3.6.4.jar)
* [imageio-pnm-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pnm/3.6.4/imageio-pnm-3.6.4.jar)
* [imageio-psd-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-psd/3.6.4/imageio-psd-3.6.4.jar)
* [imageio-sgi-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-sgi/3.6.4/imageio-sgi-3.6.4.jar)
* [imageio-tga-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tga/3.6.4/imageio-tga-3.6.4.jar)
* [imageio-thumbsdb-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-thumbsdb/3.6.4/imageio-thumbsdb-3.6.4.jar)
* [imageio-tiff-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tiff/3.6.4/imageio-tiff-3.6.4.jar)
* [imageio-bmp-3.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.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-batik/3.6.4/imageio-batik-3.6.4.jar)
* [imageio-batik-3.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.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-clippath/3.6.4/imageio-clippath-3.6.4.jar)
* [imageio-clippath-3.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.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/servlet/servlet/3.6.4/servlet-3.6.4.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)
+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<groupId>com.twelvemonkeys</groupId>
<artifactId>twelvemonkeys</artifactId>
<version>3.7.0</version>
<version>3.6.5-SNAPSHOT</version>
</parent>
<groupId>com.twelvemonkeys.bom</groupId>
+1 -5
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.common</groupId>
<artifactId>common</artifactId>
<version>3.7.0</version>
<version>3.6.5-SNAPSHOT</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.7.0</version>
<version>3.6.5-SNAPSHOT</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.7.0</version>
<version>3.6.5-SNAPSHOT</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.7.0</version>
<version>3.6.5-SNAPSHOT</version>
</parent>
<groupId>com.twelvemonkeys.common</groupId>
<artifactId>common</artifactId>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys</groupId>
<artifactId>twelvemonkeys</artifactId>
<version>3.7.0</version>
<version>3.6.5-SNAPSHOT</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.7.0</version>
<version>3.6.5-SNAPSHOT</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.7.0</version>
<version>3.6.5-SNAPSHOT</version>
</parent>
<artifactId>imageio-bmp</artifactId>
<name>TwelveMonkeys :: ImageIO :: BMP plugin</name>
<description>ImageIO plugin for Microsoft Device Independent Bitmap (BMP/DIB) format.</description>
<properties>
<project.jpms.module.name>com.twelvemonkeys.imageio.bmp</project.jpms.module.name>
</properties>
<dependencies>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
@@ -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.7.0</version>
<version>3.6.5-SNAPSHOT</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.7.0</version>
<version>3.6.5-SNAPSHOT</version>
</parent>
<artifactId>imageio-core</artifactId>
<name>TwelveMonkeys :: ImageIO :: Core</name>
<properties>
<project.jpms.module.name>com.twelvemonkeys.imageio.core</project.jpms.module.name>
</properties>
<dependencies>
<dependency>
<groupId>com.twelvemonkeys.common</groupId>
@@ -265,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,251 +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 {
bufferPos = 0;
int length = raf.read(buffer, 0, buffer.length);
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[] pBuffer, final int pOffset, final int pLength) throws IOException {
checkClosed();
bitOffset = 0;
if (bufferEmpty()) {
// Bypass buffer if buffer is empty for reads longer than buffer
if (pLength >= buffer.length) {
return readDirect(pBuffer, pOffset, pLength);
}
else if (!fillBuffer()) {
return -1;
}
}
return readBuffered(pBuffer, pOffset, pLength);
}
private int readDirect(final byte[] pBuffer, final int pOffset, final int pLength) throws IOException {
// Invalidate the buffer, as its contents is no longer in sync with the stream's position.
bufferLimit = 0;
int read = raf.read(pBuffer, pOffset, pLength);
if (read > 0) {
streamPos += read;
}
return read;
}
private int readBuffered(final byte[] pBuffer, final int pOffset, final int pLength) {
// Read as much as possible from buffer
int length = Math.min(bufferLimit - bufferPos, pLength);
if (length > 0) {
System.arraycopy(buffer, bufferPos, pBuffer, pOffset, length);
bufferPos += length;
streamPos += length;
}
return length;
}
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!");
}
}
};
}
}
@@ -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,386 +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 {
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);
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);
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);
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);
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 testReadBitRandom() throws IOException {
byte[] bytes = new byte[8];
File file = randomDataToFile(bytes);
long value = ByteBuffer.wrap(bytes).getLong();
// Create stream
ImageInputStream stream = new BufferedFileImageInputStream(file);
for (int i = 1; i <= 64; i++) {
assertEquals(String.format("bit %d differ", i), (value << (i - 1L)) >>> 63L, stream.readBit());
}
}
@Test
public void testReadBitsRandom() throws IOException {
byte[] bytes = new byte[8];
File file = randomDataToFile(bytes);
long value = ByteBuffer.wrap(bytes).getLong();
// Create stream
ImageInputStream stream = new BufferedFileImageInputStream(file);
for (int i = 1; i <= 64; i++) {
stream.seek(0);
assertEquals(String.format("bit %d differ", i), value >>> (64L - i), stream.readBits(i));
assertEquals(i % 8, stream.getBitOffset());
}
}
@Test
public void testReadBitsRandomOffset() throws IOException {
byte[] bytes = new byte[8];
File file = randomDataToFile(bytes);
long value = ByteBuffer.wrap(bytes).getLong();
// Create stream
ImageInputStream stream = new BufferedFileImageInputStream(file);
for (int i = 1; i <= 60; i++) {
stream.seek(0);
stream.setBitOffset(i % 8);
assertEquals(String.format("bit %d differ", i), (value << (i % 8)) >>> (64L - i), stream.readBits(i));
assertEquals(i * 2 % 8, stream.getBitOffset());
}
}
@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);
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);
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);
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);
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();
}
}
@@ -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 -5
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.7.0</version>
<version>3.6.5-SNAPSHOT</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.7.0</version>
<version>3.6.5-SNAPSHOT</version>
</parent>
<artifactId>imageio-icns</artifactId>
<name>TwelveMonkeys :: ImageIO :: ICNS plugin</name>
<description>ImageIO plugin for Apple Icon Image (ICNS) format.</description>
<properties>
<project.jpms.module.name>com.twelvemonkeys.imageio.icns</project.jpms.module.name>
</properties>
<dependencies>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
+1 -5
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.7.0</version>
<version>3.6.5-SNAPSHOT</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) {
@@ -30,13 +30,12 @@
package com.twelvemonkeys.imageio.plugins.iff;
import java.io.IOException;
import java.util.Locale;
import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
import java.io.IOException;
import java.util.Locale;
/**
* IFFImageReaderSpi
@@ -53,7 +52,6 @@ public final class IFFImageReaderSpi extends ImageReaderSpiBase {
super(new IFFProviderInfo());
}
@Override
public boolean canDecodeInput(Object pSource) throws IOException {
return pSource instanceof ImageInputStream && canDecode((ImageInputStream) pSource);
}
@@ -82,12 +80,10 @@ public final class IFFImageReaderSpi extends ImageReaderSpiBase {
return false;
}
@Override
public ImageReader createReaderInstance(Object pExtension) throws IOException {
return new IFFImageReader(this);
}
@Override
public String getDescription(Locale pLocale) {
return "Commodore Amiga/Electronic Arts Image Interchange Format (IFF) image reader";
}
@@ -30,31 +30,22 @@
package com.twelvemonkeys.imageio.plugins.iff;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.IndexColorModel;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.spi.ImageWriterSpi;
import com.twelvemonkeys.imageio.ImageWriterBase;
import com.twelvemonkeys.imageio.util.IIOUtil;
import com.twelvemonkeys.io.FastByteArrayOutputStream;
import com.twelvemonkeys.io.enc.EncoderStream;
import com.twelvemonkeys.io.enc.PackBitsEncoder;
import javax.imageio.*;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.spi.ImageWriterSpi;
import java.awt.*;
import java.awt.image.*;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
/**
* Writer for Commodore Amiga (Electronic Arts) IFF ILBM (InterLeaved BitMap) format.
* The IFF format (Interchange File Format) is the standard file format
@@ -66,23 +57,24 @@ import com.twelvemonkeys.io.enc.PackBitsEncoder;
* @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 IFFImageWriter extends ImageWriterBase {
public class IFFImageWriter extends ImageWriterBase {
IFFImageWriter(ImageWriterSpi pProvider) {
public IFFImageWriter() {
this(null);
}
protected IFFImageWriter(ImageWriterSpi pProvider) {
super(pProvider);
}
@Override
public IIOMetadata getDefaultImageMetadata(ImageTypeSpecifier imageType, ImageWriteParam param) {
throw new UnsupportedOperationException("Method getDefaultImageMetadata not implemented");// TODO: Implement
}
@Override
public IIOMetadata convertImageMetadata(IIOMetadata inData, ImageTypeSpecifier imageType, ImageWriteParam param) {
throw new UnsupportedOperationException("Method convertImageMetadata not implemented");// TODO: Implement
}
@Override
public void write(IIOMetadata pStreamMetadata, IIOImage pImage, ImageWriteParam pParam) throws IOException {
assertOutput();
@@ -113,9 +105,13 @@ public final class IFFImageWriter extends ImageWriterBase {
// NOTE: This is much faster than imageOutput.write(pImageData.toByteArray())
// as the data array is not duplicated
try (OutputStream adapter = IIOUtil.createStreamAdapter(imageOutput)) {
OutputStream adapter = IIOUtil.createStreamAdapter(imageOutput);
try {
pImageData.writeTo(adapter);
}
finally {
adapter.close();
}
if (pImageData.size() % 2 == 0) {
imageOutput.writeByte(0); // PAD
@@ -184,7 +180,7 @@ public final class IFFImageWriter extends ImageWriterBase {
private void writeMeta(RenderedImage pImage, int pBodyLength) throws IOException {
// Annotation ANNO chunk, 8 + annoData.length bytes
String annotation = String.format("Written by %s IFFImageWriter %s", getOriginatingProvider().getVendorName(), getOriginatingProvider().getVersion());
String annotation = "Written by " + getOriginatingProvider().getDescription(null) + " by " + getOriginatingProvider().getVendorName();
GenericChunk anno = new GenericChunk(IFFUtil.toInt("ANNO".getBytes()), annotation.getBytes());
ColorModel cm = pImage.getColorModel();
@@ -30,13 +30,12 @@
package com.twelvemonkeys.imageio.plugins.iff;
import java.io.IOException;
import java.util.Locale;
import com.twelvemonkeys.imageio.spi.ImageWriterSpiBase;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriter;
import com.twelvemonkeys.imageio.spi.ImageWriterSpiBase;
import java.io.IOException;
import java.util.Locale;
/**
* IFFImageWriterSpi
@@ -59,12 +58,10 @@ public class IFFImageWriterSpi extends ImageWriterSpiBase {
return true;
}
@Override
public ImageWriter createWriterInstance(Object pExtension) throws IOException {
return new IFFImageWriter(this);
}
@Override
public String getDescription(Locale pLocale) {
return "Commodore Amiga/Electronic Arts Image Interchange Format (IFF) image writer";
}
@@ -47,7 +47,7 @@ package com.twelvemonkeys.imageio.plugins.iff;
* @author Harald Kuhr (Java port)
* @version $Id: IFFUtil.java,v 1.0 06.mar.2006 13:31:35 haku Exp$
*/
final class IFFUtil {
class IFFUtil {
/**
* Creates a rotation table
@@ -1,511 +0,0 @@
package com.twelvemonkeys.imageio.plugins.iff;
import static org.junit.Assert.*;
import java.awt.image.IndexColorModel;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.imageio.metadata.IIOMetadataFormatImpl;
import javax.imageio.metadata.IIOMetadataNode;
import org.junit.Test;
import org.junit.function.ThrowingRunnable;
import org.w3c.dom.Node;
public class IFFImageMetadataTest {
@Test
public void testStandardFeatures() {
BMHDChunk header = new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
final IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, null, Collections.<GenericChunk>emptyList());
// Standard metadata format
assertTrue(metadata.isStandardMetadataFormatSupported());
Node root = metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName);
assertNotNull(root);
assertTrue(root instanceof IIOMetadataNode);
// Other formats
assertNull(metadata.getNativeMetadataFormatName());
assertNull(metadata.getExtraMetadataFormatNames());
assertThrows(IllegalArgumentException.class, new ThrowingRunnable() {
@Override
public void run() {
metadata.getAsTree("com_foo_bar_1.0");
}
});
// Read-only
assertTrue(metadata.isReadOnly());
assertThrows(IllegalStateException.class, new ThrowingRunnable() {
@Override
public void run() throws Throwable {
metadata.mergeTree(IIOMetadataFormatImpl.standardMetadataFormatName, new IIOMetadataNode(IIOMetadataFormatImpl.standardMetadataFormatName));
}
});
}
@Test
public void testStandardChromaGray() {
BMHDChunk header = new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, null, Collections.<GenericChunk>emptyList());
IIOMetadataNode chroma = metadata.getStandardChromaNode();
assertNotNull(chroma);
assertEquals("Chroma", chroma.getNodeName());
assertEquals(3, chroma.getLength());
IIOMetadataNode colorSpaceType = (IIOMetadataNode) chroma.getFirstChild();
assertEquals("ColorSpaceType", colorSpaceType.getNodeName());
assertEquals("GRAY", colorSpaceType.getAttribute("name"));
IIOMetadataNode numChannels = (IIOMetadataNode) colorSpaceType.getNextSibling();
assertEquals("NumChannels", numChannels.getNodeName());
assertEquals("1", numChannels.getAttribute("value"));
IIOMetadataNode blackIsZero = (IIOMetadataNode) numChannels.getNextSibling();
assertEquals("BlackIsZero", blackIsZero.getNodeName());
assertEquals("TRUE", blackIsZero.getAttribute("value"));
assertNull(blackIsZero.getNextSibling()); // No more children
}
@Test
public void testStandardChromaRGB() {
BMHDChunk header = new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, null, Collections.<GenericChunk>emptyList());
IIOMetadataNode chroma = metadata.getStandardChromaNode();
assertNotNull(chroma);
assertEquals("Chroma", chroma.getNodeName());
assertEquals(3, chroma.getLength());
IIOMetadataNode colorSpaceType = (IIOMetadataNode) chroma.getFirstChild();
assertEquals("ColorSpaceType", colorSpaceType.getNodeName());
assertEquals("RGB", colorSpaceType.getAttribute("name"));
IIOMetadataNode numChannels = (IIOMetadataNode) colorSpaceType.getNextSibling();
assertEquals("NumChannels", numChannels.getNodeName());
assertEquals("3", numChannels.getAttribute("value"));
IIOMetadataNode blackIsZero = (IIOMetadataNode) numChannels.getNextSibling();
assertEquals("BlackIsZero", blackIsZero.getNodeName());
assertEquals("TRUE", blackIsZero.getAttribute("value"));
assertNull(blackIsZero.getNextSibling()); // No more children
}
@Test
public void testStandardChromaPalette() {
BMHDChunk header = new BMHDChunk(300, 200, 1, BMHDChunk.MASK_TRANSPARENT_COLOR, BMHDChunk.COMPRESSION_BYTE_RUN, 1);
byte[] bw = {0, (byte) 0xff};
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, new IndexColorModel(header.bitplanes, bw.length, bw, bw, bw, header.transparentIndex), null, Collections.<GenericChunk>emptyList());
IIOMetadataNode chroma = metadata.getStandardChromaNode();
assertNotNull(chroma);
assertEquals("Chroma", chroma.getNodeName());
assertEquals(4, chroma.getLength());
IIOMetadataNode colorSpaceType = (IIOMetadataNode) chroma.getFirstChild();
assertEquals("ColorSpaceType", colorSpaceType.getNodeName());
assertEquals("RGB", colorSpaceType.getAttribute("name"));
IIOMetadataNode numChannels = (IIOMetadataNode) colorSpaceType.getNextSibling();
assertEquals("NumChannels", numChannels.getNodeName());
assertEquals("3", numChannels.getAttribute("value"));
IIOMetadataNode blackIsZero = (IIOMetadataNode) numChannels.getNextSibling();
assertEquals("BlackIsZero", blackIsZero.getNodeName());
assertEquals("TRUE", blackIsZero.getAttribute("value"));
IIOMetadataNode palette = (IIOMetadataNode) blackIsZero.getNextSibling();
assertEquals("Palette", palette.getNodeName());
assertEquals(bw.length, palette.getLength());
for (int i = 0; i < palette.getLength(); i++) {
IIOMetadataNode item0 = (IIOMetadataNode) palette.item(i);
assertEquals("PaletteEntry", item0.getNodeName());
assertEquals(String.valueOf(i), item0.getAttribute("index"));
String rgb = String.valueOf(bw[i] & 0xff);
assertEquals(rgb, item0.getAttribute("red"));
assertEquals(rgb, item0.getAttribute("green"));
assertEquals(rgb, item0.getAttribute("blue"));
}
// TODO: BackgroundIndex == 1??
}
@Test
public void testStandardCompressionRLE() {
BMHDChunk header = new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, null, Collections.<GenericChunk>emptyList());
IIOMetadataNode compression = metadata.getStandardCompressionNode();
assertNotNull(compression);
assertEquals("Compression", compression.getNodeName());
assertEquals(2, compression.getLength());
IIOMetadataNode compressionTypeName = (IIOMetadataNode) compression.getFirstChild();
assertEquals("CompressionTypeName", compressionTypeName.getNodeName());
assertEquals("RLE", compressionTypeName.getAttribute("value"));
IIOMetadataNode lossless = (IIOMetadataNode) compressionTypeName.getNextSibling();
assertEquals("Lossless", lossless.getNodeName());
assertEquals("TRUE", lossless.getAttribute("value"));
assertNull(lossless.getNextSibling()); // No more children
}
@Test
public void testStandardCompressionNone() {
BMHDChunk header = new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_NONE, 0);
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, null, Collections.<GenericChunk>emptyList());
assertNull(metadata.getStandardCompressionNode()); // No compression, all default...
}
@Test
public void testStandardDataILBM_Gray() {
BMHDChunk header = new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, null, Collections.<GenericChunk>emptyList());
IIOMetadataNode data = metadata.getStandardDataNode();
assertNotNull(data);
assertEquals("Data", data.getNodeName());
assertEquals(3, data.getLength());
IIOMetadataNode planarConfiguration = (IIOMetadataNode) data.getFirstChild();
assertEquals("PlanarConfiguration", planarConfiguration.getNodeName());
assertEquals("PlaneInterleaved", planarConfiguration.getAttribute("value"));
IIOMetadataNode sampleFomat = (IIOMetadataNode) planarConfiguration.getNextSibling();
assertEquals("SampleFormat", sampleFomat.getNodeName());
assertEquals("UnsignedIntegral", sampleFomat.getAttribute("value"));
IIOMetadataNode bitsPerSample = (IIOMetadataNode) sampleFomat.getNextSibling();
assertEquals("BitsPerSample", bitsPerSample.getNodeName());
assertEquals("8", bitsPerSample.getAttribute("value"));
assertNull(bitsPerSample.getNextSibling()); // No more children
}
@Test
public void testStandardDataILBM_RGB() {
BMHDChunk header = new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, null, Collections.<GenericChunk>emptyList());
IIOMetadataNode data = metadata.getStandardDataNode();
assertNotNull(data);
assertEquals("Data", data.getNodeName());
assertEquals(3, data.getLength());
IIOMetadataNode planarConfiguration = (IIOMetadataNode) data.getFirstChild();
assertEquals("PlanarConfiguration", planarConfiguration.getNodeName());
assertEquals("PlaneInterleaved", planarConfiguration.getAttribute("value"));
IIOMetadataNode sampleFomat = (IIOMetadataNode) planarConfiguration.getNextSibling();
assertEquals("SampleFormat", sampleFomat.getNodeName());
assertEquals("UnsignedIntegral", sampleFomat.getAttribute("value"));
IIOMetadataNode bitsPerSample = (IIOMetadataNode) sampleFomat.getNextSibling();
assertEquals("BitsPerSample", bitsPerSample.getNodeName());
assertEquals("8 8 8", bitsPerSample.getAttribute("value"));
assertNull(bitsPerSample.getNextSibling()); // No more children
}
@Test
public void testStandardDataILBM_RGBA() {
BMHDChunk header = new BMHDChunk(300, 200, 32, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, null, Collections.<GenericChunk>emptyList());
IIOMetadataNode data = metadata.getStandardDataNode();
assertNotNull(data);
assertEquals("Data", data.getNodeName());
assertEquals(3, data.getLength());
IIOMetadataNode planarConfiguration = (IIOMetadataNode) data.getFirstChild();
assertEquals("PlanarConfiguration", planarConfiguration.getNodeName());
assertEquals("PlaneInterleaved", planarConfiguration.getAttribute("value"));
IIOMetadataNode sampleFomat = (IIOMetadataNode) planarConfiguration.getNextSibling();
assertEquals("SampleFormat", sampleFomat.getNodeName());
assertEquals("UnsignedIntegral", sampleFomat.getAttribute("value"));
IIOMetadataNode bitsPerSample = (IIOMetadataNode) sampleFomat.getNextSibling();
assertEquals("BitsPerSample", bitsPerSample.getNodeName());
assertEquals("8 8 8 8", bitsPerSample.getAttribute("value"));
assertNull(bitsPerSample.getNextSibling()); // No more children
}
@Test
public void testStandardDataILBM_Palette() {
for (int i = 1; i <= 8; i++) {
BMHDChunk header = new BMHDChunk(300, 200, i, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
byte[] rgb = new byte[2 << i]; // Colors doesn't really matter here
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, new IndexColorModel(header.bitplanes, rgb.length, rgb, rgb, rgb, 0), null, Collections.<GenericChunk>emptyList());
IIOMetadataNode data = metadata.getStandardDataNode();
assertNotNull(data);
assertEquals("Data", data.getNodeName());
assertEquals(3, data.getLength());
IIOMetadataNode planarConfiguration = (IIOMetadataNode) data.getFirstChild();
assertEquals("PlanarConfiguration", planarConfiguration.getNodeName());
assertEquals("PlaneInterleaved", planarConfiguration.getAttribute("value"));
IIOMetadataNode sampleFomat = (IIOMetadataNode) planarConfiguration.getNextSibling();
assertEquals("SampleFormat", sampleFomat.getNodeName());
assertEquals("Index", sampleFomat.getAttribute("value"));
IIOMetadataNode bitsPerSample = (IIOMetadataNode) sampleFomat.getNextSibling();
assertEquals("BitsPerSample", bitsPerSample.getNodeName());
assertEquals(String.valueOf(i), bitsPerSample.getAttribute("value"));
assertNull(bitsPerSample.getNextSibling()); // No more children
}
}
@Test
public void testStandardDataPBM_Gray() {
BMHDChunk header = new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_PBM, header, null, null, Collections.<GenericChunk>emptyList());
IIOMetadataNode data = metadata.getStandardDataNode();
assertNotNull(data);
assertEquals("Data", data.getNodeName());
assertEquals(3, data.getLength());
IIOMetadataNode planarConfiguration = (IIOMetadataNode) data.getFirstChild();
assertEquals("PlanarConfiguration", planarConfiguration.getNodeName());
assertEquals("PixelInterleaved", planarConfiguration.getAttribute("value"));
IIOMetadataNode sampleFomat = (IIOMetadataNode) planarConfiguration.getNextSibling();
assertEquals("SampleFormat", sampleFomat.getNodeName());
assertEquals("UnsignedIntegral", sampleFomat.getAttribute("value"));
IIOMetadataNode bitsPerSample = (IIOMetadataNode) sampleFomat.getNextSibling();
assertEquals("BitsPerSample", bitsPerSample.getNodeName());
assertEquals("8", bitsPerSample.getAttribute("value"));
assertNull(bitsPerSample.getNextSibling()); // No more children
}
@Test
public void testStandardDataPBM_RGB() {
BMHDChunk header = new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_PBM, header, null, null, Collections.<GenericChunk>emptyList());
IIOMetadataNode data = metadata.getStandardDataNode();
assertNotNull(data);
assertEquals("Data", data.getNodeName());
assertEquals(3, data.getLength());
IIOMetadataNode planarConfiguration = (IIOMetadataNode) data.getFirstChild();
assertEquals("PlanarConfiguration", planarConfiguration.getNodeName());
assertEquals("PixelInterleaved", planarConfiguration.getAttribute("value"));
IIOMetadataNode sampleFomat = (IIOMetadataNode) planarConfiguration.getNextSibling();
assertEquals("SampleFormat", sampleFomat.getNodeName());
assertEquals("UnsignedIntegral", sampleFomat.getAttribute("value"));
IIOMetadataNode bitsPerSample = (IIOMetadataNode) sampleFomat.getNextSibling();
assertEquals("BitsPerSample", bitsPerSample.getNodeName());
assertEquals("8 8 8", bitsPerSample.getAttribute("value"));
assertNull(bitsPerSample.getNextSibling()); // No more children
}
@Test
public void testStandardDimensionNoViewport() {
BMHDChunk header = new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, null, Collections.<GenericChunk>emptyList());
IIOMetadataNode dimension = metadata.getStandardDimensionNode();
assertNull(dimension);
}
@Test
public void testStandardDimensionNormal() {
BMHDChunk header = new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, new CAMGChunk(4), Collections.<GenericChunk>emptyList());
IIOMetadataNode dimension = metadata.getStandardDimensionNode();
assertNotNull(dimension);
assertEquals("Dimension", dimension.getNodeName());
assertEquals(1, dimension.getLength());
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) dimension.getFirstChild();
assertEquals("PixelAspectRatio", pixelAspectRatio.getNodeName());
assertEquals("1.0", pixelAspectRatio.getAttribute("value"));
assertNull(pixelAspectRatio.getNextSibling()); // No more children
}
@Test
public void testStandardDimensionHires() {
BMHDChunk header = new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
CAMGChunk viewPort = new CAMGChunk(4);
viewPort.camg = 0x8000;
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, viewPort, Collections.<GenericChunk>emptyList());
IIOMetadataNode dimension = metadata.getStandardDimensionNode();
assertNotNull(dimension);
assertEquals("Dimension", dimension.getNodeName());
assertEquals(1, dimension.getLength());
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) dimension.getFirstChild();
assertEquals("PixelAspectRatio", pixelAspectRatio.getNodeName());
assertEquals("2.0", pixelAspectRatio.getAttribute("value"));
assertNull(pixelAspectRatio.getNextSibling()); // No more children
}
@Test
public void testStandardDimensionInterlaced() {
BMHDChunk header = new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
CAMGChunk viewPort = new CAMGChunk(4);
viewPort.camg = 0x4;
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, viewPort, Collections.<GenericChunk>emptyList());
IIOMetadataNode dimension = metadata.getStandardDimensionNode();
assertNotNull(dimension);
assertEquals("Dimension", dimension.getNodeName());
assertEquals(1, dimension.getLength());
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) dimension.getFirstChild();
assertEquals("PixelAspectRatio", pixelAspectRatio.getNodeName());
assertEquals("0.5", pixelAspectRatio.getAttribute("value"));
assertNull(pixelAspectRatio.getNextSibling()); // No more children
}
@Test
public void testStandardDimensionHiresInterlaced() {
BMHDChunk header = new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
CAMGChunk viewPort = new CAMGChunk(4);
viewPort.camg = 0x8004;
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, viewPort, Collections.<GenericChunk>emptyList());
IIOMetadataNode dimension = metadata.getStandardDimensionNode();
assertNotNull(dimension);
assertEquals("Dimension", dimension.getNodeName());
assertEquals(1, dimension.getLength());
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) dimension.getFirstChild();
assertEquals("PixelAspectRatio", pixelAspectRatio.getNodeName());
assertEquals("1.0", pixelAspectRatio.getAttribute("value"));
assertNull(pixelAspectRatio.getNextSibling()); // No more children
}
@Test
public void testStandardDocument() {
BMHDChunk header = new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, null, Collections.<GenericChunk>emptyList());
IIOMetadataNode document = metadata.getStandardDocumentNode();
assertNotNull(document);
assertEquals("Document", document.getNodeName());
assertEquals(1, document.getLength());
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) document.getFirstChild();
assertEquals("FormatVersion", pixelAspectRatio.getNodeName());
assertEquals("1.0", pixelAspectRatio.getAttribute("value"));
assertNull(pixelAspectRatio.getNextSibling()); // No more children
}
@Test
public void testStandardText() {
BMHDChunk header = new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
String[] texts = {"annotation", "äñnótâtïøñ"};
List<GenericChunk> meta = Arrays.asList(new GenericChunk(IFF.CHUNK_ANNO, texts[0].getBytes(StandardCharsets.US_ASCII)),
new GenericChunk(IFF.CHUNK_UTF8, texts[1].getBytes(StandardCharsets.UTF_8)));
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, null, meta);
IIOMetadataNode text = metadata.getStandardTextNode();
assertNotNull(text);
assertEquals("Text", text.getNodeName());
assertEquals(texts.length, text.getLength());
for (int i = 0; i < texts.length; i++) {
IIOMetadataNode textEntry = (IIOMetadataNode) text.item(i);
assertEquals("TextEntry", textEntry.getNodeName());
assertEquals(IFFUtil.toChunkStr(meta.get(i).chunkId), textEntry.getAttribute("keyword"));
assertEquals(texts[i], textEntry.getAttribute("value"));
}
}
@Test
public void testStandardTransparencyRGB() {
BMHDChunk header = new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, null, Collections.<GenericChunk>emptyList());
IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
assertNull(transparency); // No transparency, just defaults
}
@Test
public void testStandardTransparencyRGBA() {
BMHDChunk header = new BMHDChunk(300, 200, 32, BMHDChunk.MASK_HAS_MASK, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, null, Collections.<GenericChunk>emptyList());
IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
assertNotNull(transparency);
assertEquals("Transparency", transparency.getNodeName());
assertEquals(1, transparency.getLength());
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) transparency.getFirstChild();
assertEquals("Alpha", pixelAspectRatio.getNodeName());
assertEquals("nonpremultiplied", pixelAspectRatio.getAttribute("value"));
assertNull(pixelAspectRatio.getNextSibling()); // No more children
}
@Test
public void testStandardTransparencyPalette() {
BMHDChunk header = new BMHDChunk(300, 200, 1, BMHDChunk.MASK_TRANSPARENT_COLOR, BMHDChunk.COMPRESSION_BYTE_RUN, 1);
byte[] bw = {0, (byte) 0xff};
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, new IndexColorModel(header.bitplanes, bw.length, bw, bw, bw, header.transparentIndex), null, Collections.<GenericChunk>emptyList());
IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
assertNotNull(transparency);
assertEquals("Transparency", transparency.getNodeName());
assertEquals(1, transparency.getLength());
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) transparency.getFirstChild();
assertEquals("TransparentIndex", pixelAspectRatio.getNodeName());
assertEquals("1", pixelAspectRatio.getAttribute("value"));
assertNull(pixelAspectRatio.getNextSibling()); // No more children
}
}
-44
View File
@@ -1,44 +0,0 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.7.0</version>
</parent>
<artifactId>imageio-jpeg-jai-interop</artifactId>
<name>TwelveMonkeys :: ImageIO :: JPEG/JAI TIFF Interop</name>
<description>
Test JPEG plugin and JAI TIFF plugin interoperability
</description>
<properties>
<project.jpms.module.name>com.twelvemonkeys.imageio.jaiinterop</project.jpms.module.name>
</properties>
<dependencies>
<dependency>
<groupId>com.github.jai-imageio</groupId>
<artifactId>jai-imageio-core</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-core</artifactId>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-core</artifactId>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-metadata</artifactId>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-jpeg</artifactId>
</dependency>
</dependencies>
</project>
@@ -1,121 +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.plugins.jpeg.jaiinterop;
import com.twelvemonkeys.imageio.plugins.jpeg.JPEGImageReaderSpi;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
import org.junit.Ignore;
import org.junit.Test;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.spi.IIORegistry;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.spi.ServiceRegistry;
import java.awt.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import static org.junit.Assert.fail;
/**
* Tests the JAI TIFFImageReader delegating to our JPEGImageReader.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: JAITIFFImageReaderInteroperabilityTest.java,v 1.0 08.05.12 15:25 haraldk Exp$
*/
public class JAITIFFImageReaderInteroperabilityTest extends ImageReaderAbstractTest<ImageReader> {
private static final String JAI_TIFF_PROVIDER_CLASS_NAME = "com.github.jaiimageio.impl.plugins.tiff.TIFFImageReaderSpi";
@Override
protected ImageReaderSpi createProvider() {
Iterator<ImageReaderSpi> providers = IIORegistry.getDefaultInstance().getServiceProviders(ImageReaderSpi.class, new ServiceRegistry.Filter() {
@Override
public boolean filter(final Object provider) {
return JAI_TIFF_PROVIDER_CLASS_NAME.equals(provider.getClass().getName());
}
}, true);
if (providers.hasNext()) {
return providers.next();
}
return null;
}
@Override
protected List<TestData> getTestData() {
return Arrays.asList(
new TestData(getClassLoaderResource("/tiff/foto_0001.tif"), new Dimension(1663, 2338)), // Little endian, Old JPEG
new TestData(getClassLoaderResource("/tiff/cmyk_jpeg.tif"), new Dimension(100, 100)), // CMYK, JPEG compressed, with ICC profile
new TestData(getClassLoaderResource("/tiff/jpeg-lossless-8bit-gray.tif"), new Dimension(512, 512)) // Lossless JPEG Gray, 8 bit/sample
);
}
@Override
protected List<String> getFormatNames() {
return Collections.emptyList();
}
@Override
protected List<String> getSuffixes() {
return Collections.emptyList();
}
@Override
protected List<String> getMIMETypes() {
return Collections.emptyList();
}
@Ignore("Fails in TIFFImageReader")
@Override
public void testSetDestinationIllegal() {
}
@Test
public void testReallyUsingOurJPEGImageReader() {
Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName("JPEG");
if (readers.hasNext()) {
ImageReader reader = readers.next();
if ((reader.getOriginatingProvider() instanceof JPEGImageReaderSpi)) {
return;
}
}
fail("Expected Spi not registered (dependency issue?): " + JPEGImageReaderSpi.class);
}
}
@@ -1,39 +0,0 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.7.0</version>
</parent>
<artifactId>imageio-jpeg-jep262-interop</artifactId>
<name>TwelveMonkeys :: ImageIO :: JPEG/JEP-262 Interop</name>
<description>
Test JPEG plugin and JEP-262 (JDK TIFF plugin) interoperability
</description>
<properties>
<project.jpms.module.name>com.twelvemonkeys.imageio.jep262interop</project.jpms.module.name>
</properties>
<dependencies>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-core</artifactId>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-core</artifactId>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-metadata</artifactId>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-jpeg</artifactId>
</dependency>
</dependencies>
</project>
@@ -1,125 +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.plugins.jpeg.jep262interop;
import com.twelvemonkeys.imageio.plugins.jpeg.JPEGImageReaderSpi;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
import org.junit.Ignore;
import org.junit.Test;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.spi.IIORegistry;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.spi.ServiceRegistry;
import java.awt.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
/**
* Tests the JEP 262 TIFFImageReader delegating to our JPEGImageReader.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: TIFFImageReaderTest.java,v 1.0 08.05.12 15:25 haraldk Exp$
*/
public class JEP262TIFFImageReaderInteroperabilityTest extends ImageReaderAbstractTest<ImageReader> {
private static final String JEP_262_PROVIDER_CLASS_NAME = "com.sun.imageio.plugins.tiff.TIFFImageReaderSpi";
@Override
protected ImageReaderSpi createProvider() {
Iterator<ImageReaderSpi> providers = IIORegistry.getDefaultInstance().getServiceProviders(ImageReaderSpi.class, new ServiceRegistry.Filter() {
@Override
public boolean filter(final Object provider) {
return JEP_262_PROVIDER_CLASS_NAME.equals(provider.getClass().getName()) && ((ImageReaderSpi) provider).getVendorName().startsWith("Oracle");
}
}, true);
if (providers.hasNext()) {
return providers.next();
}
// Skip tests if we have no Spi (ie. pre JDK 9)
assumeTrue("Provider " + JEP_262_PROVIDER_CLASS_NAME + " not found", false);
return null;
}
@Override
protected List<TestData> getTestData() {
return Arrays.asList(
new TestData(getClassLoaderResource("/tiff/foto_0001.tif"), new Dimension(1663, 2338)), // Little endian, Old JPEG
new TestData(getClassLoaderResource("/tiff/cmyk_jpeg.tif"), new Dimension(100, 100)), // CMYK, JPEG compressed, with ICC profile
new TestData(getClassLoaderResource("/tiff/jpeg-lossless-8bit-gray.tif"), new Dimension(512, 512)) // Lossless JPEG Gray, 8 bit/sample
);
}
@Override
protected List<String> getFormatNames() {
return Collections.emptyList();
}
@Override
protected List<String> getSuffixes() {
return Collections.emptyList();
}
@Override
protected List<String> getMIMETypes() {
return Collections.emptyList();
}
@Ignore("Fails in TIFFImageReader")
@Override
public void testSetDestinationIllegal() {
}
@Test
public void testReallyUsingOurJPEGImageReader() {
Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName("JPEG");
if (readers.hasNext()) {
ImageReader reader = readers.next();
if ((reader.getOriginatingProvider() instanceof JPEGImageReaderSpi)) {
return;
}
}
fail("Expected Spi not registered (dependency issue?): " + JPEGImageReaderSpi.class);
}
}
+1 -5
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.7.0</version>
<version>3.6.5-SNAPSHOT</version>
</parent>
<artifactId>imageio-jpeg</artifactId>
<name>TwelveMonkeys :: ImageIO :: JPEG plugin</name>
@@ -12,10 +12,6 @@
ImageIO plugin for Joint Photographer Expert Group images (JPEG/JFIF).
</description>
<properties>
<project.jpms.module.name>com.twelvemonkeys.imageio.jpeg</project.jpms.module.name>
</properties>
<dependencies>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
@@ -38,7 +38,7 @@ import java.io.IOException;
import java.io.InputStream;
/**
* An application (APPn) segment in the JPEG stream.
* Application.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: harald.kuhr$
@@ -78,9 +78,7 @@ class Application extends Segment {
if ("JFXX".equals(identifier)) {
return JFXX.read(data, length);
}
if ("Exif".equals(identifier)) {
return EXIF.read(data, length);
}
// TODO: Exif?
case JPEG.APP2:
// ICC_PROFILE
if ("ICC_PROFILE".equals(identifier)) {
@@ -1,158 +0,0 @@
/*
* Copyright (c) 2012, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.plugins.jpeg;
import com.twelvemonkeys.imageio.color.YCbCrConverter;
import com.twelvemonkeys.imageio.metadata.CompoundDirectory;
import com.twelvemonkeys.imageio.metadata.Directory;
import com.twelvemonkeys.imageio.metadata.Entry;
import com.twelvemonkeys.imageio.metadata.jpeg.JPEG;
import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
import com.twelvemonkeys.imageio.plugins.jpeg.ThumbnailReader.JPEGThumbnailReader;
import com.twelvemonkeys.imageio.plugins.jpeg.ThumbnailReader.UncompressedThumbnailReader;
import javax.imageio.IIOException;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
import java.nio.ByteOrder;
import java.util.Arrays;
/**
* EXIFThumbnail
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: EXIFThumbnail.java,v 1.0 18.04.12 12:19 haraldk Exp$
*/
final class EXIFThumbnail {
private EXIFThumbnail() {
}
static ThumbnailReader from(final EXIF segment, final CompoundDirectory exif, final ImageReader jpegThumbnailReader) throws IOException {
if (segment != null && exif != null && exif.directoryCount() >= 2) {
ImageInputStream stream = segment.exifData(); // NOTE This is an in-memory stream and must not be closed...
Directory ifd1 = exif.getDirectory(1);
// Compression: 1 = no compression, 6 = JPEG compression (default)
Entry compressionEntry = ifd1.getEntryById(TIFF.TAG_COMPRESSION);
int compression = compressionEntry == null ? 6 : ((Number) compressionEntry.getValue()).intValue();
switch (compression) {
case 1:
return createUncompressedThumbnailReader(stream, ifd1);
case 6:
return createJPEGThumbnailReader(segment, jpegThumbnailReader, stream, ifd1);
default:
throw new IIOException("EXIF IFD with unknown thumbnail compression (expected 1 or 6): " + compression);
}
}
return null;
}
private static UncompressedThumbnailReader createUncompressedThumbnailReader(ImageInputStream stream, Directory ifd1) throws IOException {
Entry stripOffEntry = ifd1.getEntryById(TIFF.TAG_STRIP_OFFSETS);
Entry width = ifd1.getEntryById(TIFF.TAG_IMAGE_WIDTH);
Entry height = ifd1.getEntryById(TIFF.TAG_IMAGE_HEIGHT);
if (stripOffEntry != null && width != null && height != null) {
Entry bitsPerSample = ifd1.getEntryById(TIFF.TAG_BITS_PER_SAMPLE);
Entry samplesPerPixel = ifd1.getEntryById(TIFF.TAG_SAMPLES_PER_PIXEL);
Entry photometricInterpretation = ifd1.getEntryById(TIFF.TAG_PHOTOMETRIC_INTERPRETATION);
// Required
int w = ((Number) width.getValue()).intValue();
int h = ((Number) height.getValue()).intValue();
if (bitsPerSample != null && !Arrays.equals((int[]) bitsPerSample.getValue(), new int[] {8, 8, 8})) {
throw new IIOException("Unknown BitsPerSample value for uncompressed EXIF thumbnail (expected [8, 8, 8]): " + bitsPerSample.getValueAsString());
}
if (samplesPerPixel != null && ((Number) samplesPerPixel.getValue()).intValue() != 3) {
throw new IIOException("Unknown SamplesPerPixel value for uncompressed EXIF thumbnail (expected 3): " + samplesPerPixel.getValueAsString());
}
int interpretation = photometricInterpretation != null ? ((Number) photometricInterpretation.getValue()).intValue() : 2;
long stripOffset = ((Number) stripOffEntry.getValue()).longValue();
int thumbLength = w * h * 3;
if (stripOffset >= 0 && stripOffset + thumbLength <= stream.length()) {
// Read raw image data, either RGB or YCbCr
stream.seek(stripOffset);
byte[] thumbData = new byte[thumbLength];
stream.readFully(thumbData);
switch (interpretation) {
case 2:
// RGB
break;
case 6:
// YCbCr
for (int i = 0; i < thumbLength; i += 3) {
YCbCrConverter.convertYCbCr2RGB(thumbData, thumbData, i);
}
break;
default:
throw new IIOException("Unknown PhotometricInterpretation value for uncompressed EXIF thumbnail (expected 2 or 6): " + interpretation);
}
return new UncompressedThumbnailReader(w, h, thumbData);
}
}
throw new IIOException("EXIF IFD with empty or incomplete uncompressed thumbnail");
}
private static JPEGThumbnailReader createJPEGThumbnailReader(EXIF exif, ImageReader jpegThumbnailReader, ImageInputStream stream, Directory ifd1) throws IOException {
Entry jpegOffEntry = ifd1.getEntryById(TIFF.TAG_JPEG_INTERCHANGE_FORMAT);
if (jpegOffEntry != null) {
Entry jpegLenEntry = ifd1.getEntryById(TIFF.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
// Test if Exif thumbnail is contained within the Exif segment (offset + length <= segment.length)
long jpegOffset = ((Number) jpegOffEntry.getValue()).longValue();
long jpegLength = jpegLenEntry != null ? ((Number) jpegLenEntry.getValue()).longValue() : -1;
if (jpegLength > 0 && jpegOffset + jpegLength <= exif.data.length) {
// Verify first bytes are FFD8
stream.seek(jpegOffset);
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
if (stream.readUnsignedShort() == JPEG.SOI) {
return new JPEGThumbnailReader(jpegThumbnailReader, stream, jpegOffset);
}
}
}
throw new IIOException("EXIF IFD with empty or incomplete JPEG thumbnail");
}
}
@@ -0,0 +1,248 @@
/*
* Copyright (c) 2012, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.plugins.jpeg;
import com.twelvemonkeys.imageio.color.YCbCrConverter;
import com.twelvemonkeys.imageio.metadata.Directory;
import com.twelvemonkeys.imageio.metadata.Entry;
import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
import com.twelvemonkeys.imageio.util.IIOUtil;
import com.twelvemonkeys.lang.Validate;
import javax.imageio.IIOException;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.MemoryCacheImageInputStream;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.SequenceInputStream;
import java.lang.ref.SoftReference;
import java.util.Arrays;
/**
* EXIFThumbnail
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: EXIFThumbnail.java,v 1.0 18.04.12 12:19 haraldk Exp$
*/
final class EXIFThumbnailReader extends ThumbnailReader {
private final ImageReader reader;
private final Directory ifd;
private final ImageInputStream stream;
private final int compression;
private transient SoftReference<BufferedImage> cachedThumbnail;
EXIFThumbnailReader(final ThumbnailReadProgressListener progressListener, final ImageReader jpegReader, final int imageIndex, final int thumbnailIndex, final Directory ifd, final ImageInputStream stream) {
super(progressListener, imageIndex, thumbnailIndex);
this.reader = Validate.notNull(jpegReader);
this.ifd = ifd;
this.stream = stream;
Entry compression = ifd.getEntryById(TIFF.TAG_COMPRESSION);
this.compression = compression != null ? ((Number) compression.getValue()).intValue() : 6;
}
@Override
public BufferedImage read() throws IOException {
if (compression == 1) { // 1 = no compression
processThumbnailStarted();
BufferedImage thumbnail = readUncompressed();
processThumbnailProgress(100f);
processThumbnailComplete();
return thumbnail;
}
else if (compression == 6) { // 6 = JPEG compression
processThumbnailStarted();
BufferedImage thumbnail = readJPEGCached(true);
processThumbnailProgress(100f);
processThumbnailComplete();
return thumbnail;
}
else {
throw new IIOException("Unsupported EXIF thumbnail compression: " + compression);
}
}
private BufferedImage readJPEGCached(final boolean pixelsExposed) throws IOException {
BufferedImage thumbnail = cachedThumbnail != null ? cachedThumbnail.get() : null;
if (thumbnail == null) {
thumbnail = readJPEG();
}
cachedThumbnail = pixelsExposed ? null : new SoftReference<>(thumbnail);
return thumbnail;
}
private BufferedImage readJPEG() throws IOException {
// IFD1 should contain JPEG offset for JPEG thumbnail
Entry jpegOffset = ifd.getEntryById(TIFF.TAG_JPEG_INTERCHANGE_FORMAT);
if (jpegOffset != null) {
stream.seek(((Number) jpegOffset.getValue()).longValue());
InputStream input = IIOUtil.createStreamAdapter(stream);
// For certain EXIF files (encoded with TIFF.TAG_YCBCR_POSITIONING = 2?), we need
// EXIF information to read the thumbnail correctly (otherwise the colors are messed up).
// Probably related to: http://bugs.sun.com/view_bug.do?bug_id=4881314
// HACK: Splice empty EXIF information into the thumbnail stream
byte[] fakeEmptyExif = {
// SOI (from original data)
(byte) input.read(), (byte) input.read(),
// APP1 + len (016) + 'Exif' + 0-term + pad
(byte) 0xFF, (byte) 0xE1, 0, 16, 'E', 'x', 'i', 'f', 0, 0,
// Big-endian BOM (MM), TIFF magic (042), offset (0000)
'M', 'M', 0, 42, 0, 0, 0, 0,
};
input = new SequenceInputStream(new ByteArrayInputStream(fakeEmptyExif), input);
try {
try (MemoryCacheImageInputStream stream = new MemoryCacheImageInputStream(input)) {
return readJPEGThumbnail(reader, stream);
}
}
finally {
input.close();
}
}
throw new IIOException("Missing JPEGInterchangeFormat tag for JPEG compressed EXIF thumbnail");
}
private BufferedImage readUncompressed() throws IOException {
// Read ImageWidth, ImageLength (height) and BitsPerSample (=8 8 8, always)
// PhotometricInterpretation (2=RGB, 6=YCbCr), SamplesPerPixel (=3, always),
Entry width = ifd.getEntryById(TIFF.TAG_IMAGE_WIDTH);
Entry height = ifd.getEntryById(TIFF.TAG_IMAGE_HEIGHT);
if (width == null || height == null) {
throw new IIOException("Missing dimensions for uncompressed EXIF thumbnail");
}
Entry bitsPerSample = ifd.getEntryById(TIFF.TAG_BITS_PER_SAMPLE);
Entry samplesPerPixel = ifd.getEntryById(TIFF.TAG_SAMPLES_PER_PIXEL);
Entry photometricInterpretation = ifd.getEntryById(TIFF.TAG_PHOTOMETRIC_INTERPRETATION);
// Required
int w = ((Number) width.getValue()).intValue();
int h = ((Number) height.getValue()).intValue();
if (bitsPerSample != null) {
int[] bpp = (int[]) bitsPerSample.getValue();
if (!Arrays.equals(bpp, new int[] {8, 8, 8})) {
throw new IIOException("Unknown BitsPerSample value for uncompressed EXIF thumbnail (expected [8, 8, 8]): " + bitsPerSample.getValueAsString());
}
}
if (samplesPerPixel != null && (Integer) samplesPerPixel.getValue() != 3) {
throw new IIOException("Unknown SamplesPerPixel value for uncompressed EXIF thumbnail (expected 3): " + samplesPerPixel.getValueAsString());
}
int interpretation = photometricInterpretation != null ? ((Number) photometricInterpretation.getValue()).intValue() : 2;
// IFD1 should contain strip offsets for uncompressed images
Entry offset = ifd.getEntryById(TIFF.TAG_STRIP_OFFSETS);
if (offset != null) {
stream.seek(((Number) offset.getValue()).longValue());
// Read raw image data, either RGB or YCbCr
int thumbSize = w * h * 3;
byte[] thumbData = JPEGImageReader.readFully(stream, thumbSize);
switch (interpretation) {
case 2:
// RGB
break;
case 6:
// YCbCr
for (int i = 0; i < thumbSize; i += 3) {
YCbCrConverter.convertYCbCr2RGB(thumbData, thumbData, i);
}
break;
default:
throw new IIOException("Unknown PhotometricInterpretation value for uncompressed EXIF thumbnail (expected 2 or 6): " + interpretation);
}
return ThumbnailReader.readRawThumbnail(thumbData, thumbSize, 0, w, h);
}
throw new IIOException("Missing StripOffsets tag for uncompressed EXIF thumbnail");
}
@Override
public int getWidth() throws IOException {
if (compression == 1) { // 1 = no compression
Entry width = ifd.getEntryById(TIFF.TAG_IMAGE_WIDTH);
if (width == null) {
throw new IIOException("Missing dimensions for uncompressed EXIF thumbnail");
}
return ((Number) width.getValue()).intValue();
}
else if (compression == 6) { // 6 = JPEG compression
return readJPEGCached(false).getWidth();
}
else {
throw new IIOException("Unsupported EXIF thumbnail compression (expected 1 or 6): " + compression);
}
}
@Override
public int getHeight() throws IOException {
if (compression == 1) { // 1 = no compression
Entry height = ifd.getEntryById(TIFF.TAG_IMAGE_HEIGHT);
if (height == null) {
throw new IIOException("Missing dimensions for uncompressed EXIF thumbnail");
}
return ((Number) height.getValue()).intValue();
}
else if (compression == 6) { // 6 = JPEG compression
return readJPEGCached(false).getHeight();
}
else {
throw new IIOException("Unsupported EXIF thumbnail compression (expected 1 or 6): " + compression);
}
}
}
@@ -62,7 +62,6 @@ class FastCMYKToRGB implements /*BufferedImageOp,*/ RasterOp {
* @return {@code dest}, or a new {@link WritableRaster} if {@code dest} is {@code null}.
* @throws IllegalArgumentException if {@code src} and {@code dest} refer to the same object
*/
@Override
public WritableRaster filter(Raster src, WritableRaster dest) {
Validate.notNull(src, "src may not be null");
// TODO: Why not allow same raster, if converting to 4 byte ABGR?
@@ -143,12 +142,10 @@ class FastCMYKToRGB implements /*BufferedImageOp,*/ RasterOp {
rgb[2] = (byte) (255 - (((cmyk[2] & 0xFF) * (255 - k) / 255) + k));
}
@Override
public Rectangle2D getBounds2D(Raster src) {
return src.getBounds();
}
@Override
public WritableRaster createCompatibleDestRaster(final Raster src) {
// WHAT?? This code no longer work for JRE 7u45+... JRE bug?!
// Raster child = src.createChild(0, 0, src.getWidth(), src.getHeight(), 0, 0, new int[] {0, 1, 2});
@@ -156,11 +153,10 @@ class FastCMYKToRGB implements /*BufferedImageOp,*/ RasterOp {
// This is a workaround for the above code that no longer works.
// It wil use 25% more memory, but it seems to work...
return src.createCompatibleWritableRaster()
.createWritableChild(0, 0, src.getWidth(), src.getHeight(), 0, 0, new int[] {0, 1, 2});
WritableRaster raster = src.createCompatibleWritableRaster();
return raster.createWritableChild(0, 0, src.getWidth(), src.getHeight(), 0, 0, new int[] {0, 1, 2});
}
@Override
public Point2D getPoint2D(Point2D srcPt, Point2D dstPt) {
if (dstPt == null) {
dstPt = new Point2D.Double(srcPt.getX(), srcPt.getY());
@@ -172,7 +168,6 @@ class FastCMYKToRGB implements /*BufferedImageOp,*/ RasterOp {
return dstPt;
}
@Override
public RenderingHints getRenderingHints() {
return null;
}
@@ -38,7 +38,7 @@ import java.io.IOException;
import java.nio.ByteBuffer;
/**
* A JFIF segment.
* JFIFSegment
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
@@ -54,8 +54,8 @@ final class JFIF extends Application {
final int yThumbnail;
final byte[] thumbnail;
JFIF(int majorVersion, int minorVersion, int units, int xDensity, int yDensity, int xThumbnail, int yThumbnail, byte[] thumbnail) {
super(JPEG.APP0, "JFIF", new byte[5 + 9 + (thumbnail != null ? thumbnail.length : 0)]);
private JFIF(int majorVersion, int minorVersion, int units, int xDensity, int yDensity, int xThumbnail, int yThumbnail, byte[] thumbnail, byte[] data) {
super(JPEG.APP0, "JFIF", data);
this.majorVersion = majorVersion;
this.minorVersion = minorVersion;
@@ -98,7 +98,7 @@ final class JFIF extends Application {
throw new EOFException();
}
data.readFully(new byte[5]); // Skip "JFIF\0"
data.readFully(new byte[5]);
byte[] bytes = new byte[length - 2 - 5];
data.readFully(bytes);
@@ -115,7 +115,8 @@ final class JFIF extends Application {
buffer.getShort() & 0xffff,
x = buffer.get() & 0xff,
y = buffer.get() & 0xff,
getBytes(buffer, Math.min(buffer.remaining(), x * y * 3))
getBytes(buffer, Math.min(buffer.remaining(), x * y * 3)),
bytes
);
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, Harald Kuhr
* Copyright (c) 2012, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -30,45 +30,41 @@
package com.twelvemonkeys.imageio.plugins.jpeg;
import com.twelvemonkeys.imageio.metadata.jpeg.JPEG;
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
import javax.imageio.stream.ImageInputStream;
import java.io.DataInput;
import java.io.EOFException;
import java.awt.image.BufferedImage;
import java.io.IOException;
/**
* An EXIF segment.
* JFIFThumbnailReader
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: JFIFSegment.java,v 1.0 23.04.12 16:52 haraldk Exp$
* @version $Id: JFIFThumbnailReader.java,v 1.0 18.04.12 12:19 haraldk Exp$
*/
final class EXIF extends Application {
EXIF(byte[] data) {
super(JPEG.APP1, "Exif", data);
final class JFIFThumbnailReader extends ThumbnailReader {
private final JFIF segment;
JFIFThumbnailReader(final ThumbnailReadProgressListener progressListener, final int imageIndex, final int thumbnailIndex, final JFIF segment) {
super(progressListener, imageIndex, thumbnailIndex);
this.segment = segment;
}
@Override
public String toString() {
return String.format("APP1/Exif, length: %d", data.length);
public BufferedImage read() {
processThumbnailStarted();
BufferedImage thumbnail = readRawThumbnail(segment.thumbnail, segment.thumbnail.length, 0, segment.xThumbnail, segment.yThumbnail);
processThumbnailProgress(100f);
processThumbnailComplete();
return thumbnail;
}
ImageInputStream exifData() {
// Identifier is "Exif\0" + 1 byte pad
int offset = identifier.length() + 2;
return new ByteArrayImageInputStream(data, offset, data.length - offset);
@Override
public int getWidth() throws IOException {
return segment.xThumbnail;
}
public static EXIF read(final DataInput data, int length) throws IOException {
if (length < 2 + 6) {
throw new EOFException();
}
byte[] bytes = new byte[length - 2];
data.readFully(bytes);
return new EXIF(bytes);
@Override
public int getHeight() throws IOException {
return segment.yThumbnail;
}
}
@@ -35,7 +35,7 @@ import java.io.IOException;
import java.util.Arrays;
/**
* A JFXX segment (aka JFIF extension segment).
* JFXXSegment
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
@@ -49,8 +49,8 @@ final class JFXX extends Application {
final int extensionCode;
final byte[] thumbnail;
JFXX(final int extensionCode, final byte[] thumbnail) {
super(com.twelvemonkeys.imageio.metadata.jpeg.JPEG.APP0, "JFXX", new byte[1 + (thumbnail != null ? thumbnail.length : 0)]);
private JFXX(final int extensionCode, final byte[] thumbnail, final byte[] data) {
super(com.twelvemonkeys.imageio.metadata.jpeg.JPEG.APP0, "JFXX", data);
this.extensionCode = extensionCode;
this.thumbnail = thumbnail;
@@ -82,7 +82,8 @@ final class JFXX extends Application {
return new JFXX(
bytes[0] & 0xff,
bytes.length - 1 > 0 ? Arrays.copyOfRange(bytes, 1, bytes.length - 1) : null
bytes.length - 1 > 0 ? Arrays.copyOfRange(bytes, 1, bytes.length - 1) : null,
bytes
);
}
}
@@ -1,96 +0,0 @@
/*
* Copyright (c) 2012, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.plugins.jpeg;
import com.twelvemonkeys.imageio.metadata.jpeg.JPEG;
import com.twelvemonkeys.imageio.plugins.jpeg.ThumbnailReader.IndexedThumbnailReader;
import com.twelvemonkeys.imageio.plugins.jpeg.ThumbnailReader.JPEGThumbnailReader;
import com.twelvemonkeys.imageio.plugins.jpeg.ThumbnailReader.UncompressedThumbnailReader;
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
import javax.imageio.IIOException;
import javax.imageio.ImageReader;
import java.io.IOException;
/**
* JFXXThumbnailReader
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: JFXXThumbnailReader.java,v 1.0 18.04.12 12:19 haraldk Exp$
*/
final class JFXXThumbnail {
private JFXXThumbnail() {
}
static ThumbnailReader from(final JFXX segment, final ImageReader thumbnailReader) throws IOException {
if (segment != null) {
if (segment.thumbnail != null && segment.thumbnail.length > 2) {
switch (segment.extensionCode) {
case JFXX.JPEG:
if (((segment.thumbnail[0] & 0xff) << 8 | segment.thumbnail[1] & 0xff) == JPEG.SOI) {
return new JPEGThumbnailReader(thumbnailReader, new ByteArrayImageInputStream(segment.thumbnail), 0);
}
break;
case JFXX.INDEXED:
int w = segment.thumbnail[0] & 0xff;
int h = segment.thumbnail[1] & 0xff;
if (segment.thumbnail.length >= 2 + 768 + w * h) {
return new IndexedThumbnailReader(w, h, segment.thumbnail, 2, segment.thumbnail, 2 + 768);
}
break;
case JFXX.RGB:
w = segment.thumbnail[0] & 0xff;
h = segment.thumbnail[1] & 0xff;
if (segment.thumbnail.length >= 2 + w * h * 3) {
return new UncompressedThumbnailReader(w, h, segment.thumbnail, 2);
}
break;
default:
throw new IIOException(String.format("Unknown JFXX extension code: %d, ignoring thumbnail", segment.extensionCode));
}
}
throw new IIOException("JFXX segment truncated, ignoring thumbnail");
}
return null;
}
}
@@ -0,0 +1,178 @@
/*
* Copyright (c) 2012, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.plugins.jpeg;
import com.twelvemonkeys.image.InverseColorMapIndexColorModel;
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
import com.twelvemonkeys.lang.Validate;
import javax.imageio.IIOException;
import javax.imageio.ImageReader;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.stream.ImageInputStream;
import java.awt.image.*;
import java.io.IOException;
import java.lang.ref.SoftReference;
/**
* JFXXThumbnailReader
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: JFXXThumbnailReader.java,v 1.0 18.04.12 12:19 haraldk Exp$
*/
final class JFXXThumbnailReader extends ThumbnailReader {
private final ImageReader reader;
private final JFXX segment;
private transient SoftReference<BufferedImage> cachedThumbnail;
JFXXThumbnailReader(final ThumbnailReadProgressListener progressListener, final ImageReader jpegReader, final int imageIndex, final int thumbnailIndex, final JFXX segment) {
super(progressListener, imageIndex, thumbnailIndex);
this.reader = Validate.notNull(jpegReader);
this.segment = segment;
}
@Override
public BufferedImage read() throws IOException {
processThumbnailStarted();
BufferedImage thumbnail;
switch (segment.extensionCode) {
case JFXX.JPEG:
thumbnail = readJPEGCached(true);
break;
case JFXX.INDEXED:
thumbnail = readIndexed();
break;
case JFXX.RGB:
thumbnail = readRGB();
break;
default:
throw new IIOException(String.format("Unsupported JFXX extension code: %d", segment.extensionCode));
}
processThumbnailProgress(100f);
processThumbnailComplete();
return thumbnail;
}
IIOMetadata readMetadata() throws IOException {
ImageInputStream input = new ByteArrayImageInputStream(segment.thumbnail);
try {
reader.setInput(input);
return reader.getImageMetadata(0);
}
finally {
input.close();
}
}
private BufferedImage readJPEGCached(boolean pixelsExposed) throws IOException {
BufferedImage thumbnail = cachedThumbnail != null ? cachedThumbnail.get() : null;
if (thumbnail == null) {
ImageInputStream stream = new ByteArrayImageInputStream(segment.thumbnail);
try {
thumbnail = readJPEGThumbnail(reader, stream);
}
finally {
stream.close();
}
}
cachedThumbnail = pixelsExposed ? null : new SoftReference<>(thumbnail);
return thumbnail;
}
@Override
public int getWidth() throws IOException {
switch (segment.extensionCode) {
case JFXX.RGB:
case JFXX.INDEXED:
return segment.thumbnail[0] & 0xff;
case JFXX.JPEG:
return readJPEGCached(false).getWidth();
default:
throw new IIOException(String.format("Unsupported JFXX extension code: %d", segment.extensionCode));
}
}
@Override
public int getHeight() throws IOException {
switch (segment.extensionCode) {
case JFXX.RGB:
case JFXX.INDEXED:
return segment.thumbnail[1] & 0xff;
case JFXX.JPEG:
return readJPEGCached(false).getHeight();
default:
throw new IIOException(String.format("Unsupported JFXX extension code: %d", segment.extensionCode));
}
}
private BufferedImage readIndexed() {
// 1 byte: xThumb
// 1 byte: yThumb
// 768 bytes: palette
// x * y bytes: 8 bit indexed pixels
int w = segment.thumbnail[0] & 0xff;
int h = segment.thumbnail[1] & 0xff;
int[] rgbs = new int[256];
for (int i = 0; i < rgbs.length; i++) {
rgbs[i] = (segment.thumbnail[3 * i + 2] & 0xff) << 16
| (segment.thumbnail[3 * i + 3] & 0xff) << 8
| (segment.thumbnail[3 * i + 4] & 0xff);
}
IndexColorModel icm = new InverseColorMapIndexColorModel(8, rgbs.length, rgbs, 0, false, -1, DataBuffer.TYPE_BYTE);
DataBufferByte buffer = new DataBufferByte(segment.thumbnail, segment.thumbnail.length - 770, 770);
WritableRaster raster = Raster.createPackedRaster(buffer, w, h, 8, null);
return new BufferedImage(icm, raster, icm.isAlphaPremultiplied(), null);
}
private BufferedImage readRGB() {
// 1 byte: xThumb
// 1 byte: yThumb
// 3 * x * y bytes: 24 bit RGB pixels
int w = segment.thumbnail[0] & 0xff;
int h = segment.thumbnail[1] & 0xff;
return ThumbnailReader.readRawThumbnail(segment.thumbnail, segment.thumbnail.length - 2, 2, w, h);
}
}
@@ -32,7 +32,6 @@ package com.twelvemonkeys.imageio.plugins.jpeg;
import com.twelvemonkeys.imageio.metadata.jpeg.JPEG;
import com.twelvemonkeys.xml.XMLSerializer;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
@@ -133,7 +132,7 @@ final class JPEGImage10MetadataCleaner {
IIOMetadataNode app0JFXX = new IIOMetadataNode("app0JFXX");
app0JFXX.setAttribute("extensionCode", String.valueOf(jfxx.extensionCode));
ThumbnailReader thumbnailReader = JFXXThumbnail.from(jfxx, reader.getThumbnailReader());
JFXXThumbnailReader thumbnailReader = new JFXXThumbnailReader(null, reader.getThumbnailReader(), 0, 0, jfxx);
IIOMetadataNode jfifThumb;
switch (jfxx.extensionCode) {
@@ -34,10 +34,14 @@ import com.twelvemonkeys.imageio.ImageReaderBase;
import com.twelvemonkeys.imageio.color.ColorSpaces;
import com.twelvemonkeys.imageio.color.YCbCrConverter;
import com.twelvemonkeys.imageio.metadata.CompoundDirectory;
import com.twelvemonkeys.imageio.metadata.Directory;
import com.twelvemonkeys.imageio.metadata.Entry;
import com.twelvemonkeys.imageio.metadata.jpeg.JPEG;
import com.twelvemonkeys.imageio.metadata.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.stream.ByteArrayImageInputStream;
import com.twelvemonkeys.imageio.stream.SubImageInputStream;
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
import com.twelvemonkeys.imageio.util.ProgressListenerBase;
@@ -58,6 +62,7 @@ import java.awt.color.ICC_ColorSpace;
import java.awt.color.ICC_Profile;
import java.awt.image.*;
import java.io.*;
import java.nio.ByteOrder;
import java.util.List;
import java.util.*;
@@ -194,38 +199,73 @@ public final class JPEGImageReader extends ImageReaderBase {
@Override
public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IOException {
ImageTypeSpecifier rawImageType = getRawImageType(imageIndex);
ColorModel rawColorModel = rawImageType.getColorModel();
JPEGColorSpace sourceCSType = getSourceCSType(getJFIF(), getAdobeDCT(), getSOF());
checkBounds(imageIndex);
initHeader(imageIndex);
Set<ImageTypeSpecifier> types = new LinkedHashSet<>();
if (rawColorModel.getColorSpace().getType() != ColorSpace.TYPE_GRAY) {
// Add the standard types, we can always convert to these, except for gray
if (rawColorModel.hasAlpha()) {
types.add(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_INT_ARGB));
types.add(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR));
types.add(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_INT_ARGB_PRE));
types.add(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR_PRE));
}
types.add(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR));
types.add(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB));
types.add(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_INT_BGR));
Iterator<ImageTypeSpecifier> types;
try {
types = delegate.getImageTypes(0);
}
catch (IndexOutOfBoundsException | NegativeArraySizeException ignore) {
types = null;
}
types.add(rawImageType);
JPEGColorSpace csType = getSourceCSType(getJFIF(), getAdobeDCT(), getSOF());
// If the source type has a luminance (Y) component, we can also convert to gray
if (sourceCSType != JPEGColorSpace.RGB && sourceCSType != JPEGColorSpace.RGBA && sourceCSType != JPEGColorSpace.CMYK) {
if (rawColorModel.hasAlpha()) {
types.add(ImageTypeSpecifiers.createGrayscale(8, DataBuffer.TYPE_BYTE, false));
if (types == null || !types.hasNext() || csType == JPEGColorSpace.CMYK || csType == JPEGColorSpace.YCCK) {
ArrayList<ImageTypeSpecifier> typeList = new ArrayList<>();
// Add the standard types, we can always convert to these
typeList.add(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR));
typeList.add(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB));
typeList.add(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_INT_BGR));
// We also read and return CMYK if the source image is CMYK/YCCK + original color profile if present
ICC_Profile profile = getEmbeddedICCProfile(false);
if (csType == JPEGColorSpace.CMYK || csType == JPEGColorSpace.YCCK) {
if (profile != null && profile.getNumComponents() == 4) {
typeList.add(ImageTypeSpecifiers.createInterleaved(ColorSpaces.createColorSpace(profile), new int[] {3, 2, 1, 0}, DataBuffer.TYPE_BYTE, false, false));
}
typeList.add(ImageTypeSpecifiers.createInterleaved(ColorSpaces.getColorSpace(ColorSpaces.CS_GENERIC_CMYK), new int[] {3, 2, 1, 0}, DataBuffer.TYPE_BYTE, false, false));
}
else if (csType == JPEGColorSpace.YCbCr || csType == JPEGColorSpace.RGB) {
if (profile != null && profile.getNumComponents() == 3) {
typeList.add(ImageTypeSpecifiers.createInterleaved(ColorSpaces.createColorSpace(profile), new int[] {0, 1, 2}, DataBuffer.TYPE_BYTE, false, false));
}
}
else if (csType == JPEGColorSpace.YCbCrA || csType == JPEGColorSpace.RGBA) {
// Prepend ARGB types
typeList.addAll(0, Arrays.asList(
ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_INT_ARGB),
ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR),
ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_INT_ARGB_PRE),
ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR_PRE)
));
if (profile != null && profile.getNumComponents() == 3) {
typeList.add(ImageTypeSpecifiers.createInterleaved(ColorSpaces.createColorSpace(profile), new int[] {0, 1, 2, 3}, DataBuffer.TYPE_BYTE, true, false));
}
}
types.add(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY));
return typeList.iterator();
}
else if (csType == JPEGColorSpace.RGB) {
// Bug in com.sun...JPEGImageReader: returns gray as acceptable type, but refuses to convert
ArrayList<ImageTypeSpecifier> typeList = new ArrayList<>();
// Filter out the gray type
while (types.hasNext()) {
ImageTypeSpecifier type = types.next();
if (type.getBufferedImageType() != BufferedImage.TYPE_BYTE_GRAY) {
typeList.add(type);
}
}
return typeList.iterator();
}
return types.iterator();
return types;
}
@Override
@@ -233,55 +273,34 @@ public final class JPEGImageReader extends ImageReaderBase {
checkBounds(imageIndex);
initHeader(imageIndex);
// Consult the image metadata
JPEGColorSpace csType = getSourceCSType(getJFIF(), getAdobeDCT(), getSOF());
ICC_Profile profile = getEmbeddedICCProfile(false);
// If delegate can determine the spec, we'll just go with that
try {
ImageTypeSpecifier rawType = delegate.getRawImageType(0);
ColorSpace cs;
boolean hasAlpha = false;
if (rawType != null) {
return rawType;
}
}
catch (IIOException | NullPointerException | ArrayIndexOutOfBoundsException | NegativeArraySizeException ignore) {
// Fall through
}
// Otherwise, consult the image metadata
JPEGColorSpace csType = getSourceCSType(getJFIF(), getAdobeDCT(), getSOF());
switch (csType) {
case GrayA:
hasAlpha = true;
case Gray:
// Create based on embedded profile if exists, otherwise create from Gray
cs = profile != null && profile.getNumComponents() == 1
? ColorSpaces.createColorSpace(profile)
: ColorSpaces.getColorSpace(ColorSpace.CS_GRAY);
return ImageTypeSpecifiers.createInterleaved(cs, hasAlpha ? new int[] {1, 0} : new int[] {0}, DataBuffer.TYPE_BYTE, hasAlpha, false);
case YCbCrA:
case RGBA:
case PhotoYCCA:
hasAlpha = true;
case YCbCr:
case RGB:
case PhotoYCC:
// Create based on PhotoYCC profile...
if (csType == JPEGColorSpace.PhotoYCC || csType == JPEGColorSpace.PhotoYCCA) {
cs = ColorSpaces.getColorSpace(ColorSpace.CS_PYCC);
}
else {
// ...or create based on embedded profile if exists, otherwise create from sRGB
cs = profile != null && profile.getNumComponents() == 3
? ColorSpaces.createColorSpace(profile)
: ColorSpaces.getColorSpace(ColorSpace.CS_sRGB);
}
return ImageTypeSpecifiers.createInterleaved(cs, hasAlpha ? new int[] {3, 2, 1, 0} : new int[] {2, 1, 0}, DataBuffer.TYPE_BYTE, hasAlpha, false);
case YCCK:
case CMYK:
// Create based on embedded profile if exists, otherwise create from "Generic CMYK"
cs = profile != null && profile.getNumComponents() == 4
? ColorSpaces.createColorSpace(profile)
: ColorSpaces.getColorSpace(ColorSpaces.CS_GENERIC_CMYK);
// Create based on embedded profile if exists, or create from "Generic CMYK"
ICC_Profile profile = getEmbeddedICCProfile(false);
return ImageTypeSpecifiers.createInterleaved(cs, new int[] {3, 2, 1, 0}, DataBuffer.TYPE_BYTE, false, false);
if (profile != null && profile.getNumComponents() == 4) {
return ImageTypeSpecifiers.createInterleaved(ColorSpaces.createColorSpace(profile), new int[]{3, 2, 1, 0}, DataBuffer.TYPE_BYTE, false, false);
}
return ImageTypeSpecifiers.createInterleaved(ColorSpaces.getColorSpace(ColorSpaces.CS_GENERIC_CMYK), new int[] {3, 2, 1, 0}, DataBuffer.TYPE_BYTE, false, false);
default:
// For other types, we probably can't give a proper type
throw new IIOException("Could not determine JPEG source color space");
// For other types, we probably can't give a proper type, return null
return null;
}
}
@@ -308,8 +327,7 @@ public final class JPEGImageReader extends ImageReaderBase {
adobeDCT = null;
}
JFIF jfif = getJFIF();
JPEGColorSpace sourceCSType = getSourceCSType(jfif, adobeDCT, sof);
JPEGColorSpace sourceCSType = getSourceCSType(getJFIF(), adobeDCT, sof);
if (sof.marker == JPEG.SOF3) {
// Read image as lossless
@@ -334,15 +352,20 @@ public final class JPEGImageReader extends ImageReaderBase {
// We need to apply ICC profile unless the profile is sRGB/default gray (whatever that is)
// - or only filter out the bad ICC profiles in the JPEGSegmentImageInputStream.
else if (bogusAdobeDCT
|| profile != null && !ColorSpaces.isCS_sRGB(profile)
|| (long) sof.lines * sof.samplesPerLine > Integer.MAX_VALUE
|| delegateCSTypeMismatch(jfif, adobeDCT, sof, sourceCSType)) {
else if (delegate.canReadRaster() && (
bogusAdobeDCT ||
sourceCSType == JPEGColorSpace.CMYK ||
sourceCSType == JPEGColorSpace.YCCK ||
profile != null && !ColorSpaces.isCS_sRGB(profile) ||
(long) sof.lines * sof.samplesPerLine > Integer.MAX_VALUE ||
!delegate.getImageTypes(0).hasNext() ||
sourceCSType == JPEGColorSpace.YCbCr && getRawImageType(imageIndex) != null)) { // TODO: Issue warning?
if (DEBUG) {
System.out.println("Reading using raster and extra conversion");
System.out.println("ICC color profile: " + profile);
}
// TODO: Possible to optimize slightly, to avoid readAsRaster for non-CMYK and other good types?
return readImageAsRasterAndReplaceColorProfile(imageIndex, param, sof, sourceCSType, profile);
}
@@ -353,56 +376,6 @@ public final class JPEGImageReader extends ImageReaderBase {
return delegate.read(0, param);
}
private boolean delegateCSTypeMismatch(final JFIF jfif, final AdobeDCT adobeDCT, final Frame startOfFrame, final JPEGColorSpace sourceCSType) throws IOException {
switch (sourceCSType) {
case GrayA:
case RGBA:
case YCbCrA:
case PhotoYCC:
case PhotoYCCA:
case CMYK:
case YCCK:
// These are no longer supported by the delegate, we'll handle ourselves
return true;
}
try {
ImageTypeSpecifier rawImageType = delegate.getRawImageType(0);
switch (sourceCSType) {
case Gray:
return rawImageType == null || rawImageType.getColorModel().getColorSpace().getType() != ColorSpace.TYPE_GRAY;
case YCbCr:
// NOTE: For backwards compatibility, null is allowed for YCbCr
if (rawImageType == null) {
return false;
}
// If We have a JFIF, but with non-standard component Ids, the standard reader mistakes it for RGB
if (jfif != null && (startOfFrame.components[0].id != 1 || startOfFrame.components[1].id != 2 || startOfFrame.components[2].id != 3)) {
return true;
}
// Else, if we have no Adobe marker and no subsampling, the standard reader mistakes it for RGB
else if (adobeDCT == null
&& (startOfFrame.components[0].id != 1 || startOfFrame.components[1].id != 2 || startOfFrame.components[2].id != 3)
&& (startOfFrame.components[0].hSub == 1 || startOfFrame.components[0].vSub == 1
|| startOfFrame.components[1].hSub == 1 || startOfFrame.components[1].vSub == 1
|| startOfFrame.components[2].hSub == 1 || startOfFrame.components[2].vSub == 1)) {
return true;
}
case RGB:
return rawImageType == null || rawImageType.getColorModel().getColorSpace().getType() != ColorSpace.TYPE_RGB;
default:
// Probably needs special handling, but we don't know what to do...
return false;
}
}
catch (IIOException | NullPointerException | ArrayIndexOutOfBoundsException | NegativeArraySizeException ignore) {
// An exception here is a clear indicator we need to handle conversion
return true;
}
}
private BufferedImage readImageAsRasterAndReplaceColorProfile(int imageIndex, ImageReadParam param, Frame startOfFrame, JPEGColorSpace csType, ICC_Profile profile) throws IOException {
int origWidth = getWidth(imageIndex);
int origHeight = getHeight(imageIndex);
@@ -420,10 +393,7 @@ public final class JPEGImageReader extends ImageReaderBase {
RasterOp convert = null;
ICC_ColorSpace intendedCS = profile != null ? ColorSpaces.createColorSpace(profile) : null;
if (destination.getNumBands() <= 2 && (csType != JPEGColorSpace.Gray && csType != JPEGColorSpace.GrayA)) {
convert = new LuminanceToGray();
}
else if (profile != null && (csType == JPEGColorSpace.Gray || csType == JPEGColorSpace.GrayA)) {
if (profile != null && (csType == JPEGColorSpace.Gray || csType == JPEGColorSpace.GrayA)) {
// com.sun. reader does not do ColorConvertOp for CS_GRAY, even if embedded ICC profile,
// probably because IJG native part does it already...? If applied, color looks wrong (too dark)...
// convert = new ColorConvertOp(intendedCS, image.getColorModel().getColorSpace(), null);
@@ -432,7 +402,8 @@ public final class JPEGImageReader extends ImageReaderBase {
// Handle inconsistencies
if (startOfFrame.componentsInFrame() != intendedCS.getNumComponents()) {
// If ICC profile number of components and startOfFrame does not match, ignore ICC profile
processWarningOccurred(String.format("Embedded ICC color profile is incompatible with image data. " +
processWarningOccurred(String.format(
"Embedded ICC color profile is incompatible with image data. " +
"Profile indicates %d components, but SOF%d has %d color components. " +
"Ignoring ICC profile, assuming source color space %s.",
intendedCS.getNumComponents(), startOfFrame.marker & 0xf, startOfFrame.componentsInFrame(), csType
@@ -456,7 +427,10 @@ public final class JPEGImageReader extends ImageReaderBase {
ColorSpace cmykCS = ColorSpaces.getColorSpace(ColorSpaces.CS_GENERIC_CMYK);
if (cmykCS instanceof ICC_ColorSpace) {
processWarningOccurred("No embedded ICC color profile, defaulting to \"generic\" CMYK ICC profile. Colors may look incorrect.");
processWarningOccurred(
"No embedded ICC color profile, defaulting to \"generic\" CMYK ICC profile. " +
"Colors may look incorrect."
);
// NOTE: Avoid using CCOp if same color space, as it's more compatible that way
if (cmykCS != image.getColorModel().getColorSpace()) {
@@ -465,11 +439,17 @@ public final class JPEGImageReader extends ImageReaderBase {
}
else {
// ColorConvertOp using non-ICC CS is deadly slow, fall back to fast conversion instead
processWarningOccurred("No embedded ICC color profile, will convert using inaccurate CMYK to RGB conversion. Colors may look incorrect.");
processWarningOccurred(
"No embedded ICC color profile, will convert using inaccurate CMYK to RGB conversion. " +
"Colors may look incorrect."
);
convert = new FastCMYKToRGB();
}
}
else if (profile != null) {
processWarningOccurred("Embedded ICC color profile is incompatible with Java 2D, color profile will be ignored.");
}
// We'll need a read param
if (param == null) {
@@ -548,9 +528,10 @@ public final class JPEGImageReader extends ImageReaderBase {
switch (adobeDCT.transform) {
case AdobeDCT.Unknown:
return JPEGColorSpace.RGB;
case AdobeDCT.YCC:
return JPEGColorSpace.YCbCr;
default:
// TODO: Warning!
case AdobeDCT.YCC:
return JPEGColorSpace.YCbCr; // assume it's YCbCr
}
}
@@ -580,9 +561,10 @@ public final class JPEGImageReader extends ImageReaderBase {
switch (adobeDCT.transform) {
case AdobeDCT.Unknown:
return JPEGColorSpace.CMYK;
case AdobeDCT.YCCK:
return JPEGColorSpace.YCCK;
default:
// TODO: Warning!
case AdobeDCT.YCCK:
return JPEGColorSpace.YCCK; // assume it's YCCK
}
}
@@ -659,12 +641,6 @@ public final class JPEGImageReader extends ImageReaderBase {
try {
if (imageInput != null) {
// Need to wrap stream to avoid messing with the byte order of the underlying stream
// in the case we are operating as a delegate for ie. TIFFImageReader.
if (!(imageInput instanceof SubImageInputStream)) {
imageInput = new SubImageInputStream(imageInput, Long.MAX_VALUE);
}
streamOffsets.add(imageInput.getStreamPosition());
}
@@ -679,7 +655,7 @@ public final class JPEGImageReader extends ImageReaderBase {
private void initDelegate(boolean seekForwardOnly, boolean ignoreMetadata) throws IOException {
// JPEGSegmentImageInputStream that filters out/skips bad/unnecessary segments
delegate.setInput(imageInput != null
? new JPEGSegmentImageInputStream(imageInput, new JPEGSegmentWarningDelegate())
? new JPEGSegmentImageInputStream(new SubImageInputStream(imageInput, Long.MAX_VALUE), new JPEGSegmentStreamWarningDelegate())
: null, seekForwardOnly, ignoreMetadata);
}
@@ -717,7 +693,6 @@ public final class JPEGImageReader extends ImageReaderBase {
}
private void initHeader(final int imageIndex) throws IOException {
assertInput();
if (imageIndex < 0) {
throw new IndexOutOfBoundsException("imageIndex < 0: " + imageIndex);
}
@@ -734,6 +709,7 @@ public final class JPEGImageReader extends ImageReaderBase {
thumbnails = null;
initDelegate(seekForwardOnly, ignoreMetadata);
initHeader();
}
@@ -817,14 +793,13 @@ public final class JPEGImageReader extends ImageReaderBase {
// TODO: We should probably optimize this
try {
segments = null;
getSOF(); // No SOF, no image
count++;
}
catch (IIOException ignore) {}
}
imageInput.seek(streamOffsets.get(currentStreamIndex));
currentStreamIndex = -1;
return count;
}
@@ -902,30 +877,41 @@ public final class JPEGImageReader extends ImageReaderBase {
return jfxx.isEmpty() ? null : (JFXX) jfxx.get(0);
}
private EXIF getExif() throws IOException {
List<Application> exif = getAppSegments(JPEG.APP1, "Exif");
return exif.isEmpty() ? null : (EXIF) exif.get(0); // TODO: Can there actually be more Exif segments?
}
private CompoundDirectory getExif() throws IOException {
List<Application> exifSegments = getAppSegments(JPEG.APP1, "Exif");
private CompoundDirectory parseExif(final EXIF exif) throws IOException {
if (exif != null) {
// Identifier is "Exif\0" + 1 byte pad
if (exif.data.length > exif.identifier.length() + 2) {
try (ImageInputStream stream = exif.exifData()) {
if (!exifSegments.isEmpty()) {
Application exif = exifSegments.get(0);
int offset = exif.identifier.length() + 2; // Incl. pad
if (exif.data.length <= offset) {
processWarningOccurred("Exif chunk has no data.");
}
else {
// TODO: Consider returning ByteArrayImageInputStream from Segment.data()
try (ImageInputStream stream = new ByteArrayImageInputStream(exif.data, offset, exif.data.length - offset)) {
return (CompoundDirectory) new TIFFReader().read(stream);
}
catch (IIOException e) {
processWarningOccurred("Exif chunk is present, but can't be read: " + e.getMessage());
}
}
else {
processWarningOccurred("Exif chunk has no data.");
}
}
return null;
}
// TODO: Util method?
static byte[] readFully(DataInput stream, int len) throws IOException {
if (len == 0) {
return null;
}
byte[] data = new byte[len];
stream.readFully(data);
return data;
}
ICC_Profile getEmbeddedICCProfile(final boolean allowBadIndexes) throws IOException {
// ICC v 1.42 (2006) annex B:
// APP2 marker (0xFFE2) + 2 byte length + ASCII 'ICC_PROFILE' + 0 (termination)
@@ -1091,39 +1077,109 @@ public final class JPEGImageReader extends ImageReaderBase {
if (thumbnails == null) {
thumbnails = new ArrayList<>();
ThumbnailReadProgressListener thumbnailProgressDelegator = new ThumbnailProgressDelegate();
// Read JFIF thumbnails if present
try {
ThumbnailReader thumbnail = JFIFThumbnail.from(getJFIF());
if (thumbnail != null) {
thumbnails.add(thumbnail);
}
}
catch (IOException e) {
processWarningOccurred(e.getMessage());
JFIF jfif = getJFIF();
if (jfif != null && jfif.thumbnail != null) {
// TODO: Check if the JFIF segment really has room for this thumbnail?
thumbnails.add(new JFIFThumbnailReader(thumbnailProgressDelegator, imageIndex, thumbnails.size(), jfif));
}
// Read JFXX thumbnails if present
try {
ThumbnailReader thumbnail = JFXXThumbnail.from(getJFXX(), getThumbnailReader());
if (thumbnail != null) {
thumbnails.add(thumbnail);
JFXX jfxx = getJFXX();
if (jfxx != null && jfxx.thumbnail != null) {
switch (jfxx.extensionCode) {
case JFXX.JPEG:
case JFXX.INDEXED:
case JFXX.RGB:
// TODO: Check if the JFXX segment really has room for this thumbnail?
thumbnails.add(new JFXXThumbnailReader(thumbnailProgressDelegator, getThumbnailReader(), imageIndex, thumbnails.size(), jfxx));
break;
default:
processWarningOccurred("Unknown JFXX extension code: " + jfxx.extensionCode);
}
}
catch (IOException e) {
processWarningOccurred(e.getMessage());
}
// Read Exif thumbnails if present
try {
EXIF exif = getExif();
ThumbnailReader thumbnailReader = EXIFThumbnail.from(exif, parseExif(exif), getThumbnailReader());
if (thumbnailReader != null) {
thumbnails.add(thumbnailReader);
List<Application> exifSegments = getAppSegments(JPEG.APP1, "Exif");
if (!exifSegments.isEmpty()) {
Application exif = exifSegments.get(0);
// Identifier is "Exif\0" + 1 byte pad
int dataOffset = exif.identifier.length() + 2;
if (exif.data.length <= dataOffset) {
processWarningOccurred("Exif chunk has no data.");
}
else {
ImageInputStream stream = new ByteArrayImageInputStream(exif.data, dataOffset, exif.data.length - dataOffset);
try {
CompoundDirectory exifMetadata = (CompoundDirectory) new TIFFReader().read(stream);
if (exifMetadata.directoryCount() == 2) {
Directory ifd1 = exifMetadata.getDirectory(1);
// Compression: 1 = no compression, 6 = JPEG compression (default)
Entry compressionEntry = ifd1.getEntryById(TIFF.TAG_COMPRESSION);
int compression = compressionEntry == null ? 6 : ((Number) compressionEntry.getValue()).intValue();
if (compression == 6) {
Entry jpegOffEntry = ifd1.getEntryById(TIFF.TAG_JPEG_INTERCHANGE_FORMAT);
if (jpegOffEntry != null) {
Entry jpegLenEntry = ifd1.getEntryById(TIFF.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
// Test if Exif thumbnail is contained within the Exif segment (offset + length <= segment.length)
long jpegOffset = ((Number) jpegOffEntry.getValue()).longValue();
long jpegLength = jpegLenEntry != null ? ((Number) jpegLenEntry.getValue()).longValue() : -1;
if (jpegLength > 0 && jpegOffset + jpegLength <= stream.length()) {
// Verify first bytes are FFD8
stream.seek(jpegOffset);
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
if (stream.readUnsignedShort() == JPEG.SOI) {
thumbnails.add(new EXIFThumbnailReader(thumbnailProgressDelegator, getThumbnailReader(), 0, thumbnails.size(), ifd1, stream));
}
// TODO: Simplify this warning fallback stuff...
else {
processWarningOccurred("EXIF IFD with empty or incomplete JPEG thumbnail");
}
}
else {
processWarningOccurred("EXIF IFD with empty or incomplete JPEG thumbnail");
}
}
else {
processWarningOccurred("EXIF IFD with JPEG thumbnail missing JPEGInterchangeFormat tag");
}
}
else if (compression == 1) {
Entry stripOffEntry = ifd1.getEntryById(TIFF.TAG_STRIP_OFFSETS);
if (stripOffEntry != null) {
long stripOffset = ((Number) stripOffEntry.getValue()).longValue();
if (stripOffset < stream.length()) {
// TODO: Verify length of Exif thumbnail vs length of segment like in JPEG
// ...but this requires so many extra values... Instead move this logic to the
// EXIFThumbnailReader?
thumbnails.add(new EXIFThumbnailReader(thumbnailProgressDelegator, getThumbnailReader(), 0, thumbnails.size(), ifd1, stream));
}
else {
processWarningOccurred("EXIF IFD with empty or incomplete uncompressed thumbnail");
}
}
else {
processWarningOccurred("EXIF IFD with uncompressed thumbnail missing StripOffsets tag");
}
}
else {
processWarningOccurred("EXIF IFD with unknown compression (expected 1 or 6): " + compression);
}
}
}
catch (IIOException e) {
processWarningOccurred("Exif chunk present, but can't be read: " + e.getMessage());
}
}
}
catch (IOException e) {
processWarningOccurred(e.getMessage());
}
}
}
@@ -1166,13 +1222,13 @@ public final class JPEGImageReader extends ImageReaderBase {
public BufferedImage readThumbnail(int imageIndex, int thumbnailIndex) throws IOException {
checkThumbnailBounds(imageIndex, thumbnailIndex);
processThumbnailStarted(imageIndex, thumbnailIndex);
processThumbnailProgress(0f);
// processThumbnailStarted(imageIndex, thumbnailIndex);
// processThumbnailProgress(0f);
BufferedImage thumbnail = thumbnails.get(thumbnailIndex).read();;
processThumbnailProgress(100f);
processThumbnailComplete();
// processThumbnailProgress(100f);
// processThumbnailComplete();
return thumbnail;
}
@@ -1183,7 +1239,7 @@ public final class JPEGImageReader extends ImageReaderBase {
public IIOMetadata getImageMetadata(int imageIndex) throws IOException {
initHeader(imageIndex);
return new JPEGImage10Metadata(segments, getSOF(), getJFIF(), getJFXX(), getEmbeddedICCProfile(true), getAdobeDCT(), parseExif(getExif()));
return new JPEGImage10Metadata(segments, getSOF(), getJFIF(), getJFXX(), getEmbeddedICCProfile(true), getAdobeDCT(), getExif());
}
@Override
@@ -1308,7 +1364,24 @@ public final class JPEGImageReader extends ImageReaderBase {
}
}
private class JPEGSegmentWarningDelegate implements JPEGSegmentWarningListener {
private class ThumbnailProgressDelegate implements ThumbnailReadProgressListener {
@Override
public void thumbnailStarted(int imageIndex, int thumbnailIndex) {
processThumbnailStarted(imageIndex, thumbnailIndex);
}
@Override
public void thumbnailProgress(float percentageDone) {
processThumbnailProgress(percentageDone);
}
@Override
public void thumbnailComplete() {
processThumbnailComplete();
}
}
private class JPEGSegmentStreamWarningDelegate implements JPEGSegmentStreamWarningListener {
@Override
public void warningOccurred(String warning) {
processWarningOccurred(warning);
@@ -31,6 +31,8 @@
package com.twelvemonkeys.imageio.plugins.jpeg;
import com.twelvemonkeys.imageio.stream.BufferedImageInputStream;
import javax.imageio.IIOException;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
@@ -75,7 +77,7 @@ final class JPEGLosslessDecoderWrapper {
* @throws IOException is thrown if the decoder failed or a conversion is not supported
*/
BufferedImage readImage(final List<Segment> segments, final ImageInputStream input) throws IOException {
JPEGLosslessDecoder decoder = new JPEGLosslessDecoder(segments, input, listenerDelegate);
JPEGLosslessDecoder decoder = new JPEGLosslessDecoder(segments, createBufferedInput(input), listenerDelegate);
// TODO: Allow 10/12/14 bit (using a ComponentColorModel with correct bits, as in TIFF)
// TODO: Rewrite this to pass a pre-allocated buffer of correct type (byte/short)/correct bands
@@ -109,6 +111,10 @@ final class JPEGLosslessDecoderWrapper {
throw new IIOException("JPEG Lossless with " + decoder.getPrecision() + " bit precision and " + decoder.getNumComponents() + " component(s) not supported");
}
private ImageInputStream createBufferedInput(final ImageInputStream input) throws IOException {
return input instanceof BufferedImageInputStream ? input : new BufferedImageInputStream(input);
}
Raster readRaster(final List<Segment> segments, final ImageInputStream input) throws IOException {
// TODO: Can perhaps be implemented faster
return readImage(segments, input).getRaster();
@@ -50,8 +50,8 @@ final class JPEGProviderInfo extends ReaderWriterProviderInfo {
new String[] {"com.twelvemonkeys.imageio.plugins.jpeg.JPEGImageReaderSpi"},
"com.twelvemonkeys.imageio.plugins.jpeg.JPEGImageWriter",
new String[] {"com.twelvemonkeys.imageio.plugins.jpeg.JPEGImageWriterSpi"},
false, "javax_imageio_jpeg_stream_1.0", null, null, null,
true, "javax_imageio_jpeg_image_1.0", null, null, null
false, null, null, null, null,
true, null, null, null, null
);
}
}
@@ -59,7 +59,7 @@ final class JPEGSegmentImageInputStream extends ImageInputStreamImpl {
// TODO: Support multiple JPEG streams (SOI...EOI, SOI...EOI, ...) in a single file
private final ImageInputStream stream;
private final JPEGSegmentWarningListener warningListener;
private final JPEGSegmentStreamWarningListener warningListener;
private final ComponentIdSet componentIds = new ComponentIdSet();
@@ -67,13 +67,14 @@ final class JPEGSegmentImageInputStream extends ImageInputStreamImpl {
private int currentSegment = -1;
private Segment segment;
JPEGSegmentImageInputStream(final ImageInputStream stream, final JPEGSegmentWarningListener warningListener) {
JPEGSegmentImageInputStream(final ImageInputStream stream, final JPEGSegmentStreamWarningListener warningListener) {
this.stream = notNull(stream, "stream");
this.warningListener = notNull(warningListener, "warningListener");
}
JPEGSegmentImageInputStream(final ImageInputStream stream) {
this(stream, JPEGSegmentWarningListener.NULL_LISTENER);
this(stream, JPEGSegmentStreamWarningListener.NULL_LISTENER);
}
private void processWarningOccured(final String warning) {
@@ -149,6 +150,7 @@ final class JPEGSegmentImageInputStream extends ImageInputStreamImpl {
else {
if (marker == JPEG.EOI) {
segment = new Segment(marker, realPosition, segment.end(), 2);
segments.add(segment);
}
else {
// Length including length field itself
@@ -163,7 +165,6 @@ final class JPEGSegmentImageInputStream extends ImageInputStreamImpl {
// Inspect segment, see if we have 16 bit precision (assuming segments will not contain
// multiple quality tables with varying precision)
int qtInfo = stream.read();
if ((qtInfo & 0x10) == 0x10) {
processWarningOccured("16 bit DQT encountered");
segment = new DownsampledDQTReplacement(realPosition, segment.end(), length, qtInfo, stream);
@@ -187,9 +188,10 @@ final class JPEGSegmentImageInputStream extends ImageInputStreamImpl {
else {
segment = new Segment(marker, realPosition, segment.end(), length);
}
segments.add(segment);
}
segments.add(segment);
currentSegment = segments.size() - 1;
if (marker == JPEG.SOS) {
@@ -332,7 +334,7 @@ final class JPEGSegmentImageInputStream extends ImageInputStreamImpl {
}
private void streamInit() throws IOException {
long position = stream.getStreamPosition();
stream.seek(0);
try {
int soi = stream.readUnsignedShort();
@@ -341,7 +343,7 @@ final class JPEGSegmentImageInputStream extends ImageInputStreamImpl {
throw new IIOException(String.format("Not a JPEG stream (starts with: 0x%04x, expected SOI: 0x%04x)", soi, JPEG.SOI));
}
segment = new Segment(soi, position, 0, 2);
segment = new Segment(soi, 0, 0, 2);
segments.add(segment);
currentSegment = segments.size() - 1; // 0
@@ -33,10 +33,10 @@ package com.twelvemonkeys.imageio.plugins.jpeg;
/**
* JPEGSegmentStreamWarningListener
*/
interface JPEGSegmentWarningListener {
interface JPEGSegmentStreamWarningListener {
void warningOccurred(String warning);
JPEGSegmentWarningListener NULL_LISTENER = new JPEGSegmentWarningListener() {
JPEGSegmentStreamWarningListener NULL_LISTENER = new JPEGSegmentStreamWarningListener() {
@Override
public void warningOccurred(final String warning) {}
};
@@ -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.plugins.jpeg;
import com.twelvemonkeys.lang.Validate;
import java.awt.*;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.Raster;
import java.awt.image.RasterOp;
import java.awt.image.WritableRaster;
/**
* LuminanceToGray.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: LuminanceToGray.java,v 1.0 10/04/2021 haraldk Exp$
*/
final class LuminanceToGray implements RasterOp {
@Override
public WritableRaster filter(final Raster src, WritableRaster dest) {
Validate.notNull(src, "src may not be null");
Validate.isTrue(src != dest, "src and dest raster may not be same");
Validate.isTrue(src.getNumDataElements() >= 3, src.getNumDataElements(), "luminance raster must have at least 3 data elements: %s");
if (dest == null) {
dest = createCompatibleDestRaster(src);
}
// If src and dest have alpha component, keep it, otherwise extract luminance only
int[] bandList = src.getNumBands() > 3 && dest.getNumBands() > 1 ? new int[] {0, 3} : new int[] {0};
dest.setRect(0, 0, src.createChild(0, 0, src.getWidth(), src.getHeight(), 0, 0, bandList));
return dest;
}
@Override
public Rectangle2D getBounds2D(final Raster src) {
return src.getBounds();
}
@Override
public WritableRaster createCompatibleDestRaster(final Raster src) {
return src.createCompatibleWritableRaster()
.createWritableChild(0, 0, src.getWidth(), src.getHeight(), 0, 0, new int[] {0});
}
@Override
public Point2D getPoint2D(final Point2D srcPt, Point2D dstPt) {
if (dstPt == null) {
dstPt = new Point2D.Double(srcPt.getX(), srcPt.getY());
}
else {
dstPt.setLocation(srcPt);
}
return dstPt;
}
@Override
public RenderingHints getRenderingHints() {
return null;
}
}
@@ -30,31 +30,17 @@
package com.twelvemonkeys.imageio.plugins.jpeg;
import com.twelvemonkeys.imageio.plugins.jpeg.ThumbnailReader.UncompressedThumbnailReader;
import javax.imageio.IIOException;
import java.io.IOException;
/**
* JFIFThumbnail
* ThumbnailReadProgressListener
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: JFIFThumbnail.java,v 1.0 18.04.12 12:19 haraldk Exp$
* @version $Id: ThumbnailReadProgressListener.java,v 1.0 07.05.12 10:15 haraldk Exp$
*/
final class JFIFThumbnail {
private JFIFThumbnail() {
}
interface ThumbnailReadProgressListener {
void thumbnailStarted(int imageIndex, int thumbnailIndex);
static ThumbnailReader from(final JFIF segment) throws IOException {
if (segment != null && segment.xThumbnail > 0 && segment.yThumbnail > 0) {
if (segment.thumbnail == null || segment.thumbnail.length < segment.xThumbnail * segment.yThumbnail) {
throw new IIOException("Truncated JFIF thumbnail");
}
void thumbnailProgress(float percentageDone);
return new UncompressedThumbnailReader(segment.xThumbnail, segment.yThumbnail, segment.thumbnail);
}
return null;
}
void thumbnailComplete();
}
@@ -31,16 +31,12 @@
package com.twelvemonkeys.imageio.plugins.jpeg;
import javax.imageio.ImageReader;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
import java.awt.color.ColorSpace;
import java.awt.image.*;
import java.io.IOException;
import static com.twelvemonkeys.lang.Validate.isTrue;
import static com.twelvemonkeys.lang.Validate.notNull;
/**
* ThumbnailReader
*
@@ -50,156 +46,68 @@ import static com.twelvemonkeys.lang.Validate.notNull;
*/
abstract class ThumbnailReader {
private final ThumbnailReadProgressListener progressListener;
protected final int imageIndex;
protected final int thumbnailIndex;
protected ThumbnailReader(final ThumbnailReadProgressListener progressListener, final int imageIndex, final int thumbnailIndex) {
this.progressListener = progressListener != null ? progressListener : new NullProgressListener();
this.imageIndex = imageIndex;
this.thumbnailIndex = thumbnailIndex;
}
protected final void processThumbnailStarted() {
progressListener.thumbnailStarted(imageIndex, thumbnailIndex);
}
protected final void processThumbnailProgress(float percentageDone) {
progressListener.thumbnailProgress(percentageDone);
}
protected final void processThumbnailComplete() {
progressListener.thumbnailComplete();
}
static protected BufferedImage readJPEGThumbnail(final ImageReader reader, final ImageInputStream stream) throws IOException {
reader.setInput(stream);
return reader.read(0);
}
static protected BufferedImage readRawThumbnail(final byte[] thumbnail, final int size, final int offset, int w, int h) {
DataBufferByte buffer = new DataBufferByte(thumbnail, size, offset);
WritableRaster raster;
ColorModel cm;
if (thumbnail.length == w * h) {
raster = Raster.createInterleavedRaster(buffer, w, h, w, 1, new int[] {0}, null);
cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
}
else {
raster = Raster.createInterleavedRaster(buffer, w, h, w * 3, 3, new int[] {0, 1, 2}, null);
cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
}
return new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
}
public abstract BufferedImage read() throws IOException;
public abstract int getWidth() throws IOException;
public abstract int getHeight() throws IOException;
public IIOMetadata readMetadata() throws IOException {
return null;
}
static class UncompressedThumbnailReader extends ThumbnailReader {
private final int width;
private final int height;
private final byte[] data;
private final int offset;
public UncompressedThumbnailReader(int width, int height, byte[] data) {
this(width, height, data, 0);
}
public UncompressedThumbnailReader(int width, int height, byte[] data, int offset) {
this.width = isTrue(width > 0, width, "width");
this.height = isTrue(height > 0, height, "height");;
this.data = notNull(data, "data");
this.offset = isTrue(offset >= 0 && offset < data.length, offset, "offset");
private static class NullProgressListener implements ThumbnailReadProgressListener {
@Override
public void thumbnailStarted(int imageIndex, int thumbnailIndex) {
}
@Override
public BufferedImage read() throws IOException {
DataBufferByte buffer = new DataBufferByte(data, data.length, offset);
WritableRaster raster;
ColorModel cm;
if (data.length == width * height) {
raster = Raster.createInterleavedRaster(buffer, width, height, width, 1, new int[] {0}, null);
cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
}
else {
raster = Raster.createInterleavedRaster(buffer, width, height, width * 3, 3, new int[] {0, 1, 2}, null);
cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
}
return new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
public void thumbnailProgress(float percentageDone) {
}
@Override
public int getWidth() throws IOException {
return width;
}
@Override
public int getHeight() throws IOException {
return height;
}
}
static class IndexedThumbnailReader extends ThumbnailReader {
private final int width;
private final int height;
private final byte[] palette;
private final int paletteOff;
private final byte[] data;
private final int dataOff;
public IndexedThumbnailReader(final int width, int height, final byte[] palette, final int paletteOff, final byte[] data, final int dataOff) {
this.width = isTrue(width > 0, width, "width");
this.height = isTrue(height > 0, height, "height");;
this.palette = notNull(palette, "palette");
this.paletteOff = isTrue(paletteOff >= 0 && paletteOff < palette.length, paletteOff, "paletteOff");
this.data = notNull(data, "data");
this.dataOff = isTrue(dataOff >= 0 && dataOff < data.length, dataOff, "dataOff");
}
@Override
public BufferedImage read() throws IOException {
// 256 RGB triplets
int[] rgbs = new int[256];
for (int i = 0; i < rgbs.length; i++) {
rgbs[i] = (palette[paletteOff + 3 * i ] & 0xff) << 16
| (palette[paletteOff + 3 * i + 1] & 0xff) << 8
| (palette[paletteOff + 3 * i + 2] & 0xff);
}
IndexColorModel icm = new IndexColorModel(8, rgbs.length, rgbs, 0, false, -1, DataBuffer.TYPE_BYTE);
DataBufferByte buffer = new DataBufferByte(data, data.length - dataOff, dataOff);
WritableRaster raster = Raster.createPackedRaster(buffer, width, height, 8, null);
return new BufferedImage(icm, raster, icm.isAlphaPremultiplied(), null);
}
@Override
public int getWidth() throws IOException {
return width;
}
@Override
public int getHeight() throws IOException {
return height;
}
}
static class JPEGThumbnailReader extends ThumbnailReader {
private final ImageReader reader;
private final ImageInputStream input;
private final long offset;
private Dimension dimension;
public JPEGThumbnailReader(final ImageReader reader, final ImageInputStream input, final long offset) {
this.reader = notNull(reader, "reader");
this.input = notNull(input, "input");
this.offset = isTrue(offset >= 0, offset, "offset");
}
private void initReader() throws IOException {
if (reader.getInput() != input) {
input.seek(offset);
reader.setInput(input);
}
}
@Override
public BufferedImage read() throws IOException {
initReader();
return reader.read(0, null);
}
private Dimension readDimensions() throws IOException {
if (dimension == null) {
initReader();
dimension = new Dimension(reader.getWidth(0), reader.getHeight(0));
}
return dimension;
}
@Override
public int getWidth() throws IOException {
return readDimensions().width;
}
@Override
public int getHeight() throws IOException {
return readDimensions().height;
}
@Override
public IIOMetadata readMetadata() throws IOException {
initReader();
return reader.getImageMetadata(0);
public void thumbnailComplete() {
}
}
}
@@ -52,7 +52,9 @@ public abstract class AbstractThumbnailReaderTest {
IIORegistry.getDefaultInstance().registerServiceProvider(new URLImageInputStreamSpi());
}
protected abstract ThumbnailReader createReader(ImageInputStream stream) throws IOException;
protected abstract ThumbnailReader createReader(
ThumbnailReadProgressListener progressListener, int imageIndex, int thumbnailIndex, ImageInputStream stream
) throws IOException;
protected final ImageInputStream createStream(final String name) throws IOException {
URL resource = getClass().getResource(name);
@@ -30,33 +30,23 @@
package com.twelvemonkeys.imageio.plugins.jpeg;
import com.twelvemonkeys.imageio.metadata.AbstractCompoundDirectory;
import com.twelvemonkeys.imageio.metadata.CompoundDirectory;
import com.twelvemonkeys.imageio.metadata.Entry;
import com.twelvemonkeys.imageio.metadata.jpeg.JPEG;
import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegment;
import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegmentUtil;
import com.twelvemonkeys.imageio.metadata.tiff.IFD;
import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
import com.twelvemonkeys.imageio.metadata.tiff.TIFFEntry;
import com.twelvemonkeys.imageio.metadata.tiff.TIFFReader;
import org.junit.After;
import org.junit.Test;
import org.mockito.InOrder;
import javax.imageio.IIOException;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import java.awt.image.BufferedImage;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
/**
* EXIFThumbnailReaderTest
@@ -67,175 +57,31 @@ import static org.junit.Assert.*;
*/
public class EXIFThumbnailReaderTest extends AbstractThumbnailReaderTest {
private final ImageReader thumbnailReader = ImageIO.getImageReadersByFormatName("jpeg").next();
@After
public void tearDown() {
thumbnailReader.dispose();
}
@Test
public void testFromNullSegment() throws IOException {
assertNull(EXIFThumbnail.from(null, null, thumbnailReader));
}
@Test
public void testFromNullIFD() throws IOException {
assertNull(EXIFThumbnail.from(new EXIF(new byte[0]), null, thumbnailReader));
}
@Test
public void testFromEmptyIFD() throws IOException {
assertNull(EXIFThumbnail.from(new EXIF(new byte[0]), new EXIFDirectory(), thumbnailReader));
}
@Test
public void testFromSingleIFD() throws IOException {
assertNull(EXIFThumbnail.from(new EXIF(new byte[42]), new EXIFDirectory(new IFD(Collections.<Entry>emptyList())), thumbnailReader));
}
@Test(expected = IIOException.class)
public void testFromMissingThumbnail() throws IOException {
EXIFThumbnail.from(new EXIF(new byte[42]), new EXIFDirectory(new IFD(Collections.<Entry>emptyList()), new IFD(Collections.<Entry>emptyList())), thumbnailReader);
}
@Test(expected = IIOException.class)
public void testFromUnsupportedThumbnailCompression() throws IOException {
List<TIFFEntry> entries = Collections.singletonList(new TIFFEntry(TIFF.TAG_COMPRESSION, 42));
EXIFThumbnail.from(new EXIF(new byte[42]), new EXIFDirectory(new IFD(Collections.<Entry>emptyList()), new IFD(entries)), thumbnailReader);
}
@Test(expected = IIOException.class)
public void testFromMissingOffsetUncompressed() throws IOException {
List<TIFFEntry> entries = Arrays.asList(
new TIFFEntry(TIFF.TAG_COMPRESSION, 1),
new TIFFEntry(TIFF.TAG_IMAGE_WIDTH, 16),
new TIFFEntry(TIFF.TAG_IMAGE_HEIGHT, 9)
);
EXIFThumbnail.from(new EXIF(new byte[6 + 16 * 9 * 3]), new EXIFDirectory(new IFD(Collections.<Entry>emptyList()), new IFD(entries)), thumbnailReader);
}
@Test(expected = IIOException.class)
public void testFromMissingWidthUncompressed() throws IOException {
List<TIFFEntry> entries = Arrays.asList(
new TIFFEntry(TIFF.TAG_COMPRESSION, 1),
new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, 0),
new TIFFEntry(TIFF.TAG_IMAGE_HEIGHT, 9)
);
EXIFThumbnail.from(new EXIF(new byte[6 + 16 * 9 * 3]), new EXIFDirectory(new IFD(Collections.<Entry>emptyList()), new IFD(entries)), thumbnailReader);
}
@Test(expected = IIOException.class)
public void testFromMissingHeightUncompressed() throws IOException {
List<TIFFEntry> entries = Arrays.asList(
new TIFFEntry(TIFF.TAG_COMPRESSION, 1),
new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, 0),
new TIFFEntry(TIFF.TAG_IMAGE_WIDTH, 16)
);
EXIFThumbnail.from(new EXIF(new byte[6 + 16 * 9 * 3]), new EXIFDirectory(new IFD(Collections.<Entry>emptyList()), new IFD(entries)), thumbnailReader);
}
@Test(expected = IIOException.class)
public void testFromUnsupportedPhotometricUncompressed() throws IOException {
List<TIFFEntry> entries = Arrays.asList(
new TIFFEntry(TIFF.TAG_COMPRESSION, 1),
new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, 0),
new TIFFEntry(TIFF.TAG_IMAGE_WIDTH, 16),
new TIFFEntry(TIFF.TAG_IMAGE_HEIGHT, 9),
new TIFFEntry(TIFF.TAG_PHOTOMETRIC_INTERPRETATION, 42)
);
EXIFThumbnail.from(new EXIF(new byte[6 + 16 * 9 * 3]), new EXIFDirectory(new IFD(Collections.<Entry>emptyList()), new IFD(entries)), thumbnailReader);
}
@Test(expected = IIOException.class)
public void testFromUnsupportedBitsPerSampleUncompressed() throws IOException {
List<TIFFEntry> entries = Arrays.asList(
new TIFFEntry(TIFF.TAG_COMPRESSION, 1),
new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, 0),
new TIFFEntry(TIFF.TAG_IMAGE_WIDTH, 16),
new TIFFEntry(TIFF.TAG_IMAGE_HEIGHT, 9),
new TIFFEntry(TIFF.TAG_BITS_PER_SAMPLE, new int[]{5, 6, 5})
);
EXIFThumbnail.from(new EXIF(new byte[6 + 16 * 9 * 3]), new EXIFDirectory(new IFD(Collections.<Entry>emptyList()), new IFD(entries)), thumbnailReader);
}
@Test(expected = IIOException.class)
public void testFromUnsupportedSamplesPerPixelUncompressed() throws IOException {
List<TIFFEntry> entries = Arrays.asList(
new TIFFEntry(TIFF.TAG_COMPRESSION, 1),
new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, 0),
new TIFFEntry(TIFF.TAG_IMAGE_WIDTH, 160),
new TIFFEntry(TIFF.TAG_IMAGE_HEIGHT, 90),
new TIFFEntry(TIFF.TAG_SAMPLES_PER_PIXEL, 1)
);
EXIFThumbnail.from(new EXIF(new byte[6 + 16 * 9]), new EXIFDirectory(new IFD(Collections.<Entry>emptyList()), new IFD(entries)), thumbnailReader);
}
@Test(expected = IIOException.class)
public void testFromTruncatedUncompressed() throws IOException {
List<TIFFEntry> entries = Arrays.asList(
new TIFFEntry(TIFF.TAG_COMPRESSION, 1),
new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, 0),
new TIFFEntry(TIFF.TAG_IMAGE_WIDTH, 160),
new TIFFEntry(TIFF.TAG_IMAGE_HEIGHT, 90)
);
EXIFThumbnail.from(new EXIF(new byte[42]), new EXIFDirectory(new IFD(Collections.<Entry>emptyList()), new IFD(entries)), thumbnailReader);
}
@Test
public void testValidUncompressed() throws IOException {
List<TIFFEntry> entries = Arrays.asList(
new TIFFEntry(TIFF.TAG_COMPRESSION, 1),
new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, 0),
new TIFFEntry(TIFF.TAG_IMAGE_WIDTH, 16),
new TIFFEntry(TIFF.TAG_IMAGE_HEIGHT, 9)
);
ThumbnailReader reader = EXIFThumbnail.from(new EXIF(new byte[6 + 16 * 9 * 3]), new EXIFDirectory(new IFD(Collections.<Entry>emptyList()), new IFD(entries)), thumbnailReader);
assertNotNull(reader);
// Sanity check below
assertEquals(16, reader.getWidth());
assertEquals(9, reader.getHeight());
assertNotNull(reader.read());
}
@Test(expected = IIOException.class)
public void testFromMissingOffsetJPEG() throws IOException {
List<TIFFEntry> entries = Collections.singletonList(new TIFFEntry(TIFF.TAG_COMPRESSION, 6));
EXIFThumbnail.from(new EXIF(new byte[42]), new EXIFDirectory(new IFD(Collections.<Entry>emptyList()), new IFD(entries)), thumbnailReader);
}
@Test(expected = IIOException.class)
public void testFromTruncatedJPEG() throws IOException {
List<TIFFEntry> entries = Arrays.asList(
new TIFFEntry(TIFF.TAG_COMPRESSION, 6),
new TIFFEntry(TIFF.TAG_JPEG_INTERCHANGE_FORMAT, 0)
);
EXIFThumbnail.from(new EXIF(new byte[42]), new EXIFDirectory(new IFD(Collections.<Entry>emptyList()), new IFD(entries)), thumbnailReader);
}
@Override
protected ThumbnailReader createReader(final ImageInputStream stream) throws IOException {
protected EXIFThumbnailReader createReader(final ThumbnailReadProgressListener progressListener, final int imageIndex, final int thumbnailIndex, final ImageInputStream stream) throws IOException {
List<JPEGSegment> segments = JPEGSegmentUtil.readSegments(stream, JPEG.APP1, "Exif");
stream.close();
assertNotNull(segments);
assertFalse(segments.isEmpty());
JPEGSegment exifSegment = segments.get(0);
InputStream data = exifSegment.segmentData();
byte[] exifData = new byte[exifSegment.segmentLength() - 2];
new DataInputStream(data).readFully(exifData);
TIFFReader reader = new TIFFReader();
InputStream data = segments.get(0).data();
if (data.read() < 0) {
throw new AssertionError("EOF!");
}
EXIF exif = new EXIF(exifData);
return EXIFThumbnail.from(exif, (CompoundDirectory) new TIFFReader().read(exif.exifData()), thumbnailReader);
ImageInputStream exifStream = ImageIO.createImageInputStream(data);
CompoundDirectory ifds = (CompoundDirectory) reader.read(exifStream);
assertEquals(2, ifds.directoryCount());
return new EXIFThumbnailReader(progressListener, ImageIO.getImageReadersByFormatName("JPEG").next(), imageIndex, thumbnailIndex, ifds.getDirectory(1), exifStream);
}
@Test
public void testReadJPEG() throws IOException {
ThumbnailReader reader = createReader(createStream("/jpeg/cmyk-sample-multiple-chunk-icc.jpg"));
ThumbnailReader reader = createReader(mock(ThumbnailReadProgressListener.class), 0, 0, createStream("/jpeg/cmyk-sample-multiple-chunk-icc.jpg"));
assertEquals(114, reader.getWidth());
assertEquals(160, reader.getHeight());
@@ -248,7 +94,7 @@ public class EXIFThumbnailReaderTest extends AbstractThumbnailReaderTest {
@Test
public void testReadRaw() throws IOException {
ThumbnailReader reader = createReader(createStream("/jpeg/exif-rgb-thumbnail-sony-d700.jpg"));
ThumbnailReader reader = createReader(mock(ThumbnailReadProgressListener.class), 0, 0, createStream("/jpeg/exif-rgb-thumbnail-sony-d700.jpg"));
assertEquals(80, reader.getWidth());
assertEquals(60, reader.getHeight());
@@ -259,9 +105,27 @@ public class EXIFThumbnailReaderTest extends AbstractThumbnailReaderTest {
assertEquals(60, thumbnail.getHeight());
}
private static class EXIFDirectory extends AbstractCompoundDirectory {
public EXIFDirectory(IFD... ifds) {
super(Arrays.asList(ifds));
}
@Test
public void testProgressListenerJPEG() throws IOException {
ThumbnailReadProgressListener listener = mock(ThumbnailReadProgressListener.class);
createReader(listener, 42, 43, createStream("/jpeg/cmyk-sample-multiple-chunk-icc.jpg")).read();
InOrder order = inOrder(listener);
order.verify(listener).thumbnailStarted(42, 43);
order.verify(listener, atLeastOnce()).thumbnailProgress(100f);
order.verify(listener).thumbnailComplete();
}
@Test
public void testProgressListenerRaw() throws IOException {
ThumbnailReadProgressListener listener = mock(ThumbnailReadProgressListener.class);
createReader(listener, 0, 99, createStream("/jpeg/exif-rgb-thumbnail-sony-d700.jpg")).read();
InOrder order = inOrder(listener);
order.verify(listener).thumbnailStarted(0, 99);
order.verify(listener, atLeastOnce()).thumbnailProgress(100f);
order.verify(listener).thumbnailComplete();
}
}
@@ -63,7 +63,7 @@ public class FastCMYKToRGBTest {
assertNotNull(pixel);
assertEquals(3, pixel.length);
byte[] expected = {(byte) 255, (byte) 255, (byte) 255};
assertArrayEquals(String.format("Was: %s, expected: %s", Arrays.toString(pixel), Arrays.toString(expected)), expected, pixel);
assertTrue(String.format("Was: %s, expected: %s", Arrays.toString(pixel), Arrays.toString(expected)), Arrays.equals(expected, pixel));
}
@Test
@@ -95,7 +95,7 @@ public class FastCMYKToRGBTest {
assertNotNull(pixel);
assertEquals(3, pixel.length);
byte[] expected = {(byte) 0, (byte) 0, (byte) 0};
assertArrayEquals(String.format("Was: %s, expected: %s", Arrays.toString(pixel), Arrays.toString(expected)), expected, pixel);
assertTrue(String.format("Was: %s, expected: %s", Arrays.toString(pixel), Arrays.toString(expected)), Arrays.equals(expected, pixel));
}
}
@@ -134,7 +134,7 @@ public class FastCMYKToRGBTest {
assertNotNull(pixel);
assertEquals(3, pixel.length);
byte[] expected = {(byte) (255 - i), (byte) i, (byte) (127 - i)};
assertArrayEquals(String.format("Was: %s, expected: %s", Arrays.toString(pixel), Arrays.toString(expected)), expected, pixel);
assertTrue(String.format("Was: %s, expected: %s", Arrays.toString(pixel), Arrays.toString(expected)), Arrays.equals(expected, pixel));
}
}
@@ -153,7 +153,7 @@ public class FastCMYKToRGBTest {
assertNotNull(pixel);
assertEquals(3, pixel.length);
byte[] expected = {(byte) (255 - i), (byte) i, (byte) (127 - i)};
assertArrayEquals(String.format("Was: %s, expected: %s", Arrays.toString(pixel), Arrays.toString(expected)), expected, pixel);
assertTrue(String.format("Was: %s, expected: %s", Arrays.toString(pixel), Arrays.toString(expected)), Arrays.equals(expected, pixel));
}
}
@@ -172,7 +172,7 @@ public class FastCMYKToRGBTest {
assertNotNull(pixel);
assertEquals(4, pixel.length);
byte[] expected = {(byte) (255 - i), (byte) i, (byte) (127 - i), (byte) 0xff};
assertArrayEquals(String.format("Was: %s, expected: %s", Arrays.toString(pixel), Arrays.toString(expected)), expected, pixel);
assertTrue(String.format("Was: %s, expected: %s", Arrays.toString(pixel), Arrays.toString(expected)), Arrays.equals(expected, pixel));
}
}
@@ -33,10 +33,9 @@ package com.twelvemonkeys.imageio.plugins.jpeg;
import com.twelvemonkeys.imageio.metadata.jpeg.JPEG;
import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegment;
import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegmentUtil;
import org.junit.Test;
import org.mockito.InOrder;
import javax.imageio.IIOException;
import javax.imageio.stream.ImageInputStream;
import java.awt.image.BufferedImage;
import java.io.DataInputStream;
@@ -44,6 +43,7 @@ import java.io.IOException;
import java.util.List;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
/**
* JFIFThumbnailReaderTest
@@ -53,9 +53,8 @@ import static org.junit.Assert.*;
* @version $Id: JFIFThumbnailReaderTest.java,v 1.0 04.05.12 15:56 haraldk Exp$
*/
public class JFIFThumbnailReaderTest extends AbstractThumbnailReaderTest {
@Override
protected ThumbnailReader createReader(ImageInputStream stream) throws IOException {
protected JFIFThumbnailReader createReader(ThumbnailReadProgressListener progressListener, int imageIndex, int thumbnailIndex, ImageInputStream stream) throws IOException {
List<JPEGSegment> segments = JPEGSegmentUtil.readSegments(stream, JPEG.APP0, "JFIF");
stream.close();
@@ -63,44 +62,12 @@ public class JFIFThumbnailReaderTest extends AbstractThumbnailReaderTest {
assertFalse(segments.isEmpty());
JPEGSegment segment = segments.get(0);
return JFIFThumbnail.from(JFIF.read(new DataInputStream(segment.segmentData()), segment.segmentLength()));
}
@Test
public void testFromNull() throws IOException {
assertNull(JFIFThumbnail.from(null));
}
@Test
public void testFromNullThumbnail() throws IOException {
assertNull(JFIFThumbnail.from(new JFIF(1, 1, 0, 1, 1, 0, 0, null)));
}
@Test
public void testFromEmpty() throws IOException {
assertNull(JFIFThumbnail.from(new JFIF(1, 1, 0, 1, 1, 0, 0, new byte[0])));
}
@Test(expected = IIOException.class)
public void testFromTruncated() throws IOException {
JFIFThumbnail.from(new JFIF(1, 1, 0, 1, 1, 255, 170, new byte[99]));
}
@Test
public void testFromValid() throws IOException {
ThumbnailReader reader = JFIFThumbnail.from(new JFIF(1, 1, 0, 1, 1, 30, 20, new byte[30 * 20 * 3]));
assertNotNull(reader);
// Sanity check below
assertEquals(30, reader.getWidth());
assertEquals(20, reader.getHeight());
assertNotNull(reader.read());
return new JFIFThumbnailReader(progressListener, imageIndex, thumbnailIndex, JFIF.read(new DataInputStream(segment.segmentData()), segment.segmentLength()));
}
@Test
public void testReadRaw() throws IOException {
ThumbnailReader reader = createReader(createStream("/jpeg/jfif-jfif-and-exif-thumbnail-sharpshot-iphone.jpg"));
ThumbnailReader reader = createReader(mock(ThumbnailReadProgressListener.class), 0, 0, createStream("/jpeg/jfif-jfif-and-exif-thumbnail-sharpshot-iphone.jpg"));
assertEquals(131, reader.getWidth());
assertEquals(122, reader.getHeight());
@@ -113,7 +80,7 @@ public class JFIFThumbnailReaderTest extends AbstractThumbnailReaderTest {
@Test
public void testReadNonSpecGray() throws IOException {
ThumbnailReader reader = createReader(createStream("/jpeg/jfif-grayscale-thumbnail.jpg"));
ThumbnailReader reader = createReader(mock(ThumbnailReadProgressListener.class), 0, 0, createStream("/jpeg/jfif-grayscale-thumbnail.jpg"));
assertEquals(127, reader.getWidth());
assertEquals(76, reader.getHeight());
@@ -124,4 +91,16 @@ public class JFIFThumbnailReaderTest extends AbstractThumbnailReaderTest {
assertEquals(127, thumbnail.getWidth());
assertEquals(76, thumbnail.getHeight());
}
@Test
public void testProgressListenerRaw() throws IOException {
ThumbnailReadProgressListener listener = mock(ThumbnailReadProgressListener.class);
createReader(listener, 0, 99, createStream("/jpeg/jfif-jfif-and-exif-thumbnail-sharpshot-iphone.jpg")).read();
InOrder order = inOrder(listener);
order.verify(listener).thumbnailStarted(0, 99);
order.verify(listener, atLeastOnce()).thumbnailProgress(100f);
order.verify(listener).thumbnailComplete();
}
}
@@ -33,13 +33,10 @@ package com.twelvemonkeys.imageio.plugins.jpeg;
import com.twelvemonkeys.imageio.metadata.jpeg.JPEG;
import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegment;
import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegmentUtil;
import org.junit.After;
import org.junit.Test;
import org.mockito.InOrder;
import javax.imageio.IIOException;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import java.awt.image.BufferedImage;
import java.io.DataInputStream;
@@ -47,6 +44,7 @@ import java.io.IOException;
import java.util.List;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
/**
* JFXXThumbnailReaderTest
@@ -56,10 +54,8 @@ import static org.junit.Assert.*;
* @version $Id: JFXXThumbnailReaderTest.java,v 1.0 04.05.12 15:56 haraldk Exp$
*/
public class JFXXThumbnailReaderTest extends AbstractThumbnailReaderTest {
private final ImageReader thumbnailReader = ImageIO.getImageReadersByFormatName("jpeg").next();
@Override
protected ThumbnailReader createReader(final ImageInputStream stream) throws IOException {
protected JFXXThumbnailReader createReader(ThumbnailReadProgressListener progressListener, int imageIndex, int thumbnailIndex, ImageInputStream stream) throws IOException {
List<JPEGSegment> segments = JPEGSegmentUtil.readSegments(stream, JPEG.APP0, "JFXX");
stream.close();
@@ -67,69 +63,12 @@ public class JFXXThumbnailReaderTest extends AbstractThumbnailReaderTest {
assertFalse(segments.isEmpty());
JPEGSegment jfxx = segments.get(0);
return JFXXThumbnail.from(JFXX.read(new DataInputStream(jfxx.segmentData()), jfxx.length()), thumbnailReader);
}
@After
public void tearDown() {
thumbnailReader.dispose();
}
@Test
public void testFromNull() throws IOException {
assertNull(JFXXThumbnail.from(null, thumbnailReader));
}
@Test(expected = IIOException.class)
public void testFromNullThumbnail() throws IOException {
JFXXThumbnail.from(new JFXX(JFXX.JPEG, null), thumbnailReader);
}
@Test(expected = IIOException.class)
public void testFromEmpty() throws IOException {
JFXXThumbnail.from(new JFXX(JFXX.JPEG, new byte[0]), thumbnailReader);
}
@Test(expected = IIOException.class)
public void testFromTruncatedJPEG() throws IOException {
JFXXThumbnail.from(new JFXX(JFXX.JPEG, new byte[99]), thumbnailReader);
}
@Test(expected = IIOException.class)
public void testFromTruncatedRGB() throws IOException {
byte[] thumbnail = new byte[765];
thumbnail[0] = (byte) 160;
thumbnail[1] = 90;
JFXXThumbnail.from(new JFXX(JFXX.RGB, thumbnail), thumbnailReader);
}
@Test(expected = IIOException.class)
public void testFromTruncatedIndexed() throws IOException {
byte[] thumbnail = new byte[365];
thumbnail[0] = (byte) 160;
thumbnail[1] = 90;
JFXXThumbnail.from(new JFXX(JFXX.INDEXED, thumbnail), thumbnailReader);
}
@Test
public void testFromValid() throws IOException {
byte[] thumbnail = new byte[14];
thumbnail[0] = 2;
thumbnail[1] = 2;
ThumbnailReader reader = JFXXThumbnail.from(new JFXX(JFXX.RGB, thumbnail), thumbnailReader);
assertNotNull(reader);
// Sanity check below
assertEquals(2, reader.getWidth());
assertEquals(2, reader.getHeight());
assertNotNull(reader.read());
return new JFXXThumbnailReader(progressListener, ImageIO.getImageReadersByFormatName("jpeg").next(), imageIndex, thumbnailIndex, JFXX.read(new DataInputStream(jfxx.segmentData()), jfxx.length()));
}
@Test
public void testReadJPEG() throws IOException {
ThumbnailReader reader = createReader(createStream("/jpeg/jfif-jfxx-thumbnail-olympus-d320l.jpg"));
ThumbnailReader reader = createReader(mock(ThumbnailReadProgressListener.class), 0, 0, createStream("/jpeg/jfif-jfxx-thumbnail-olympus-d320l.jpg"));
assertEquals(80, reader.getWidth());
assertEquals(60, reader.getHeight());
@@ -142,4 +81,16 @@ public class JFXXThumbnailReaderTest extends AbstractThumbnailReaderTest {
// TODO: Test JFXX indexed thumbnail
// TODO: Test JFXX RGB thumbnail
@Test
public void testProgressListenerRaw() throws IOException {
ThumbnailReadProgressListener listener = mock(ThumbnailReadProgressListener.class);
createReader(listener, 0, 99, createStream("/jpeg/jfif-jfxx-thumbnail-olympus-d320l.jpg")).read();
InOrder order = inOrder(listener);
order.verify(listener).thumbnailStarted(0, 99);
order.verify(listener, atLeastOnce()).thumbnailProgress(100f);
order.verify(listener).thumbnailComplete();
}
}
@@ -31,7 +31,6 @@
package com.twelvemonkeys.imageio.plugins.jpeg;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
import com.twelvemonkeys.lang.StringUtil;
import org.hamcrest.core.IsInstanceOf;
@@ -56,7 +55,6 @@ import java.awt.*;
import java.awt.color.ColorSpace;
import java.awt.color.ICC_Profile;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.io.*;
import java.util.List;
@@ -142,6 +140,11 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
// More test data in specific tests below
}
@Override
protected boolean allowsNullRawImageType() {
return true;
}
@Override
protected List<String> getFormatNames() {
return Arrays.asList("JPEG", "jpeg", "JPG", "jpg",
@@ -419,8 +422,8 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
}
@Test
public void testYCbCrNotSubsampledNonstandardComponentIds() throws IOException {
// Regression: Make sure 3 channel, non-subsampled JFIF, defaults to YCbCr, even if nonstandard component ids
public void testYCbCrNotSubsampledNonstandardChannelIndexes() throws IOException {
// Regression: Make sure 3 channel, non-subsampled JFIF, defaults to YCbCr, even if unstandard channel indexes
JPEGImageReader reader = createReader();
try (ImageInputStream stream = ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/jfif-ycbcr-no-subsampling-intel.jpg"))) {
reader.setInput(stream);
@@ -1232,56 +1235,6 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
}
}
@Test
public void testRGBANoGrayImageTypes() throws IOException {
JPEGImageReader reader = createReader();
reader.setInput(ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/adobe-unknown-rgb-ids.jpg")));
Iterator<ImageTypeSpecifier> imageTypes = reader.getImageTypes(0);
while (imageTypes.hasNext()) {
ImageTypeSpecifier specifier = imageTypes.next();
assertNotEquals("RGB JPEGs can't be decoded as Gray as it has no luminance (Y) component", ColorSpace.TYPE_GRAY, specifier.getColorModel().getColorSpace().getType());
}
reader.dispose();
}
@Test(expected = Exception.class)
public void testRGBAsGray() throws IOException {
final JPEGImageReader reader = createReader();
try {
reader.setInput(ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/adobe-unknown-rgb-ids.jpg")));
assertEquals(225, reader.getWidth(0));
assertEquals(156, reader.getHeight(0));
final ImageReadParam param = reader.getDefaultReadParam();
param.setSourceRegion(new Rectangle(0, 0, 225, 8));
param.setDestinationType(ImageTypeSpecifiers.createGrayscale(8, DataBuffer.TYPE_BYTE));
// Should ideally throw IIOException due to destination type mismatch, but throws IllegalArgumentException...
reader.read(0, param);
}
finally {
reader.dispose();
}
}
@Test
public void testYCbCrAsGray() throws IOException {
JPEGImageReader reader = createReader();
reader.setInput(ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/jfif-ycbcr-no-subsampling-intel.jpg")));
ImageReadParam param = reader.getDefaultReadParam();
param.setDestinationType(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY));
BufferedImage image = reader.read(0, param);
assertNotNull(image);
assertEquals(BufferedImage.TYPE_BYTE_GRAY, image.getType());
}
/**
* Slightly fuzzy RGB equals method. Tolerance +/-5 steps.
*/
@@ -1464,7 +1417,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
assertNotNull(unknown.getUserObject()); // All unknowns must have user object (data array)
}
}
catch (IOException e) {
catch (IIOException e) {
e.printStackTrace();
fail(String.format("Reading metadata failed for %s image %s: %s", testData, i, e.getMessage()));
}
@@ -1865,7 +1818,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
reader.setInput(ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/exif-jfif-app13-app14ycck-3channel.jpg")));
ImageTypeSpecifier rawType = reader.getRawImageType(0);
assertNotNull(rawType); // As of Java 9, use RGB for YCC and CMYK for YCCK
assertNull(rawType); // But no exception, please...
}
finally {
reader.dispose();
@@ -30,16 +30,26 @@
package com.twelvemonkeys.imageio.plugins.jpeg;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
import com.twelvemonkeys.imageio.util.IIOUtil;
import com.twelvemonkeys.imageio.util.ImageWriterAbstractTest;
import org.junit.Test;
import org.w3c.dom.NodeList;
import javax.imageio.*;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataFormatImpl;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.spi.IIORegistry;
import javax.imageio.spi.ImageWriterSpi;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import javax.imageio.stream.MemoryCacheImageOutputStream;
import java.awt.*;
import java.awt.color.ColorSpace;
import java.awt.color.ICC_Profile;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.RenderedImage;
@@ -51,29 +61,10 @@ import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataFormatImpl;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.spi.IIORegistry;
import javax.imageio.spi.ImageWriterSpi;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import javax.imageio.stream.MemoryCacheImageOutputStream;
import org.junit.Test;
import org.w3c.dom.NodeList;
import com.twelvemonkeys.imageio.color.ColorSpaces;
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
import com.twelvemonkeys.imageio.util.IIOUtil;
import com.twelvemonkeys.imageio.util.ImageWriterAbstractTest;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* JPEGImageWriterTest
@@ -94,16 +85,13 @@ public class JPEGImageWriterTest extends ImageWriterAbstractTest<JPEGImageWriter
@Override
protected List<? extends RenderedImage> getTestData() {
ColorModel cmyk = new ComponentColorModel(ColorSpaces.getColorSpace(ColorSpaces.CS_GENERIC_CMYK), false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
return Arrays.asList(
new BufferedImage(320, 200, BufferedImage.TYPE_3BYTE_BGR),
new BufferedImage(32, 20, BufferedImage.TYPE_INT_RGB),
new BufferedImage(32, 20, BufferedImage.TYPE_INT_BGR),
// Java 11+ no longer supports RGBA JPEG
// new BufferedImage(32, 20, BufferedImage.TYPE_INT_ARGB),
// new BufferedImage(32, 20, BufferedImage.TYPE_4BYTE_ABGR),
new BufferedImage(32, 20, BufferedImage.TYPE_BYTE_GRAY),
new BufferedImage(cmyk, cmyk.createCompatibleWritableRaster(32, 20), cmyk.isAlphaPremultiplied(), null)
new BufferedImage(32, 20, BufferedImage.TYPE_INT_ARGB),
new BufferedImage(32, 20, BufferedImage.TYPE_4BYTE_ABGR),
new BufferedImage(32, 20, BufferedImage.TYPE_BYTE_GRAY)
);
}
@@ -187,7 +187,7 @@ public class JPEGSegmentImageInputStreamTest {
assertEquals(2, iis.read(buffer, 0, buffer.length));
assertEquals(2, iis.getStreamPosition());
iis.seek(2000); // Just a random position beyond EOF
iis.seek(2000); // Just a random postion beyond EOF
assertEquals(2000, iis.getStreamPosition());
// So far, so good (but stream position is now really beyond EOF)...
@@ -1,102 +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.plugins.jpeg;
import org.junit.Test;
import java.awt.image.DataBuffer;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.util.Arrays;
import static org.junit.Assert.*;
public class LuminanceToGrayTest {
@Test
public void testConvertByteYcc() {
LuminanceToGray convert = new LuminanceToGray();
WritableRaster input = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, 1, 1, 3, null);
WritableRaster result = null;
byte[] pixel = null;
for (int i = 0; i < 255; i++) {
input.setDataElements(0, 0, new byte[] {(byte) i, (byte) (255 - i), (byte) (127 + i)});
result = convert.filter(input, result);
pixel = (byte[]) result.getDataElements(0, 0, pixel);
assertNotNull(pixel);
assertEquals(1, pixel.length);
byte[] expected = {(byte) i};
assertArrayEquals(String.format("Was: %s, expected: %s", Arrays.toString(pixel), Arrays.toString(expected)), expected, pixel);
}
}
@Test
public void testConvertByteYccK() {
LuminanceToGray convert = new LuminanceToGray();
WritableRaster input = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, 1, 1, 4, null);
WritableRaster result = null;
byte[] pixel = null;
for (int i = 0; i < 255; i++) {
input.setDataElements(0, 0, new byte[] {(byte) i, (byte) (255 - i), (byte) (127 + i), (byte) 255});
result = convert.filter(input, result);
pixel = (byte[]) result.getDataElements(0, 0, pixel);
assertNotNull(pixel);
assertEquals(1, pixel.length);
byte[] expected = {(byte) i};
assertArrayEquals(String.format("Was: %s, expected: %s", Arrays.toString(pixel), Arrays.toString(expected)), expected, pixel);
}
}
@Test
public void testConvertByteYccA() {
LuminanceToGray convert = new LuminanceToGray();
WritableRaster input = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, 1, 1, 4, null);
WritableRaster result = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, 1, 1, 2, null);
byte[] pixel = null;
for (int i = 0; i < 255; i++) {
input.setDataElements(0, 0, new byte[] {(byte) i, (byte) 255, (byte) (127 + i), (byte) (255 - i)});
result = convert.filter(input, result);
pixel = (byte[]) result.getDataElements(0, 0, pixel);
assertNotNull(pixel);
assertEquals(2, pixel.length);
byte[] expected = {(byte) i, (byte) (255 - i)};
assertArrayEquals(String.format("Was: %s, expected: %s", Arrays.toString(pixel), Arrays.toString(expected)), expected, pixel);
}
}
}

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