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
151 changed files with 2813 additions and 1755 deletions
+249 -403
View File
@@ -1,15 +1,13 @@
## Latest [![Build Status](https://travis-ci.org/haraldk/TwelveMonkeys.svg?branch=master)](https://travis-ci.org/haraldk/TwelveMonkeys)
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.twelvemonkeys.imageio/imageio/badge.svg?color=slateblue)](https://maven-badges.herokuapp.com/maven-central/com.twelvemonkeys.imageio/imageio)
Master branch build status: [![Build Status](https://travis-ci.org/haraldk/TwelveMonkeys.svg?branch=master)](https://travis-ci.org/haraldk/TwelveMonkeys) [![StackOverflow](https://img.shields.io/badge/stack_overflow-twelvemonkeys-orange.svg)](https://stackoverflow.com/questions/tagged/twelvemonkeys)
[![Donate](https://img.shields.io/badge/donate-PayPal-blue.svg)](https://paypal.me/haraldk76/100)
Latest release is TwelveMonkeys ImageIO [3.5](https://search.maven.org/search?q=g:com.twelvemonkeys.imageio%20AND%20v:3.5) (Jan. 22nd, 2020).
[Release notes](https://github.com/haraldk/TwelveMonkeys/releases/latest).
## About ## About
TwelveMonkeys ImageIO is a collection of plugins and extensions for Java's ImageIO. TwelveMonkeys ImageIO is a collection of plugins and extensions for Java's ImageIO.
These plugins extends the number of image file formats supported in Java, using the javax.imageio.* package. These plugins extend the number of image file formats supported in Java, using the `javax.imageio.*` package.
The main purpose of this project is to provide support for formats not covered by the JRE itself. The main purpose of this project is to provide support for formats not covered by the JRE itself.
Support for formats is important, both to be able to read data found Support for formats is important, both to be able to read data found
@@ -19,246 +17,63 @@ The goal is to create a set of efficient and robust ImageIO plug-ins, that can b
---- ----
## Features ## File formats supported
Mainstream format support | Plugin | Format | Description | Read | Write | Metadata | Notes |
| ------ | -------- | ----------- |:----:|:-----:| -------- | ----- |
| 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 & 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 |
| [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 |
| [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 & 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 |
#### BMP - MS Windows/IBM OS/2 Device Independent Bitmap
* Read support for all known versions of the DIB/BMP format
* Indexed color, 1, 4 and 8 bit, including 4 and 8 bit RLE
* RGB, 16, 24 and 32 bit
* Embedded PNG and JPEG data
* Windows and OS/2 versions
* Native and standard metadata format
#### JPEG **Important note on using Batik:** *Please read [The Apache™ XML Graphics Project - Security](http://xmlgraphics.apache.org/security.html),
and make sure you use version 1.14 or later.*
* Read support for the following JPEG "flavors":
* All JFIF compliant JPEGs
* All Exif compliant JPEGs
* YCbCr JPEGs without JFIF segment (converted to RGB, using embedded ICC profile)
* CMYK JPEGs (converted to RGB by default or as CMYK, using embedded ICC profile)
* Adobe YCCK JPEGs (converted to RGB by default or as CMYK, using embedded ICC profile)
* JPEGs containing ICC profiles with interpretation other than 'Perceptual' or class other than 'Display'
* JPEGs containing ICC profiles that are incompatible with stream data, corrupted ICC profiles or corrupted `ICC_PROFILE` segments
* JPEGs using non-standard color spaces, unsupported by Java 2D
* JPEGs with APP14/Adobe segments with length other than 14 bytes
* 8 bit JPEGs with 16 bit DQT segments
* Issues warnings instead of throwing exceptions in cases of corrupted or non-conformant data where ever the image
data can still be read in a reasonable way
* Thumbnail support:
* JFIF thumbnails (even if stream contains "inconsistent metadata")
* JFXX thumbnails (JPEG, Indexed and RGB)
* EXIF thumbnails (JPEG, RGB and YCbCr)
* Metadata support:
* JPEG metadata in both standard and native formats (even if stream contains "inconsistent metadata")
* `javax_imageio_jpeg_image_1.0` format (currently as native format, may change in the future)
* Non-conforming combinations of JFIF, Exif and Adobe markers, using "unknown" segments in the
"MarkerSequence" tag for the unsupported segments (for `javax_imageio_jpeg_image_1.0` format)
* Extended write support:
* CMYK JPEGs
* YCCK JPEGs in progress
#### JPEG-2000
* Possibly coming in the future, pending some license issues.
If you are one of the authors, or know one of the authors and/or the current license holders of either the original
jj2000 package or the JAI ImageIO project, please contact me (I've tried to get in touch in various ways,
without success so far).
Alternatively, if you have or know of a JPEG-2000 implementation in Java with a suitable license, get in touch. :-)
#### PNM - NetPBM Portable Any Map
* Read support for the following file types:
* PBM in 'P1' (ASCII) and 'P4' (binary) formats, 1 bit per pixel
* PGM in 'P2' (ASCII) and 'P5' (binary) formats, up to 16/32 bits per pixel
* PPM in 'P3' (ASCII) and 'P6' (binary) formats, up to 16/32 bits per pixel component
* PAM in 'P7' (binary) format up to 32 bits per pixel component
* Limited support for PFM in 'Pf' (gray) and 'PF' (RGB) formats, 32 bits floating point
* Write support for the following formats:
* PPM in 'P6' (binary) format
* PAM in 'P7' (binary) format
* Standard metadata support
#### PSD - Adobe Photoshop Document
* Read support for the following file types:
* Monochrome, 1 channel, 1 bit
* Indexed, 1 channel, 8 bit
* Gray, 1 channel, 8, 16 and 32 bit
* Duotone, 1 channel, 8, 16 and 32 bit
* RGB, 3-4 channels, 8, 16 and 32 bit
* CMYK, 4-5 channels, 8, 16 and 32 bit
* Read support for the following compression types:
* Uncompressed
* RLE (PackBits)
* Layer support
* Image layers only, in all of the above types
* Thumbnail support
* JPEG
* RAW (RGB)
* Support for "Large Document Format" (PSB)
* Native and Standard metadata support
#### TIFF - Aldus/Adobe Tagged Image File Format
* Read support for the following "Baseline" TIFF file types:
* Class B (Bi-level), all relevant compression types, 1 bit per sample
* Class G (Gray), all relevant compression types, 2, 4, 8, 16 or 32 bits per sample, unsigned integer
* Class P (Palette/indexed color), all relevant compression types, 1, 2, 4, 8 or 16 bits per sample, unsigned integer
* Class R (RGB), all relevant compression types, 8 or 16 bits per sample, unsigned integer
* Read support for the following TIFF extensions:
* Tiling
* Class F (Facsimile), CCITT Modified Huffman RLE, T4 and T6 (type 2, 3 and 4) compressions.
* LZW Compression (type 5)
* "Old-style" JPEG Compression (type 6), as a best effort, as the spec is not well-defined
* JPEG Compression (type 7)
* ZLib (aka Adobe-style Deflate) Compression (type 8)
* Deflate Compression (type 32946)
* Horizontal differencing Predictor (type 2) for LZW, ZLib, Deflate and PackBits compression
* Alpha channel (ExtraSamples type 1/Associated Alpha and type 2/Unassociated Alpha)
* CMYK data (PhotometricInterpretation type 5/Separated)
* YCbCr data (PhotometricInterpretation type 6/YCbCr) for JPEG
* CIELab data in TIFF, ITU and ICC variants (PhotometricInterpretation type 9, 10 and 11)
* Planar data (PlanarConfiguration type 2/Planar)
* ICC profiles (ICCProfile)
* BitsPerSample values up to 16 for most PhotometricInterpretations
* Multiple images (pages) in one file
* Write support for most "Baseline" TIFF options
* Uncompressed, PackBits, ZLib and Deflate
* Additional support for CCITT T4 and and T6 compressions.
* Additional support for LZW and JPEG (type 7) compressions
* Horizontal differencing Predictor (type 2) for LZW, ZLib, Deflate
* Native and Standard metadata support
Legacy formats
#### HDR - Radiance High Dynamic Range RGBE Format
* Read support for the most common RGBE (.hdr) format
* Samples are converted to 32 bit floating point (`float`) and normalized using a global tone mapper by default.
* Support for custom global tone mappers
* Alternatively, use a "null-tone mapper", for unnormalized data (allows local tone mapping)
* Unconverted RGBE samples accessible using `readRaster`
* Standard metadata support
#### IFF - Commodore Amiga/Electronic Arts Interchange File Format
* Legacy format, allows reading popular image format from the Commodore Amiga computer.
* Read support for the following file types:
* ILBM Indexed color, 1-8 interleaved bit planes, including 6 bit EHB
* ILBM Gray, 8 bit interleaved bit planes
* ILBM RGB, 24 and 32 bit interleaved bit planes
* ILBM HAM6 and HAM8
* PBM Indexed color, 1-8 bit,
* PBM Gray, 8 bit
* PBM RGB, 24 and 32 bit
* PBM HAM6 and HAM8
* Write support
* ILBM Indexed color, 1-8 bits per sample, 8 bit gray, 24 and 32 bit true color.
* Support for the following compression types (read/write):
* Uncompressed
* RLE (PackBits)
#### PCX - ZSoft Paintbrush Format
* Read support for the following file types:
* Indexed color, 1, 2, 4 or 8 bits per pixel, bit planes or interleaved
* Grayscale, 8 bits per pixel
* Color (RGB), 8 bits per pixel component
* Read support for DCX (multi-page) fax format, containing any of the above types
* Support for the following compression types:
* Uncompressed (experimental)
* RLE compressed
* Standard metadata support
#### PICT - Apple Mac Paint Picture Format
* Legacy format, especially useful for reading OS X clipboard data.
* Read support for the following file types:
* QuickDraw (format support is not complete, but supports most OS X clipboard data as well as RGB pixel data)
* QuickDraw bitmap
* QuickDraw pixmap
* QuickTime stills
* Write support for RGB pixel data:
* QuickDraw pixmap
#### SGI - Silicon Graphics Image Format
* Read support for the following file types:
* 1, 2, 3 or 4 channel image data
* 8 or 16 bits per pixel component
* Support for the following compression types:
* Uncompressed
* RLE compressed
* Standard metadata support
#### TGA - Truevision TGA Image Format
* Read support for the following file types:
* ColorMapped
* Monochrome
* TrueColor
* Support for the following compression types:
* Uncompressed
* RLE compressed
* Standard metadata support
* Write support
Icon/other formats
#### ICNS - Apple Icon Image
* Read support for the following icon types:
* All known "native" icon types
* Large PNG encoded icons
* Large JPEG 2000 encoded icons (requires JPEG 2000 ImageIO plugin or fallback to `sips` command line tool)
* Write support for PNG encoded icons
#### ICO & CUR - MS Windows Icon and Cursor Formats
* Read support for the following file types:
* ICO Indexed color, 1, 4 and 8 bit
* ICO RGB, 16, 24 and 32 bit
* CUR Indexed color, 1, 4 and 8 bit
* CUR RGB, 16, 24 and 32 bit
* Write support
* *3.1* Note: These formats are now part of the BMP plugin
#### Thumbs.db - MS Windows Thumbs DB
* Read support
Other formats, using 3rd party libraries
#### SVG - Scalable Vector Graphics
* Read-only support using Batik
#### WMF - MS Windows MetaFile
* Limited read-only support using Batik
**Important note on using Batik:** *Please read [The Apache™ XML Graphics Project - Security](http://xmlgraphics.apache.org/security.html), and make sure you use
either version 1.6.1, 1.7.1 or 1.8+.*
Note that GIF, PNG and WBMP formats are already supported through the ImageIO API, using the
[JDK standard plugins](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/package-summary.html).
For BMP, JPEG, and TIFF formats the TwelveMonkeys plugins provides extended format support and additional features.
## Basic usage ## Basic usage
Most of the time, all you need to do is simply include the plugins in your project and write: Most of the time, all you need to do is simply include the plugins in your project and write:
BufferedImage image = ImageIO.read(file); ```java
BufferedImage image = ImageIO.read(file);
```
This will load the first image of the file, entirely into memory. This will load the first image of the file, entirely into memory.
The basic and simplest form of writing is: The basic and simplest form of writing is:
if (!ImageIO.write(image, format, file)) { ```java
// Handle image not written case if (!ImageIO.write(image, format, file)) {
} // Handle image not written case
}
```
This will write the entire image into a single file, using the default settings for the given format. This will write the entire image into a single file, using the default settings for the given format.
@@ -269,50 +84,44 @@ The plugins are discovered automatically at run time. See the [FAQ](#faq) for mo
If you need more control of read parameters and the reading process, the common idiom for reading is something like: If you need more control of read parameters and the reading process, the common idiom for reading is something like:
```java ```java
// Create input stream // Create input stream (in try-with-resource block to avoid leaks)
ImageInputStream input = ImageIO.createImageInputStream(file); try (ImageInputStream input = ImageIO.createImageInputStream(file)) {
// Get the reader
Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
if (!readers.hasNext()) {
throw new IllegalArgumentException("No reader for: " + file);
}
ImageReader reader = readers.next();
try { try {
// Get the reader reader.setInput(input);
Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
if (!readers.hasNext()) { // Optionally, listen for read warnings, progress, etc.
throw new IllegalArgumentException("No reader for: " + file); reader.addIIOReadWarningListener(...);
} reader.addIIOReadProgressListener(...);
ImageReader reader = readers.next(); ImageReadParam param = reader.getDefaultReadParam();
try { // Optionally, control read settings like sub sampling, source region or destination etc.
reader.setInput(input); param.setSourceSubsampling(...);
param.setSourceRegion(...);
param.setDestination(...);
// ...
// Optionally, listen for read warnings, progress, etc. // Finally read the image, using settings from param
reader.addIIOReadWarningListener(...); BufferedImage image = reader.read(0, param);
reader.addIIOReadProgressListener(...);
ImageReadParam param = reader.getDefaultReadParam(); // Optionally, read thumbnails, meta data, etc...
int numThumbs = reader.getNumThumbnails(0);
// Optionally, control read settings like sub sampling, source region or destination etc. // ...
param.setSourceSubsampling(...);
param.setSourceRegion(...);
param.setDestination(...);
// ...
// Finally read the image, using settings from param
BufferedImage image = reader.read(0, param);
// Optionally, read thumbnails, meta data, etc...
int numThumbs = reader.getNumThumbnails(0);
// ...
}
finally {
// Dispose reader in finally block to avoid memory leaks
reader.dispose();
}
} }
finally { finally {
// Close stream in finally block to avoid resource leaks // Dispose reader in finally block to avoid memory leaks
input.close(); reader.dispose();
} }
}
``` ```
Query the reader for source image dimensions using `reader.getWidth(n)` and `reader.getHeight(n)` without reading the Query the reader for source image dimensions using `reader.getWidth(n)` and `reader.getHeight(n)` without reading the
@@ -324,86 +133,56 @@ It's also possible to read multiple images from the same file in a loop, using `
If you need more control of write parameters and the writing process, the common idiom for writing is something like: If you need more control of write parameters and the writing process, the common idiom for writing is something like:
```java ```java
// Get the writer // Get the writer
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(format); Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(format);
if (!writers.hasNext()) { if (!writers.hasNext()) {
throw new IllegalArgumentException("No writer for: " + format); throw new IllegalArgumentException("No writer for: " + format);
} }
ImageWriter writer = writers.next(); ImageWriter writer = writers.next();
try { try {
// Create output stream // Create output stream (in try-with-resource block to avoid leaks)
ImageOutputStream output = ImageIO.createImageOutputStream(file); try (ImageOutputStream output = ImageIO.createImageOutputStream(file)) {
writer.setOutput(output);
try {
writer.setOutput(output); // Optionally, listen to progress, warnings, etc.
// Optionally, listen to progress, warnings, etc. ImageWriteParam param = writer.getDefaultWriteParam();
ImageWriteParam param = writer.getDefaultWriteParam(); // Optionally, control format specific settings of param (requires casting), or
// control generic write settings like sub sampling, source region, output type etc.
// Optionally, control format specific settings of param (requires casting), or
// control generic write settings like sub sampling, source region, output type etc. // Optionally, provide thumbnails and image/stream metadata
writer.write(..., new IIOImage(..., image, ...), param);
// Optionally, provide thumbnails and image/stream metadata
writer.write(..., new IIOImage(..., image, ...), param);
}
finally {
// Close stream in finally block to avoid resource leaks
output.close();
}
}
finally {
// Dispose writer in finally block to avoid memory leaks
writer.dispose();
} }
}
finally {
// Dispose writer in finally block to avoid memory leaks
writer.dispose();
}
``` ```
For more advanced usage, and information on how to use the ImageIO API, I suggest you read the For more advanced usage, and information on how to use the ImageIO API, I suggest you read the
[Java Image I/O API Guide](http://docs.oracle.com/javase/7/docs/technotes/guides/imageio/spec/imageio_guideTOC.fm.html) [Java Image I/O API Guide](http://docs.oracle.com/javase/7/docs/technotes/guides/imageio/spec/imageio_guideTOC.fm.html)
from Oracle. from Oracle.
#### Adobe Clipping Path support
#### Deploying the plugins in a web app ```java
import com.twelvemonkeys.imageio.path.Paths;
Because the `ImageIO` plugin registry (the `IIORegistry`) is "VM global", it doesn't by default work well with ...
servlet contexts. This is especially evident if you load plugins from the `WEB-INF/lib` or `classes` folder.
Unless you add `ImageIO.scanForPlugins()` somewhere in your code, the plugins might never be available at all.
In addition, servlet contexts dynamically loads and unloads classes (using a new class loader per context). try (ImageInputStream stream = ImageIO.createImageInputStream(new File("image_with_path.jpg")) {
If you restart your application, old classes will by default remain in memory forever (because the next time BufferedImage image = Paths.readClipped(stream);
`scanForPlugins` is called, it's another `ClassLoader` that scans/loads classes, and thus they will be new instances
in the registry). If a read is attempted using one of the remaining "old" readers, weird exceptions
(like `NullPointerException`s when accessing `static final` initialized fields or `NoClassDefFoundError`s
for uninitialized inner classes) may occur.
To work around both the discovery problem and the resource leak, // Do something with the clipped image...
it is *strongly recommended* to use the `IIOProviderContextListener` that implements }
dynamic loading and unloading of ImageIO plugins for web applications.
```xml
<web-app ...>
...
<listener>
<display-name>ImageIO service provider loader/unloader</display-name>
<listener-class>com.twelvemonkeys.servlet.image.IIOProviderContextListener</listener-class>
</listener>
...
</web-app>
``` ```
See [Adobe Clipping Path support on the Wiki](https://github.com/haraldk/TwelveMonkeys/wiki/Photoshop-Clipping-Path-support) for more details and example code.
Loading plugins from `WEB-INF/lib` without the context listener installed is unsupported and will not work correctly.
The context listener has no dependencies to the TwelveMonkeys ImageIO plugins, and may be used with JAI ImageIO
or other ImageIO plugins as well.
Another safe option, is to place the JAR files in the application server's shared or common lib folder.
#### Using the ResampleOp #### Using the ResampleOp
@@ -411,15 +190,15 @@ The library comes with a resampling (image resizing) operation, that contains ma
to provide excellent results at reasonable speed. to provide excellent results at reasonable speed.
```java ```java
import com.twelvemonkeys.image.ResampleOp; import com.twelvemonkeys.image.ResampleOp;
... ...
BufferedImage input = ...; // Image to resample BufferedImage input = ...; // Image to resample
int width, height = ...; // new width/height int width, height = ...; // new width/height
BufferedImageOp resampler = new ResampleOp(width, height, ResampleOp.FILTER_LANCZOS); // A good default filter, see class documentation for more info BufferedImageOp resampler = new ResampleOp(width, height, ResampleOp.FILTER_LANCZOS); // A good default filter, see class documentation for more info
BufferedImage output = resampler.filter(input, null); BufferedImage output = resampler.filter(input, null);
``` ```
#### Using the DiffusionDither #### Using the DiffusionDither
@@ -428,14 +207,14 @@ The library comes with a dithering operation, that can be used to convert `Buffe
Floyd-Steinberg error-diffusion dither. Floyd-Steinberg error-diffusion dither.
```java ```java
import com.twelvemonkeys.image.DiffusionDither; import com.twelvemonkeys.image.DiffusionDither;
... ...
BufferedImage input = ...; // Image to dither BufferedImage input = ...; // Image to dither
BufferedImageOp ditherer = new DiffusionDither(); BufferedImageOp ditherer = new DiffusionDither();
BufferedImage output = ditherer.filter(input, null); BufferedImage output = ditherer.filter(input, null);
``` ```
## Building ## Building
@@ -451,7 +230,7 @@ Build the project (using [Maven](http://maven.apache.org/download.cgi)):
$ mvn package $ mvn package
Currently, the recommended JDK for making a build is Oracle JDK 7.x or 8.x. Currently, the recommended JDK for making a build is Oracle JDK 8.x.
It's possible to build using OpenJDK, but some tests might fail due to some minor differences between the color management systems used. You will need to either disable the tests in question, or build without tests altogether. It's possible to build using OpenJDK, but some tests might fail due to some minor differences between the color management systems used. You will need to either disable the tests in question, or build without tests altogether.
@@ -473,10 +252,10 @@ The ImageIO registry and service lookup mechanism will make sure the plugins are
To verify that the JPEG plugin is installed and used at run-time, you could use the following code: To verify that the JPEG plugin is installed and used at run-time, you could use the following code:
```java ```java
Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName("JPEG"); Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName("JPEG");
while (readers.hasNext()) { while (readers.hasNext()) {
System.out.println("reader: " + readers.next()); System.out.println("reader: " + readers.next());
} }
``` ```
The first line should print: The first line should print:
@@ -488,82 +267,144 @@ The first line should print:
To depend on the JPEG and TIFF plugin using Maven, add the following to your POM: To depend on the JPEG and TIFF plugin using Maven, add the following to your POM:
```xml ```xml
...
<dependencies>
... ...
<dependencies> <dependency>
... <groupId>com.twelvemonkeys.imageio</groupId>
<dependency> <artifactId>imageio-jpeg</artifactId>
<groupId>com.twelvemonkeys.imageio</groupId> <version>3.6.3</version>
<artifactId>imageio-jpeg</artifactId> </dependency>
<version>3.5</version> <dependency>
</dependency> <groupId>com.twelvemonkeys.imageio</groupId>
<dependency> <artifactId>imageio-tiff</artifactId>
<groupId>com.twelvemonkeys.imageio</groupId> <version>3.6.3</version>
<artifactId>imageio-tiff</artifactId> </dependency>
<version>3.5</version>
</dependency>
<!-- <!--
Optional dependency. Needed only if you deploy `ImageIO` plugins as part of a web app. Optional dependency. Needed only if you deploy ImageIO plugins as part of a web app.
Make sure you add the `IIOProviderContextListener` to your `web.xml`, see above. Make sure you add the IIOProviderContextListener to your web.xml, see above.
--> -->
<dependency> <dependency>
<groupId>com.twelvemonkeys.servlet</groupId> <groupId>com.twelvemonkeys.servlet</groupId>
<artifactId>servlet</artifactId> <artifactId>servlet</artifactId>
<version>3.5</version> <version>3.6.3</version>
</dependency> </dependency>
</dependencies> </dependencies>
``` ```
#### Manual dependency example #### Manual dependency example
To depend on the JPEG and TIFF plugin in your IDE or program, add all of the following JARs to your class path: To depend on the JPEG and TIFF plugin in your IDE or program, add all of the following JARs to your class path:
twelvemonkeys-common-lang-3.5.jar twelvemonkeys-common-lang-3.6.3.jar
twelvemonkeys-common-io-3.5.jar twelvemonkeys-common-io-3.6.3.jar
twelvemonkeys-common-image-3.5.jar twelvemonkeys-common-image-3.6.3.jar
twelvemonkeys-imageio-core-3.5.jar twelvemonkeys-imageio-core-3.6.3.jar
twelvemonkeys-imageio-metadata-3.5.jar twelvemonkeys-imageio-metadata-3.6.3.jar
twelvemonkeys-imageio-jpeg-3.5.jar twelvemonkeys-imageio-jpeg-3.6.3.jar
twelvemonkeys-imageio-tiff-3.5.jar twelvemonkeys-imageio-tiff-3.6.3.jar
#### Deploying the plugins in a web app
Because the `ImageIO` plugin registry (the `IIORegistry`) is "VM global", it doesn't by default work well with
servlet contexts. This is especially evident if you load plugins from the `WEB-INF/lib` or `classes` folder.
Unless you add `ImageIO.scanForPlugins()` somewhere in your code, the plugins might never be available at all.
In addition, servlet contexts dynamically loads and unloads classes (using a new class loader per context).
If you restart your application, old classes will by default remain in memory forever (because the next time
`scanForPlugins` is called, it's another `ClassLoader` that scans/loads classes, and thus they will be new instances
in the registry). If a read is attempted using one of the remaining "old" readers, weird exceptions
(like `NullPointerException`s when accessing `static final` initialized fields or `NoClassDefFoundError`s
for uninitialized inner classes) may occur.
To work around both the discovery problem and the resource leak,
it is *strongly recommended* to use the `IIOProviderContextListener` that implements
dynamic loading and unloading of ImageIO plugins for web applications.
```xml
<web-app ...>
...
<listener>
<display-name>ImageIO service provider loader/unloader</display-name>
<listener-class>com.twelvemonkeys.servlet.image.IIOProviderContextListener</listener-class>
</listener>
...
</web-app>
```
Loading plugins from `WEB-INF/lib` without the context listener installed is unsupported and will not work correctly.
The context listener has no dependencies to the TwelveMonkeys ImageIO plugins, and may be used with JAI ImageIO
or other ImageIO plugins as well.
Another safe option, is to place the JAR files in the application server's shared or common lib folder.
#### Including the plugins in a "fat" JAR
The recommended way to use the plugins, is just to include the JARs as-is in your project, through a Maven dependency or similar.
Re-packaging is not necessary to use the library, and not recommended.
However, if you like to create a "fat"
JAR, or otherwise like to re-package the JARs for some reason, it's important to remember that automatic discovery of
the plugins by ImageIO depends on the
[Service Provider Interface (SPI)](https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html) mechanism.
In short, each JAR contains a special folder, named `META-INF/services` containing one or more files,
typically `javax.imageio.spi.ImageReaderSpi` and `javax.imageio.spi.ImageWriterSpi`.
These files exist *with the same name in every JAR*,
so if you simply unpack everything to a single folder or create a JAR, files will be overwritten and behavior be
unspecified (most likely you will end up with a single plugin being installed).
The solution is to make sure all files with the same name, are merged to a single file,
containing all the SPI information of each type. If using the Maven Shade plugin, you should use the
[ServicesResourceTransformer](https://maven.apache.org/plugins/maven-shade-plugin/examples/resource-transformers.html#ServicesResourceTransformer)
to properly merge these files. You may also want to use the
[ManifestResourceTransforme](https://maven.apache.org/plugins/maven-shade-plugin/examples/resource-transformers.html#ManifestResourceTransformer)
to get the correct vendor name, version info etc.
Other "fat" JAR bundlers will probably have similar mechanisms to merge entries with the same name.
### Links to prebuilt binaries ### Links to prebuilt binaries
##### Latest version (3.5) ##### Latest version (3.6.3)
Requires Java 7 or later. Requires Java 7 or later.
Common dependencies Common dependencies
* [common-lang-3.5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-lang/3.5/common-lang-3.5.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.5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-io/3.5/common-io-3.5.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.5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-image/3.5/common-image-3.5.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 dependencies
* [imageio-core-3.5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-core/3.5/imageio-core-3.5.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.5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-metadata/3.5/imageio-metadata-3.5.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 plugins
* [imageio-bmp-3.5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-bmp/3.5/imageio-bmp-3.5.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-jpeg-3.5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jpeg/3.5/imageio-jpeg-3.5.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-tiff-3.5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tiff/3.5/imageio-tiff-3.5.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-pnm-3.5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pnm/3.5/imageio-pnm-3.5.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-psd-3.5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-psd/3.5/imageio-psd-3.5.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-hdr-3.5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-hdr/3.5/imageio-hdr-3.5.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-iff-3.5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-iff/3.5/imageio-iff-3.5.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-pcx-3.5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pcx/3.5/imageio-pcx-3.5.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-pict-3.5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pict/3.5/imageio-pict-3.5.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.5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-sgi/3.5/imageio-sgi-3.5.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.5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tga/3.5/imageio-tga-3.5.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-icns-3.5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-icns/3.5/imageio-icns-3.5.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-thumbsdb-3.5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-thumbsdb/3.5/imageio-thumbsdb-3.5.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 plugins requiring 3rd party libs
* [imageio-batik-3.5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-batik/3.5/imageio-batik-3.5.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 Photoshop Path support for ImageIO
* [imageio-clippath-3.5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-clippath/3.5/imageio-clippath-3.5.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 support
* [servlet-3.5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/servlet/servlet/3.5/servlet-3.5.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) ##### Old version (3.0.x)
@@ -598,9 +439,9 @@ Servlet support
## License ## License
The project is distributed under the OSI approved [BSD license](http://opensource.org/licenses/BSD-3-Clause): This project is provided under the OSI approved [BSD license](http://opensource.org/licenses/BSD-3-Clause):
Copyright (c) 2008-2018, Harald Kuhr Copyright (c) 2008-2020, Harald Kuhr
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@@ -661,10 +502,15 @@ the Sun/Oracle provided `JPEGImageReader` and `BMPImageReader`, and the Apple pr
respectively. Using the pairwise ordering will not remove any functionality form these implementations, but in most respectively. Using the pairwise ordering will not remove any functionality form these implementations, but in most
cases you'll end up using the TwelveMonkeys plug-ins instead. cases you'll end up using the TwelveMonkeys plug-ins instead.
q: Why is there no support for common formats like GIF or PNG?
a: The short answer is simply that the built-in support in ImageIO for these formats are good enough as-is.
If you are looking for better PNG write performance on Java 7 and 8, see [JDK9 PNG Writer Backport](https://github.com/gredler/jdk9-png-writer-backport).
q: What about JAI? Several of the formats are already supported by JAI. q: What about JAI? Several of the formats are already supported by JAI.
a: While JAI (and jai-imageio in particular) have support for some of the formats, JAI has some major issues. a: While JAI (and jai-imageio in particular) have support for some of the same formats, JAI has some major issues.
The most obvious being: The most obvious being:
- It's not actively developed. No issues has been fixed for years. - It's not actively developed. No issues has been fixed for years.
- To get full format support, you need native libs. - To get full format support, you need native libs.
+6 -1
View File
@@ -5,7 +5,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys</groupId> <groupId>com.twelvemonkeys</groupId>
<artifactId>twelvemonkeys</artifactId> <artifactId>twelvemonkeys</artifactId>
<version>3.6</version> <version>3.6.5-SNAPSHOT</version>
</parent> </parent>
<groupId>com.twelvemonkeys.bom</groupId> <groupId>com.twelvemonkeys.bom</groupId>
@@ -123,6 +123,11 @@
<artifactId>imageio-tiff</artifactId> <artifactId>imageio-tiff</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-xwd</artifactId>
<version>${project.version}</version>
</dependency>
<!-- ImageIO 3rd party dependent plugins --> <!-- ImageIO 3rd party dependent plugins -->
<dependency> <dependency>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.common</groupId> <groupId>com.twelvemonkeys.common</groupId>
<artifactId>common</artifactId> <artifactId>common</artifactId>
<version>3.6</version> <version>3.6.5-SNAPSHOT</version>
</parent> </parent>
<artifactId>common-image</artifactId> <artifactId>common-image</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.common</groupId> <groupId>com.twelvemonkeys.common</groupId>
<artifactId>common</artifactId> <artifactId>common</artifactId>
<version>3.6</version> <version>3.6.5-SNAPSHOT</version>
</parent> </parent>
<artifactId>common-io</artifactId> <artifactId>common-io</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
@@ -72,7 +72,7 @@ public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
} }
@Override @Override
public void write(byte pBytes[], int pOffset, int pLength) { public void write(byte[] pBytes, int pOffset, int pLength) {
if ((pOffset < 0) || (pOffset > pBytes.length) || (pLength < 0) || if ((pOffset < 0) || (pOffset > pBytes.length) || (pLength < 0) ||
((pOffset + pLength) > pBytes.length) || ((pOffset + pLength) < 0)) { ((pOffset + pLength) > pBytes.length) || ((pOffset + pLength) < 0)) {
throw new IndexOutOfBoundsException(); throw new IndexOutOfBoundsException();
@@ -98,7 +98,7 @@ public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
private void growIfNeeded(int pNewCount) { private void growIfNeeded(int pNewCount) {
if (pNewCount > buf.length) { if (pNewCount > buf.length) {
int newSize = Math.max(Math.min(buf.length << 1, buf.length + maxGrowSize), pNewCount); int newSize = Math.max(Math.min(buf.length << 1, buf.length + maxGrowSize), pNewCount);
byte newBuf[] = new byte[newSize]; byte[] newBuf = new byte[newSize];
System.arraycopy(buf, 0, newBuf, 0, count); System.arraycopy(buf, 0, newBuf, 0, count);
buf = newBuf; buf = newBuf;
} }
@@ -113,7 +113,7 @@ public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
// Non-synchronized version of toByteArray // Non-synchronized version of toByteArray
@Override @Override
public byte[] toByteArray() { public byte[] toByteArray() {
byte newBuf[] = new byte[count]; byte[] newBuf = new byte[count];
System.arraycopy(buf, 0, newBuf, 0, count); System.arraycopy(buf, 0, newBuf, 0, count);
return newBuf; return newBuf;
+1 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.common</groupId> <groupId>com.twelvemonkeys.common</groupId>
<artifactId>common</artifactId> <artifactId>common</artifactId>
<version>3.6</version> <version>3.6.5-SNAPSHOT</version>
</parent> </parent>
<artifactId>common-lang</artifactId> <artifactId>common-lang</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
+2 -2
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys</groupId> <groupId>com.twelvemonkeys</groupId>
<artifactId>twelvemonkeys</artifactId> <artifactId>twelvemonkeys</artifactId>
<version>3.6</version> <version>3.6.5-SNAPSHOT</version>
</parent> </parent>
<groupId>com.twelvemonkeys.common</groupId> <groupId>com.twelvemonkeys.common</groupId>
<artifactId>common</artifactId> <artifactId>common</artifactId>
@@ -47,7 +47,7 @@
<dependency> <dependency>
<groupId>junit</groupId> <groupId>junit</groupId>
<artifactId>junit</artifactId> <artifactId>junit</artifactId>
<version>4.7</version> <version>4.13.1</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
+2 -2
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys</groupId> <groupId>com.twelvemonkeys</groupId>
<artifactId>twelvemonkeys</artifactId> <artifactId>twelvemonkeys</artifactId>
<version>3.6</version> <version>3.6.5-SNAPSHOT</version>
</parent> </parent>
<groupId>com.twelvemonkeys.contrib</groupId> <groupId>com.twelvemonkeys.contrib</groupId>
<artifactId>contrib</artifactId> <artifactId>contrib</artifactId>
@@ -65,7 +65,7 @@
<dependency> <dependency>
<groupId>junit</groupId> <groupId>junit</groupId>
<artifactId>junit</artifactId> <artifactId>junit</artifactId>
<version>4.7</version> <version>4.13.1</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
@@ -2,6 +2,7 @@ package com.twelvemonkeys.contrib.exif;
import com.twelvemonkeys.image.ImageUtil; import com.twelvemonkeys.image.ImageUtil;
import com.twelvemonkeys.imageio.ImageReaderBase; import com.twelvemonkeys.imageio.ImageReaderBase;
import org.w3c.dom.NodeList; import org.w3c.dom.NodeList;
import javax.imageio.IIOImage; import javax.imageio.IIOImage;
@@ -35,7 +36,7 @@ public class EXIFUtilities {
* @throws IOException if an error occurs during reading. * @throws IOException if an error occurs during reading.
*/ */
public static IIOImage readWithOrientation(final URL input) throws IOException { public static IIOImage readWithOrientation(final URL input) throws IOException {
try (ImageInputStream stream = ImageIO.createImageOutputStream(input)) { try (ImageInputStream stream = ImageIO.createImageInputStream(input)) {
return readWithOrientation(stream); return readWithOrientation(stream);
} }
} }
@@ -48,7 +49,7 @@ public class EXIFUtilities {
* @throws IOException if an error occurs during reading. * @throws IOException if an error occurs during reading.
*/ */
public static IIOImage readWithOrientation(final InputStream input) throws IOException { public static IIOImage readWithOrientation(final InputStream input) throws IOException {
try (ImageInputStream stream = ImageIO.createImageOutputStream(input)) { try (ImageInputStream stream = ImageIO.createImageInputStream(input)) {
return readWithOrientation(stream); return readWithOrientation(stream);
} }
} }
@@ -61,7 +62,7 @@ public class EXIFUtilities {
* @throws IOException if an error occurs during reading. * @throws IOException if an error occurs during reading.
*/ */
public static IIOImage readWithOrientation(final File input) throws IOException { public static IIOImage readWithOrientation(final File input) throws IOException {
try (ImageInputStream stream = ImageIO.createImageOutputStream(input)) { try (ImageInputStream stream = ImageIO.createImageInputStream(input)) {
return readWithOrientation(stream); return readWithOrientation(stream);
} }
} }
@@ -82,12 +83,11 @@ public class EXIFUtilities {
ImageReader reader = readers.next(); ImageReader reader = readers.next();
try { try {
reader.setInput(input, true, false); reader.setInput(input, true, false);
IIOImage image = reader.readAll(0, reader.getDefaultReadParam());
BufferedImage bufferedImage = ImageUtil.toBuffered(image.getRenderedImage()); IIOMetadata metadata = reader.getImageMetadata(0);
image.setRenderedImage(applyOrientation(bufferedImage, findImageOrientation(image.getMetadata()).value())); BufferedImage bufferedImage = applyOrientation(reader.read(0), findImageOrientation(metadata).value());
return image; return new IIOImage(bufferedImage, null, metadata);
} }
finally { finally {
reader.dispose(); reader.dispose();
@@ -123,9 +123,15 @@ public class EXIFUtilities {
for (String arg : args) { for (String arg : args) {
File input = new File(arg); File input = new File(arg);
// Read everything (similar to ImageReader.readAll(0, null)), but applies the correct image orientation // Read everything but thumbnails (similar to ImageReader.readAll(0, null)),
// and applies the correct image orientation
IIOImage image = readWithOrientation(input); IIOImage image = readWithOrientation(input);
if (image == null) {
System.err.printf("No reader for %s%n", input);
continue;
}
// Finds the orientation as defined by the javax_imageio_1.0 format // Finds the orientation as defined by the javax_imageio_1.0 format
Orientation orientation = findImageOrientation(image.getMetadata()); Orientation orientation = findImageOrientation(image.getMetadata());
+3 -2
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId> <artifactId>imageio</artifactId>
<version>3.6</version> <version>3.6.5-SNAPSHOT</version>
</parent> </parent>
<artifactId>imageio-batik</artifactId> <artifactId>imageio-batik</artifactId>
<name>TwelveMonkeys :: ImageIO :: Batik Plugin</name> <name>TwelveMonkeys :: ImageIO :: Batik Plugin</name>
@@ -39,6 +39,7 @@
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-core</artifactId> <artifactId>imageio-core</artifactId>
<type>test-jar</type> <type>test-jar</type>
<scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
@@ -103,6 +104,6 @@
</dependencies> </dependencies>
<properties> <properties>
<batik.version>1.12</batik.version> <batik.version>1.14</batik.version>
</properties> </properties>
</project> </project>
@@ -31,6 +31,7 @@
package com.twelvemonkeys.imageio.plugins.svg; package com.twelvemonkeys.imageio.plugins.svg;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest; import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
@@ -44,7 +45,6 @@ import javax.imageio.stream.ImageInputStream;
import java.awt.*; import java.awt.*;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.awt.image.ImagingOpException; import java.awt.image.ImagingOpException;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.URISyntaxException; import java.net.URISyntaxException;
@@ -67,7 +67,10 @@ import static org.mockito.Mockito.*;
*/ */
public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader> { public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader> {
private SVGImageReaderSpi provider = new SVGImageReaderSpi(); @Override
protected ImageReaderSpi createProvider() {
return new SVGImageReaderSpi();
}
protected List<TestData> getTestData() { protected List<TestData> getTestData() {
return Arrays.asList( return Arrays.asList(
@@ -82,19 +85,6 @@ public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader>
); );
} }
protected ImageReaderSpi createProvider() {
return provider;
}
@Override
protected SVGImageReader createReader() {
return new SVGImageReader(createProvider());
}
protected Class<SVGImageReader> getReaderClass() {
return SVGImageReader.class;
}
protected List<String> getFormatNames() { protected List<String> getFormatNames() {
return Collections.singletonList("svg"); return Collections.singletonList("svg");
} }
@@ -111,8 +101,6 @@ public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader>
public void testScaleViewBox() throws IOException { public void testScaleViewBox() throws IOException {
URL svgUrl = getClassLoaderResource("/svg/quadrants.svg"); URL svgUrl = getClassLoaderResource("/svg/quadrants.svg");
File tmpDir = new File(System.getProperty("java.io.tmpdir"));
SVGImageReader reader = createReader(); SVGImageReader reader = createReader();
SVGReadParam param = new SVGReadParam(); SVGReadParam param = new SVGReadParam();
@@ -160,11 +148,11 @@ public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader>
@Test @Test
@Override @Override
public void testReadWithSizeParam() { public void testReadWithSizeParam() throws IOException {
try { try {
super.testReadWithSizeParam(); super.testReadWithSizeParam();
} }
catch (AssertionError failure) { catch (AssertionError | IOException failure) {
Throwable cause = failure; Throwable cause = failure;
while (cause.getCause() != null) { while (cause.getCause() != null) {
@@ -299,7 +287,7 @@ public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader>
params.setAllowExternalResources(true); params.setAllowExternalResources(true);
reader.read(0, params); reader.read(0, params);
assertTrue("reader.read should've thrown an exception, but didn't", false); fail("reader.read should've thrown an exception, but didn't");
} }
catch (IIOException allowed) { catch (IIOException allowed) {
assertTrue(allowed.getMessage().contains("batikLogo.svg")); // The embedded resource we don't find assertTrue(allowed.getMessage().contains("batikLogo.svg")); // The embedded resource we don't find
@@ -31,6 +31,7 @@
package com.twelvemonkeys.imageio.plugins.wmf; package com.twelvemonkeys.imageio.plugins.wmf;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest; import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
@@ -49,7 +50,10 @@ import java.util.List;
* @version $Id: WMFImageReaderTest.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$ * @version $Id: WMFImageReaderTest.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$
*/ */
public class WMFImageReaderTest extends ImageReaderAbstractTest<WMFImageReader> { public class WMFImageReaderTest extends ImageReaderAbstractTest<WMFImageReader> {
private WMFImageReaderSpi provider = new WMFImageReaderSpi(); @Override
protected ImageReaderSpi createProvider() {
return new WMFImageReaderSpi();
}
protected List<TestData> getTestData() { protected List<TestData> getTestData() {
return Collections.singletonList( return Collections.singletonList(
@@ -57,27 +61,17 @@ public class WMFImageReaderTest extends ImageReaderAbstractTest<WMFImageReader>
); );
} }
protected ImageReaderSpi createProvider() {
return provider;
}
@Override @Override
protected WMFImageReader createReader() {
return new WMFImageReader(createProvider());
}
protected Class<WMFImageReader> getReaderClass() {
return WMFImageReader.class;
}
protected List<String> getFormatNames() { protected List<String> getFormatNames() {
return Collections.singletonList("wmf"); return Collections.singletonList("wmf");
} }
@Override
protected List<String> getSuffixes() { protected List<String> getSuffixes() {
return Arrays.asList("wmf", "emf"); return Arrays.asList("wmf", "emf");
} }
@Override
protected List<String> getMIMETypes() { protected List<String> getMIMETypes() {
return Arrays.asList("image/x-wmf", "application/x-msmetafile"); return Arrays.asList("image/x-wmf", "application/x-msmetafile");
} }
+2 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId> <artifactId>imageio</artifactId>
<version>3.6</version> <version>3.6.5-SNAPSHOT</version>
</parent> </parent>
<artifactId>imageio-bmp</artifactId> <artifactId>imageio-bmp</artifactId>
<name>TwelveMonkeys :: ImageIO :: BMP plugin</name> <name>TwelveMonkeys :: ImageIO :: BMP plugin</name>
@@ -19,6 +19,7 @@
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-core</artifactId> <artifactId>imageio-core</artifactId>
<type>test-jar</type> <type>test-jar</type>
<scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>
@@ -30,22 +30,6 @@
package com.twelvemonkeys.imageio.plugins.bmp; package com.twelvemonkeys.imageio.plugins.bmp;
import com.twelvemonkeys.imageio.ImageReaderBase;
import com.twelvemonkeys.imageio.stream.SubImageInputStream;
import com.twelvemonkeys.imageio.util.IIOUtil;
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
import com.twelvemonkeys.imageio.util.ProgressListenerBase;
import com.twelvemonkeys.io.LittleEndianDataInputStream;
import com.twelvemonkeys.io.enc.DecoderStream;
import com.twelvemonkeys.xml.XMLSerializer;
import javax.imageio.*;
import javax.imageio.event.IIOReadUpdateListener;
import javax.imageio.event.IIOReadWarningListener;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataFormatImpl;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import java.awt.*; import java.awt.*;
import java.awt.color.ColorSpace; import java.awt.color.ColorSpace;
import java.awt.image.*; import java.awt.image.*;
@@ -56,6 +40,27 @@ import java.nio.ByteOrder;
import java.util.Arrays; import java.util.Arrays;
import java.util.Iterator; import java.util.Iterator;
import javax.imageio.IIOException;
import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.event.IIOReadUpdateListener;
import javax.imageio.event.IIOReadWarningListener;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataFormatImpl;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import com.twelvemonkeys.imageio.ImageReaderBase;
import com.twelvemonkeys.imageio.stream.SubImageInputStream;
import com.twelvemonkeys.imageio.util.IIOUtil;
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
import com.twelvemonkeys.imageio.util.ProgressListenerBase;
import com.twelvemonkeys.io.LittleEndianDataInputStream;
import com.twelvemonkeys.io.enc.DecoderStream;
import com.twelvemonkeys.xml.XMLSerializer;
/** /**
* ImageReader for Microsoft Windows Bitmap (BMP) format. * ImageReader for Microsoft Windows Bitmap (BMP) format.
* *
@@ -125,6 +130,10 @@ public final class BMPImageReader extends ImageReaderBase {
// Read DIB header // Read DIB header
header = DIBHeader.read(imageInput); header = DIBHeader.read(imageInput);
if (pixelOffset < header.size + DIB.BMP_FILE_HEADER_SIZE) {
throw new IIOException("Invalid pixel offset: " + pixelOffset);
}
} }
} }
@@ -207,50 +216,55 @@ public final class BMPImageReader extends ImageReaderBase {
throw new IIOException("Multiple planes not supported"); throw new IIOException("Multiple planes not supported");
} }
switch (header.getBitCount()) { try {
case 1: switch (header.getBitCount()) {
case 2: case 1:
case 4: case 2:
case 8: case 4:
return ImageTypeSpecifiers.createFromIndexColorModel(readColorMap()); case 8:
return ImageTypeSpecifiers.createFromIndexColorModel(readColorMap());
case 16: case 16:
if (header.hasMasks()) { if (header.hasMasks()) {
return ImageTypeSpecifiers.createPacked( return ImageTypeSpecifiers.createPacked(
ColorSpace.getInstance(ColorSpace.CS_sRGB), ColorSpace.getInstance(ColorSpace.CS_sRGB),
header.masks[0], header.masks[1], header.masks[2], header.masks[3], header.masks[0], header.masks[1], header.masks[2], header.masks[3],
DataBuffer.TYPE_USHORT, false DataBuffer.TYPE_USHORT, false
); );
} }
// Default if no mask is 555 // Default if no mask is 555
return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_USHORT_555_RGB); return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_USHORT_555_RGB);
case 24: case 24:
if (header.getCompression() != DIB.COMPRESSION_RGB) { if (header.getCompression() != DIB.COMPRESSION_RGB) {
throw new IIOException("Unsupported compression for RGB: " + header.getCompression()); throw new IIOException("Unsupported compression for RGB: " + header.getCompression());
} }
return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR); return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR);
case 32: case 32:
if (header.hasMasks()) { if (header.hasMasks()) {
return ImageTypeSpecifiers.createPacked( return ImageTypeSpecifiers.createPacked(
ColorSpace.getInstance(ColorSpace.CS_sRGB), ColorSpace.getInstance(ColorSpace.CS_sRGB),
header.masks[0], header.masks[1], header.masks[2], header.masks[3], header.masks[0], header.masks[1], header.masks[2], header.masks[3],
DataBuffer.TYPE_INT, false DataBuffer.TYPE_INT, false
); );
} }
// Default if no mask // Default if no mask
return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB); return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB);
case 0: case 0:
if (header.getCompression() == DIB.COMPRESSION_JPEG || header.getCompression() == DIB.COMPRESSION_PNG) { if (header.getCompression() == DIB.COMPRESSION_JPEG || header.getCompression() == DIB.COMPRESSION_PNG) {
return initReaderDelegate(header.getCompression()).getRawImageType(0); return initReaderDelegate(header.getCompression()).getRawImageType(0);
} }
default: default:
throw new IIOException("Unsupported bit count: " + header.getBitCount()); throw new IIOException("Unsupported bit count: " + header.getBitCount());
}
}
catch (IllegalArgumentException e) {
throw new IIOException(e.getMessage(), e);
} }
} }
@@ -29,9 +29,10 @@
*/ */
package com.twelvemonkeys.imageio.plugins.bmp; package com.twelvemonkeys.imageio.plugins.bmp;
import com.twelvemonkeys.lang.Validate; import static com.twelvemonkeys.lang.Validate.notNull;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.IOException;
/** /**
* Describes a bitmap structure. * Describes a bitmap structure.
@@ -47,14 +48,11 @@ abstract class BitmapDescriptor {
protected BitmapMask mask; protected BitmapMask mask;
public BitmapDescriptor(final DirectoryEntry pEntry, final DIBHeader pHeader) { public BitmapDescriptor(final DirectoryEntry pEntry, final DIBHeader pHeader) {
Validate.notNull(pEntry, "entry"); entry = notNull(pEntry, "entry");;
Validate.notNull(pHeader, "header"); header = notNull(pHeader, "header");
entry = pEntry;
header = pHeader;
} }
abstract public BufferedImage getImage(); abstract public BufferedImage getImage() throws IOException;
public final int getWidth() { public final int getWidth() {
return entry.getWidth(); return entry.getWidth();
@@ -163,6 +163,7 @@ class BitmapIndexed extends BitmapDescriptor {
return transparent; return transparent;
} }
@Override
public BufferedImage getImage() { public BufferedImage getImage() {
if (image == null) { if (image == null) {
image = createImageIndexed(); image = createImageIndexed();
@@ -46,6 +46,7 @@ class BitmapRGB extends BitmapDescriptor {
super(pEntry, pHeader); super(pEntry, pHeader);
} }
@Override
public BufferedImage getImage() { public BufferedImage getImage() {
// Test is mask != null rather than hasMask(), as 32 bit (w/alpha) // Test is mask != null rather than hasMask(), as 32 bit (w/alpha)
// might still have bitmask, but we don't read or use it. // might still have bitmask, but we don't read or use it.
@@ -31,6 +31,9 @@
package com.twelvemonkeys.imageio.plugins.bmp; package com.twelvemonkeys.imageio.plugins.bmp;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.IIOException;
/** /**
* Represents bitmap structures we can't read. * Represents bitmap structures we can't read.
@@ -42,13 +45,14 @@ import java.awt.image.BufferedImage;
class BitmapUnsupported extends BitmapDescriptor { class BitmapUnsupported extends BitmapDescriptor {
private String message; private String message;
public BitmapUnsupported(final DirectoryEntry pEntry, final String pMessage) { public BitmapUnsupported(final DirectoryEntry pEntry, DIBHeader header, final String pMessage) {
super(pEntry, null); super(pEntry, header);
message = pMessage; message = pMessage;
} }
public BufferedImage getImage() { @Override
throw new IllegalStateException(message); public BufferedImage getImage() throws IOException {
throw new IIOException(message);
} }
} }
@@ -30,11 +30,12 @@
package com.twelvemonkeys.imageio.plugins.bmp; package com.twelvemonkeys.imageio.plugins.bmp;
import javax.imageio.IIOException;
import java.io.DataInput; import java.io.DataInput;
import java.io.DataOutput; import java.io.DataOutput;
import java.io.IOException; import java.io.IOException;
import javax.imageio.IIOException;
/** /**
* Represents the DIB (Device Independent Bitmap) Information header structure. * Represents the DIB (Device Independent Bitmap) Information header structure.
* *
@@ -213,7 +214,7 @@ abstract class DIBHeader {
// NOTE: Unlike all other headers, width and height are unsigned SHORT values (16 bit)! // NOTE: Unlike all other headers, width and height are unsigned SHORT values (16 bit)!
width = pStream.readUnsignedShort(); width = pStream.readUnsignedShort();
height = pStream.readUnsignedShort(); height = pStream.readShort();
if (height < 0) { if (height < 0) {
height = -height; height = -height;
@@ -240,6 +241,7 @@ abstract class DIBHeader {
* @see <a href="http://www.fileformat.info/format/os2bmp/egff.htm">OS/2 Bitmap File Format Summary</a> * @see <a href="http://www.fileformat.info/format/os2bmp/egff.htm">OS/2 Bitmap File Format Summary</a>
*/ */
static final class BitmapCoreHeaderV2 extends DIBHeader { static final class BitmapCoreHeaderV2 extends DIBHeader {
@SuppressWarnings("unused")
protected void read(final int pSize, final DataInput pStream) throws IOException { protected void read(final int pSize, final DataInput pStream) throws IOException {
if (pSize != DIB.OS2_V2_HEADER_SIZE && pSize != DIB.OS2_V2_HEADER_16_SIZE) { if (pSize != DIB.OS2_V2_HEADER_SIZE && pSize != DIB.OS2_V2_HEADER_16_SIZE) {
throw new IIOException(String.format("Size: %s !=: %s", pSize, DIB.OS2_V2_HEADER_SIZE)); throw new IIOException(String.format("Size: %s !=: %s", pSize, DIB.OS2_V2_HEADER_SIZE));
@@ -30,16 +30,6 @@
package com.twelvemonkeys.imageio.plugins.bmp; package com.twelvemonkeys.imageio.plugins.bmp;
import com.twelvemonkeys.image.ImageUtil;
import com.twelvemonkeys.imageio.ImageReaderBase;
import com.twelvemonkeys.imageio.stream.SubImageInputStream;
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
import com.twelvemonkeys.util.WeakWeakMap;
import javax.imageio.*;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import javax.swing.*;
import java.awt.*; import java.awt.*;
import java.awt.color.ColorSpace; import java.awt.color.ColorSpace;
import java.awt.event.WindowAdapter; import java.awt.event.WindowAdapter;
@@ -48,8 +38,26 @@ import java.awt.image.*;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.*; import java.util.Map;
import java.util.WeakHashMap;
import javax.imageio.IIOException;
import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import javax.swing.*;
import com.twelvemonkeys.image.ImageUtil;
import com.twelvemonkeys.imageio.ImageReaderBase;
import com.twelvemonkeys.imageio.stream.SubImageInputStream;
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
import com.twelvemonkeys.util.WeakWeakMap;
/** /**
* ImageReader for Microsoft Windows ICO (icon) format. * ImageReader for Microsoft Windows ICO (icon) format.
@@ -287,7 +295,7 @@ abstract class DIBImageReader extends ImageReaderBase {
// TODO: Support this, it's already in the BMP reader, spec allows RLE4 and RLE8 // TODO: Support this, it's already in the BMP reader, spec allows RLE4 and RLE8
if (header.getCompression() != DIB.COMPRESSION_RGB) { if (header.getCompression() != DIB.COMPRESSION_RGB) {
descriptor = new BitmapUnsupported(pEntry, String.format("Unsupported compression: %d", header.getCompression())); descriptor = new BitmapUnsupported(pEntry, header, String.format("Unsupported compression: %d", header.getCompression()));
} }
else { else {
int bitCount = header.getBitCount(); int bitCount = header.getBitCount();
@@ -315,7 +323,7 @@ abstract class DIBImageReader extends ImageReaderBase {
break; break;
default: default:
descriptor = new BitmapUnsupported(pEntry, String.format("Unsupported bit count %d", bitCount)); descriptor = new BitmapUnsupported(pEntry, header, String.format("Unsupported bit count %d", bitCount));
} }
} }
@@ -355,7 +363,7 @@ abstract class DIBImageReader extends ImageReaderBase {
} }
private void readBitmapIndexed1(final BitmapIndexed pBitmap, final boolean pAsMask) throws IOException { private void readBitmapIndexed1(final BitmapIndexed pBitmap, final boolean pAsMask) throws IOException {
int width = adjustToPadding(pBitmap.getWidth() >> 3); int width = adjustToPadding((pBitmap.getWidth() + 7) >> 3);
byte[] row = new byte[width]; byte[] row = new byte[width];
for (int y = 0; y < pBitmap.getHeight(); y++) { for (int y = 0; y < pBitmap.getHeight(); y++) {
@@ -389,7 +397,7 @@ abstract class DIBImageReader extends ImageReaderBase {
} }
private void readBitmapIndexed4(final BitmapIndexed pBitmap) throws IOException { private void readBitmapIndexed4(final BitmapIndexed pBitmap) throws IOException {
int width = adjustToPadding(pBitmap.getWidth() >> 1); int width = adjustToPadding((pBitmap.getWidth() + 1) >> 1);
byte[] row = new byte[width]; byte[] row = new byte[width];
for (int y = 0; y < pBitmap.getHeight(); y++) { for (int y = 0; y < pBitmap.getHeight(); y++) {
@@ -457,13 +465,12 @@ abstract class DIBImageReader extends ImageReaderBase {
} }
private void readBitmap16(final BitmapDescriptor pBitmap) throws IOException { private void readBitmap16(final BitmapDescriptor pBitmap) throws IOException {
// TODO: No idea if this actually works..
short[] pixels = new short[pBitmap.getWidth() * pBitmap.getHeight()]; short[] pixels = new short[pBitmap.getWidth() * pBitmap.getHeight()];
// TODO: Support TYPE_USHORT_565 and the RGB 444/ARGB 4444 layouts // TODO: Support TYPE_USHORT_565 and the RGB 444/ARGB 4444 layouts
// Will create TYPE_USHORT_555 // Will create TYPE_USHORT_555
DirectColorModel cm = new DirectColorModel(16, 0x7C00, 0x03E0, 0x001F); DirectColorModel cm = new DirectColorModel(16, 0x7C00, 0x03E0, 0x001F);
DataBuffer buffer = new DataBufferShort(pixels, pixels.length); DataBuffer buffer = new DataBufferUShort(pixels, pixels.length);
WritableRaster raster = Raster.createPackedRaster( WritableRaster raster = Raster.createPackedRaster(
buffer, pBitmap.getWidth(), pBitmap.getHeight(), pBitmap.getWidth(), cm.getMasks(), null buffer, pBitmap.getWidth(), pBitmap.getHeight(), pBitmap.getWidth(), cm.getMasks(), null
); );
@@ -41,8 +41,8 @@ import java.util.Arrays;
* @version $Id: RLE4Decoder.java#1 $ * @version $Id: RLE4Decoder.java#1 $
*/ */
final class RLE4Decoder extends AbstractRLEDecoder { final class RLE4Decoder extends AbstractRLEDecoder {
final static int BIT_MASKS[] = {0xf0, 0x0f}; final static int[] BIT_MASKS = {0xf0, 0x0f};
final static int BIT_SHIFTS[] = {4, 0}; final static int[] BIT_SHIFTS = {4, 0};
public RLE4Decoder(final int width) { public RLE4Decoder(final int width) {
super(width, 4); super(width, 4);
@@ -94,7 +94,7 @@ final class RLE4Decoder extends AbstractRLEDecoder {
boolean paddingByte = (((byte2 + 1) / 2) % 2) != 0; boolean paddingByte = (((byte2 + 1) / 2) % 2) != 0;
int packed = 0; int packed = 0;
for (int i = 0; i < byte2; i++) { for (int i = 0; i < byte2 && srcX / 2 < row.length; i++) {
if (i % 2 == 0) { if (i % 2 == 0) {
packed = checkEOF(stream.read()); packed = checkEOF(stream.read());
} }
@@ -111,7 +111,7 @@ final class RLE4Decoder extends AbstractRLEDecoder {
else { else {
// Encoded mode // Encoded mode
// Replicate the two samples in byte2 as many times as byte1 says // Replicate the two samples in byte2 as many times as byte1 says
for (int i = 0; i < byte1; i++) { for (int i = 0; i < byte1 && srcX / 2 < row.length; i++) {
row[srcX / 2] |= (byte) (((byte2 & BIT_MASKS[i % 2]) >> BIT_SHIFTS[i % 2]) << BIT_SHIFTS[srcX % 2]); row[srcX / 2] |= (byte) (((byte2 & BIT_MASKS[i % 2]) >> BIT_SHIFTS[i % 2]) << BIT_SHIFTS[srcX % 2]);
srcX++; srcX++;
} }
@@ -94,7 +94,7 @@ final class RLE8Decoder extends AbstractRLEDecoder {
// an additional padding byte is in the stream and must be skipped // an additional padding byte is in the stream and must be skipped
boolean paddingByte = (byte2 % 2) != 0; boolean paddingByte = (byte2 % 2) != 0;
while (byte2-- > 0) { while (byte2-- > 0 && srcX < row.length) {
row[srcX++] = (byte) checkEOF(stream.read()); row[srcX++] = (byte) checkEOF(stream.read());
} }
@@ -107,7 +107,7 @@ final class RLE8Decoder extends AbstractRLEDecoder {
// Encoded mode // Encoded mode
// Replicate byte2 as many times as byte1 says // Replicate byte2 as many times as byte1 says
byte value = (byte) byte2; byte value = (byte) byte2;
while (byte1-- > 0) { while (byte1-- > 0 && srcX < row.length) {
row[srcX++] = value; row[srcX++] = value;
} }
} }
@@ -30,35 +30,43 @@
package com.twelvemonkeys.imageio.plugins.bmp; package com.twelvemonkeys.imageio.plugins.bmp;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest; import static org.junit.Assert.*;
import com.twelvemonkeys.xml.XMLSerializer; import static org.junit.Assume.assumeNoException;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import javax.imageio.IIOException;
import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.event.IIOReadProgressListener;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.spi.ImageReaderSpi;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.mockito.InOrder; import org.mockito.InOrder;
import org.w3c.dom.Node; import org.w3c.dom.Node;
import org.w3c.dom.NodeList; import org.w3c.dom.NodeList;
import javax.imageio.*; import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
import javax.imageio.event.IIOReadProgressListener; import com.twelvemonkeys.xml.XMLSerializer;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.spi.ImageReaderSpi;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import static org.junit.Assert.*;
import static org.junit.Assume.assumeNoException;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.*;
/** /**
* BMPImageReaderTest * BMPImageReaderTest
@@ -68,6 +76,12 @@ import static org.mockito.Mockito.*;
* @version $Id: BMPImageReaderTest.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$ * @version $Id: BMPImageReaderTest.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$
*/ */
public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader> { public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader> {
@Override
protected ImageReaderSpi createProvider() {
return new BMPImageReaderSpi();
}
@Override
protected List<TestData> getTestData() { protected List<TestData> getTestData() {
return Arrays.asList( return Arrays.asList(
// BMP Suite "Good" // BMP Suite "Good"
@@ -144,27 +158,17 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
); );
} }
protected ImageReaderSpi createProvider() {
return new BMPImageReaderSpi();
}
@Override @Override
protected BMPImageReader createReader() {
return new BMPImageReader(createProvider());
}
protected Class<BMPImageReader> getReaderClass() {
return BMPImageReader.class;
}
protected List<String> getFormatNames() { protected List<String> getFormatNames() {
return Collections.singletonList("bmp"); return Collections.singletonList("bmp");
} }
@Override
protected List<String> getSuffixes() { protected List<String> getSuffixes() {
return Arrays.asList("bmp", "rle"); return Arrays.asList("bmp", "rle");
} }
@Override
protected List<String> getMIMETypes() { protected List<String> getMIMETypes() {
return Collections.singletonList("image/bmp"); return Collections.singletonList("image/bmp");
} }
@@ -178,7 +182,7 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
ImageTypeSpecifier rawType = reader.getRawImageType(0); ImageTypeSpecifier rawType = reader.getRawImageType(0);
// As the JPEGImageReader we delegate to returns null for YCbCr, we'll have to do the same // As the JPEGImageReader we delegate to may return null for YCbCr, we'll have to do the same
if (rawType == null && data.getInput().toString().contains("jpeg")) { if (rawType == null && data.getInput().toString().contains("jpeg")) {
continue; continue;
} }
@@ -273,7 +277,7 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
} }
@Test @Test
public void testAddIIOReadProgressListenerCallbacksJPEG() { public void testAddIIOReadProgressListenerCallbacksJPEG() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
TestData data = new TestData(getClassLoaderResource("/bmpsuite/q/rgb24jpeg.bmp"), new Dimension(127, 64)); TestData data = new TestData(getClassLoaderResource("/bmpsuite/q/rgb24jpeg.bmp"), new Dimension(127, 64));
reader.setInput(data.getInputStream()); reader.setInput(data.getInputStream());
@@ -296,7 +300,7 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
} }
@Test @Test
public void testAddIIOReadProgressListenerCallbacksPNG() { public void testAddIIOReadProgressListenerCallbacksPNG() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
TestData data = new TestData(getClassLoaderResource("/bmpsuite/q/rgb24png.bmp"), new Dimension(127, 64)); TestData data = new TestData(getClassLoaderResource("/bmpsuite/q/rgb24png.bmp"), new Dimension(127, 64));
reader.setInput(data.getInputStream()); reader.setInput(data.getInputStream());
@@ -319,7 +323,7 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
} }
@Test @Test
public void testMetadataEqualsJRE() throws IOException, URISyntaxException { public void testMetadataEqualsJRE() throws IOException {
ImageReader jreReader; ImageReader jreReader;
try { try {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@@ -356,7 +360,7 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
} }
IIOMetadata jreMetadata = jreReader.getImageMetadata(0); IIOMetadata jreMetadata = jreReader.getImageMetadata(0);
assertEquals(true, metadata.isStandardMetadataFormatSupported()); assertTrue(metadata.isStandardMetadataFormatSupported());
assertEquals(jreMetadata.getNativeMetadataFormatName(), metadata.getNativeMetadataFormatName()); assertEquals(jreMetadata.getNativeMetadataFormatName(), metadata.getNativeMetadataFormatName());
assertArrayEquals(jreMetadata.getExtraMetadataFormatNames(), metadata.getExtraMetadataFormatNames()); assertArrayEquals(jreMetadata.getExtraMetadataFormatNames(), metadata.getExtraMetadataFormatNames());
@@ -379,7 +383,7 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
new XMLSerializer(expected, "UTF-8").serialize(expectedTree, false); new XMLSerializer(expected, "UTF-8").serialize(expectedTree, false);
new XMLSerializer(actual, "UTF-8").serialize(actualTree, false); new XMLSerializer(actual, "UTF-8").serialize(actualTree, false);
assertEquals(e.getMessage(), new String(expected.toByteArray(), "UTF-8"), new String(actual.toByteArray(), "UTF-8")); assertEquals(e.getMessage(), new String(expected.toByteArray(), StandardCharsets.UTF_8), new String(actual.toByteArray(), StandardCharsets.UTF_8));
throw e; throw e;
} }
@@ -2,10 +2,10 @@ package com.twelvemonkeys.imageio.plugins.bmp;
import com.twelvemonkeys.imageio.util.ImageWriterAbstractTest; import com.twelvemonkeys.imageio.util.ImageWriterAbstractTest;
import javax.imageio.ImageWriter; import javax.imageio.spi.ImageWriterSpi;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage; import java.awt.image.RenderedImage;
import java.util.Arrays; import java.util.Collections;
import java.util.List; import java.util.List;
/** /**
@@ -15,18 +15,15 @@ import java.util.List;
* @author last modified by : harald.kuhr$ * @author last modified by : harald.kuhr$
* @version : BMPImageWriterTest.java,v 1.0 25/06/2020 harald.kuhr Exp$ * @version : BMPImageWriterTest.java,v 1.0 25/06/2020 harald.kuhr Exp$
*/ */
public class BMPImageWriterTest extends ImageWriterAbstractTest { public class BMPImageWriterTest extends ImageWriterAbstractTest<BMPImageWriter> {
private final BMPImageWriterSpi provider = new BMPImageWriterSpi();
@Override @Override
protected ImageWriter createImageWriter() { protected ImageWriterSpi createProvider() {
return provider.createWriterInstance(null); return new BMPImageWriterSpi();
} }
@Override @Override
protected List<? extends RenderedImage> getTestData() { protected List<? extends RenderedImage> getTestData() {
return Arrays.asList( return Collections.singletonList(
new BufferedImage(10, 10, BufferedImage.TYPE_4BYTE_ABGR) new BufferedImage(10, 10, BufferedImage.TYPE_4BYTE_ABGR)
); );
} }
@@ -31,6 +31,7 @@
package com.twelvemonkeys.imageio.plugins.bmp; package com.twelvemonkeys.imageio.plugins.bmp;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest; import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
@@ -53,6 +54,12 @@ import static org.junit.Assert.*;
* @version $Id: CURImageReaderTest.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$ * @version $Id: CURImageReaderTest.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$
*/ */
public class CURImageReaderTest extends ImageReaderAbstractTest<CURImageReader> { public class CURImageReaderTest extends ImageReaderAbstractTest<CURImageReader> {
@Override
protected ImageReaderSpi createProvider() {
return new CURImageReaderSpi();
}
@Override
protected List<TestData> getTestData() { protected List<TestData> getTestData() {
return Arrays.asList( return Arrays.asList(
new TestData(getClassLoaderResource("/cur/hand.cur"), new Dimension(32, 32)), new TestData(getClassLoaderResource("/cur/hand.cur"), new Dimension(32, 32)),
@@ -60,27 +67,17 @@ public class CURImageReaderTest extends ImageReaderAbstractTest<CURImageReader>
); );
} }
protected ImageReaderSpi createProvider() {
return new CURImageReaderSpi();
}
@Override @Override
protected CURImageReader createReader() {
return new CURImageReader();
}
protected Class<CURImageReader> getReaderClass() {
return CURImageReader.class;
}
protected List<String> getFormatNames() { protected List<String> getFormatNames() {
return Collections.singletonList("cur"); return Collections.singletonList("cur");
} }
@Override
protected List<String> getSuffixes() { protected List<String> getSuffixes() {
return Collections.singletonList("cur"); return Collections.singletonList("cur");
} }
@Override
protected List<String> getMIMETypes() { protected List<String> getMIMETypes() {
return Arrays.asList("image/vnd.microsoft.cursor", "image/cursor", "image/x-cursor"); return Arrays.asList("image/vnd.microsoft.cursor", "image/cursor", "image/x-cursor");
} }
@@ -99,7 +96,7 @@ public class CURImageReaderTest extends ImageReaderAbstractTest<CURImageReader>
assertNotNull("Hotspot for cursor not present", hotspot); assertNotNull("Hotspot for cursor not present", hotspot);
// Image weirdness // Image weirdness
assertTrue("Hotspot for cursor undefined (java.awt.Image.UndefinedProperty)", Image.UndefinedProperty != hotspot); assertNotSame("Hotspot for cursor undefined (java.awt.Image.UndefinedProperty)", Image.UndefinedProperty, hotspot);
assertTrue(String.format("Hotspot not a java.awt.Point: %s", hotspot.getClass()), hotspot instanceof Point); assertTrue(String.format("Hotspot not a java.awt.Point: %s", hotspot.getClass()), hotspot instanceof Point);
assertEquals(pExpected, hotspot); assertEquals(pExpected, hotspot);
@@ -31,6 +31,7 @@
package com.twelvemonkeys.imageio.plugins.bmp; package com.twelvemonkeys.imageio.plugins.bmp;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest; import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
@@ -49,6 +50,12 @@ import java.util.List;
* @version $Id: ICOImageReaderTest.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$ * @version $Id: ICOImageReaderTest.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$
*/ */
public class ICOImageReaderTest extends ImageReaderAbstractTest<ICOImageReader> { public class ICOImageReaderTest extends ImageReaderAbstractTest<ICOImageReader> {
@Override
protected ImageReaderSpi createProvider() {
return new ICOImageReaderSpi();
}
@Override
protected List<TestData> getTestData() { protected List<TestData> getTestData() {
return Arrays.asList( return Arrays.asList(
new TestData( new TestData(
@@ -75,27 +82,17 @@ public class ICOImageReaderTest extends ImageReaderAbstractTest<ICOImageReader>
); );
} }
protected ImageReaderSpi createProvider() {
return new ICOImageReaderSpi();
}
@Override @Override
protected ICOImageReader createReader() {
return new ICOImageReader();
}
protected Class<ICOImageReader> getReaderClass() {
return ICOImageReader.class;
}
protected List<String> getFormatNames() { protected List<String> getFormatNames() {
return Collections.singletonList("ico"); return Collections.singletonList("ico");
} }
@Override
protected List<String> getSuffixes() { protected List<String> getSuffixes() {
return Collections.singletonList("ico"); return Collections.singletonList("ico");
} }
@Override
protected List<String> getMIMETypes() { protected List<String> getMIMETypes() {
return Arrays.asList("image/vnd.microsoft.icon", "image/ico", "image/x-icon"); return Arrays.asList("image/vnd.microsoft.icon", "image/ico", "image/x-icon");
} }
@@ -2,7 +2,7 @@ package com.twelvemonkeys.imageio.plugins.bmp;
import com.twelvemonkeys.imageio.util.ImageWriterAbstractTest; import com.twelvemonkeys.imageio.util.ImageWriterAbstractTest;
import javax.imageio.ImageWriter; import javax.imageio.spi.ImageWriterSpi;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage; import java.awt.image.RenderedImage;
import java.util.Arrays; import java.util.Arrays;
@@ -15,12 +15,10 @@ import java.util.List;
* @author last modified by : harald.kuhr$ * @author last modified by : harald.kuhr$
* @version : ICOImageWriterTest.java,v 1.0 25/06/2020 harald.kuhr Exp$ * @version : ICOImageWriterTest.java,v 1.0 25/06/2020 harald.kuhr Exp$
*/ */
public class ICOImageWriterTest extends ImageWriterAbstractTest { public class ICOImageWriterTest extends ImageWriterAbstractTest<ICOImageWriter> {
private final ICOImageWriterSpi provider = new ICOImageWriterSpi();
@Override @Override
protected ImageWriter createImageWriter() { protected ImageWriterSpi createProvider() {
return provider.createWriterInstance(null); return new ICOImageWriterSpi();
} }
@Override @Override
+2 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId> <artifactId>imageio</artifactId>
<version>3.6</version> <version>3.6.5-SNAPSHOT</version>
</parent> </parent>
<artifactId>imageio-clippath</artifactId> <artifactId>imageio-clippath</artifactId>
<name>TwelveMonkeys :: ImageIO :: Photoshop Path Support</name> <name>TwelveMonkeys :: ImageIO :: Photoshop Path Support</name>
@@ -21,6 +21,7 @@
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-core</artifactId> <artifactId>imageio-core</artifactId>
<type>test-jar</type> <type>test-jar</type>
<scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
@@ -56,6 +56,8 @@ import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage; import java.awt.image.RenderedImage;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.SequenceInputStream;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
@@ -131,7 +133,13 @@ public final class Paths {
List<JPEGSegment> photoshop = JPEGSegmentUtil.readSegments(stream, segmentIdentifiers); List<JPEGSegment> photoshop = JPEGSegmentUtil.readSegments(stream, segmentIdentifiers);
if (!photoshop.isEmpty()) { if (!photoshop.isEmpty()) {
return readPathFromPhotoshopResources(new MemoryCacheImageInputStream(photoshop.get(0).data())); InputStream data = null;
for (JPEGSegment ps : photoshop) {
data = data == null ? ps.data() : new SequenceInputStream(data, ps.data());
}
return readPathFromPhotoshopResources(new MemoryCacheImageInputStream(data));
} }
} }
else if (magic >>> 16 == TIFF.BYTE_ORDER_MARK_BIG_ENDIAN && (magic & 0xffff) == TIFF.TIFF_MAGIC else if (magic >>> 16 == TIFF.BYTE_ORDER_MARK_BIG_ENDIAN && (magic & 0xffff) == TIFF.TIFF_MAGIC
@@ -350,10 +358,10 @@ public final class Paths {
IIOMetadataNode unknown = new IIOMetadataNode("unknown"); IIOMetadataNode unknown = new IIOMetadataNode("unknown");
unknown.setAttribute("MarkerTag", Integer.toString(JPEG.APP13 & 0xFF)); unknown.setAttribute("MarkerTag", Integer.toString(JPEG.APP13 & 0xFF));
byte[] identfier = "Photoshop 3.0".getBytes(StandardCharsets.US_ASCII); byte[] identifier = "Photoshop 3.0".getBytes(StandardCharsets.US_ASCII);
byte[] data = new byte[identfier.length + 1 + pathResource.length]; byte[] data = new byte[identifier.length + 1 + pathResource.length];
System.arraycopy(identfier, 0, data, 0, identfier.length); System.arraycopy(identifier, 0, data, 0, identifier.length);
System.arraycopy(pathResource, 0, data, identfier.length + 1, pathResource.length); System.arraycopy(pathResource, 0, data, identifier.length + 1, pathResource.length);
unknown.setUserObject(data); unknown.setUserObject(data);
+1 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId> <artifactId>imageio</artifactId>
<version>3.6</version> <version>3.6.5-SNAPSHOT</version>
</parent> </parent>
<artifactId>imageio-core</artifactId> <artifactId>imageio-core</artifactId>
<name>TwelveMonkeys :: ImageIO :: Core</name> <name>TwelveMonkeys :: ImageIO :: Core</name>
@@ -314,12 +314,12 @@ public abstract class ImageReaderBase extends ImageReader {
long dimension = (long) destWidth * destHeight; long dimension = (long) destWidth * destHeight;
if (dimension > Integer.MAX_VALUE) { if (dimension > Integer.MAX_VALUE) {
throw new IllegalArgumentException(String.format("destination width * height > Integer.MAX_VALUE: %d", dimension)); throw new IIOException(String.format("destination width * height > Integer.MAX_VALUE: %d", dimension));
} }
long size = dimension * imageType.getSampleModel().getNumDataElements(); long size = dimension * imageType.getSampleModel().getNumDataElements();
if (size > Integer.MAX_VALUE) { if (size > Integer.MAX_VALUE) {
throw new IllegalArgumentException(String.format("destination width * height * samplesPerPixel > Integer.MAX_VALUE: %d", size)); throw new IIOException(String.format("destination width * height * samplesPerPixel > Integer.MAX_VALUE: %d", size));
} }
// Create a new image based on the type specifier // Create a new image based on the type specifier
@@ -440,6 +440,7 @@ public abstract class ImageReaderBase extends ImageReader {
static final String ZOOM_IN = "zoom-in"; static final String ZOOM_IN = "zoom-in";
static final String ZOOM_OUT = "zoom-out"; static final String ZOOM_OUT = "zoom-out";
static final String ZOOM_ACTUAL = "zoom-actual"; static final String ZOOM_ACTUAL = "zoom-actual";
static final String ZOOM_FIT = "zoom-fit";
private BufferedImage image; private BufferedImage image;
@@ -515,9 +516,20 @@ public abstract class ImageReaderBase extends ImageReader {
private void setupActions() { private void setupActions() {
// Mac weirdness... VK_MINUS/VK_PLUS seems to map to english key map always... // Mac weirdness... VK_MINUS/VK_PLUS seems to map to english key map always...
bindAction(new ZoomAction("Zoom in", 2), ZOOM_IN, KeyStroke.getKeyStroke('+'), KeyStroke.getKeyStroke(KeyEvent.VK_ADD, 0)); bindAction(new ZoomAction("Zoom in", 2), ZOOM_IN,
bindAction(new ZoomAction("Zoom out", .5), ZOOM_OUT, KeyStroke.getKeyStroke('-'), KeyStroke.getKeyStroke(KeyEvent.VK_SUBTRACT, 0)); KeyStroke.getKeyStroke('+'),
bindAction(new ZoomAction("Zoom actual"), ZOOM_ACTUAL, KeyStroke.getKeyStroke('0'), KeyStroke.getKeyStroke(KeyEvent.VK_0, 0)); KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
KeyStroke.getKeyStroke(KeyEvent.VK_ADD, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
bindAction(new ZoomAction("Zoom out", .5), ZOOM_OUT,
KeyStroke.getKeyStroke('-'),
KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
KeyStroke.getKeyStroke(KeyEvent.VK_SUBTRACT, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
bindAction(new ZoomAction("Zoom actual"), ZOOM_ACTUAL,
KeyStroke.getKeyStroke('0'),
KeyStroke.getKeyStroke(KeyEvent.VK_0, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
bindAction(new ZoomToFitAction("Zoom fit"), ZOOM_FIT,
KeyStroke.getKeyStroke('9'),
KeyStroke.getKeyStroke(KeyEvent.VK_9, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
bindAction(TransferHandler.getCopyAction(), (String) TransferHandler.getCopyAction().getValue(Action.NAME), KeyStroke.getKeyStroke(KeyEvent.VK_C, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); bindAction(TransferHandler.getCopyAction(), (String) TransferHandler.getCopyAction().getValue(Action.NAME), KeyStroke.getKeyStroke(KeyEvent.VK_C, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
bindAction(TransferHandler.getPasteAction(), (String) TransferHandler.getPasteAction().getValue(Action.NAME), KeyStroke.getKeyStroke(KeyEvent.VK_V, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); bindAction(TransferHandler.getPasteAction(), (String) TransferHandler.getPasteAction().getValue(Action.NAME), KeyStroke.getKeyStroke(KeyEvent.VK_V, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
@@ -534,6 +546,7 @@ public abstract class ImageReaderBase extends ImageReader {
private JPopupMenu createPopupMenu() { private JPopupMenu createPopupMenu() {
JPopupMenu popup = new JPopupMenu(); JPopupMenu popup = new JPopupMenu();
popup.add(getActionMap().get(ZOOM_FIT));
popup.add(getActionMap().get(ZOOM_ACTUAL)); popup.add(getActionMap().get(ZOOM_ACTUAL));
popup.add(getActionMap().get(ZOOM_IN)); popup.add(getActionMap().get(ZOOM_IN));
popup.add(getActionMap().get(ZOOM_OUT)); popup.add(getActionMap().get(ZOOM_OUT));
@@ -554,7 +567,7 @@ public abstract class ImageReaderBase extends ImageReader {
addCheckBoxItem(new ChangeBackgroundAction("Dark", Color.DARK_GRAY), background, group); addCheckBoxItem(new ChangeBackgroundAction("Dark", Color.DARK_GRAY), background, group);
addCheckBoxItem(new ChangeBackgroundAction("Black", Color.BLACK), background, group); addCheckBoxItem(new ChangeBackgroundAction("Black", Color.BLACK), background, group);
background.addSeparator(); background.addSeparator();
ChooseBackgroundAction chooseBackgroundAction = new ChooseBackgroundAction("Choose...", defaultBG != null ? defaultBG : Color.BLUE); ChooseBackgroundAction chooseBackgroundAction = new ChooseBackgroundAction("Choose...", defaultBG != null ? defaultBG : new Color(0xFF6600));
chooseBackgroundAction.putValue(Action.SELECTED_KEY, backgroundPaint == defaultBG); chooseBackgroundAction.putValue(Action.SELECTED_KEY, backgroundPaint == defaultBG);
addCheckBoxItem(chooseBackgroundAction, background, group); addCheckBoxItem(chooseBackgroundAction, background, group);
@@ -668,14 +681,41 @@ public abstract class ImageReaderBase extends ImageReader {
} }
else { else {
Icon current = getIcon(); Icon current = getIcon();
int w = (int) Math.max(Math.min(current.getIconWidth() * zoomFactor, image.getWidth() * 16), image.getWidth() / 16); int w = Math.max(Math.min((int) (current.getIconWidth() * zoomFactor), image.getWidth() * 16), image.getWidth() / 16);
int h = (int) Math.max(Math.min(current.getIconHeight() * zoomFactor, image.getHeight() * 16), image.getHeight() / 16); int h = Math.max(Math.min((int) (current.getIconHeight() * zoomFactor), image.getHeight() * 16), image.getHeight() / 16);
setIcon(new BufferedImageIcon(image, Math.max(w, 2), Math.max(h, 2), w > image.getWidth() || h > image.getHeight())); setIcon(new BufferedImageIcon(image, Math.max(w, 2), Math.max(h, 2), w > image.getWidth() || h > image.getHeight()));
} }
} }
} }
private class ZoomToFitAction extends ZoomAction {
public ZoomToFitAction(final String name) {
super(name, -1);
}
public void actionPerformed(final ActionEvent e) {
JComponent source = (JComponent) e.getSource();
if (source instanceof JMenuItem) {
JPopupMenu menu = (JPopupMenu) SwingUtilities.getAncestorOfClass(JPopupMenu.class, source);
source = (JComponent) menu.getInvoker();
}
Container container = SwingUtilities.getAncestorOfClass(JViewport.class, source);
double ratioX = container.getWidth() / (double) image.getWidth();
double ratioY = container.getHeight() / (double) image.getHeight();
double zoomFactor = Math.min(ratioX, ratioY);
int w = Math.max(Math.min((int) (image.getWidth() * zoomFactor), image.getWidth() * 16), image.getWidth() / 16);
int h = Math.max(Math.min((int) (image.getHeight() * zoomFactor), image.getHeight() * 16), image.getHeight() / 16);
setIcon(new BufferedImageIcon(image, w, h, zoomFactor > 1));
}
}
private static class ImageTransferable implements Transferable { private static class ImageTransferable implements Transferable {
private final BufferedImage image; private final BufferedImage image;
@@ -694,7 +734,7 @@ public abstract class ImageReaderBase extends ImageReader {
} }
@Override @Override
public Object getTransferData(final DataFlavor flavor) throws UnsupportedFlavorException, IOException { public Object getTransferData(final DataFlavor flavor) throws UnsupportedFlavorException {
if (isDataFlavorSupported(flavor)) { if (isDataFlavorSupported(flavor)) {
return image; return image;
} }
@@ -57,8 +57,10 @@ public final class BufferedImageInputStream extends ImageInputStreamImpl impleme
private ImageInputStream stream; private ImageInputStream stream;
private ByteBuffer buffer; private ByteBuffer buffer;
private ByteBuffer integralCache = ByteBuffer.allocate(8);
private final ByteBuffer integralCache = ByteBuffer.allocate(8);
private final byte[] integralCacheArray = integralCache.array();
public BufferedImageInputStream(final ImageInputStream pStream) throws IOException { public BufferedImageInputStream(final ImageInputStream pStream) throws IOException {
this(pStream, DEFAULT_BUFFER_SIZE); this(pStream, DEFAULT_BUFFER_SIZE);
@@ -97,10 +99,10 @@ public final class BufferedImageInputStream extends ImageInputStreamImpl impleme
if (!buffer.hasRemaining()) { if (!buffer.hasRemaining()) {
fillBuffer(); fillBuffer();
}
if (!buffer.hasRemaining()) { if (!buffer.hasRemaining()) {
return -1; return -1;
}
} }
bitOffset = 0; bitOffset = 0;
@@ -172,21 +174,21 @@ public final class BufferedImageInputStream extends ImageInputStreamImpl impleme
@Override @Override
public short readShort() throws IOException { public short readShort() throws IOException {
readFully(integralCache.array(), 0, 2); readFully(integralCacheArray, 0, 2);
return integralCache.getShort(0); return integralCache.getShort(0);
} }
@Override @Override
public int readInt() throws IOException { public int readInt() throws IOException {
readFully(integralCache.array(), 0, 4); readFully(integralCacheArray, 0, 4);
return integralCache.getInt(0); return integralCache.getInt(0);
} }
@Override @Override
public long readLong() throws IOException { public long readLong() throws IOException {
readFully(integralCache.array(), 0, 8); readFully(integralCacheArray, 0, 8);
return integralCache.getLong(0); return integralCache.getLong(0);
} }
@@ -253,6 +255,7 @@ public final class BufferedImageInputStream extends ImageInputStreamImpl impleme
} }
int val = buffer.get() & 0xff; int val = buffer.get() & 0xff;
streamPos++;
accum <<= 8; accum <<= 8;
accum |= val; accum |= val;
@@ -262,9 +265,7 @@ public final class BufferedImageInputStream extends ImageInputStreamImpl impleme
// Move byte position back if in the middle of a byte // Move byte position back if in the middle of a byte
if (newBitOffset != 0) { if (newBitOffset != 0) {
buffer.position(buffer.position() - 1); buffer.position(buffer.position() - 1);
} streamPos--;
else {
streamPos++;
} }
this.bitOffset = newBitOffset; this.bitOffset = newBitOffset;
@@ -279,26 +280,26 @@ public final class BufferedImageInputStream extends ImageInputStreamImpl impleme
} }
@Override @Override
public void seek(long pPosition) throws IOException { public void seek(long position) throws IOException {
checkClosed(); checkClosed();
bitOffset = 0; bitOffset = 0;
if (streamPos == pPosition) { if (streamPos == position) {
return; return;
} }
// Optimized to not invalidate buffer if new position is within current buffer // Optimized to not invalidate buffer if new position is within current buffer
long newBufferPos = buffer.position() + pPosition - streamPos; long newBufferPos = buffer.position() + position - streamPos;
if (newBufferPos >= 0 && newBufferPos <= buffer.limit()) { if (newBufferPos >= 0 && newBufferPos <= buffer.limit()) {
buffer.position((int) newBufferPos); buffer.position((int) newBufferPos);
} }
else { else {
// Will invalidate buffer // Will invalidate buffer
buffer.limit(0); buffer.limit(0);
stream.seek(pPosition); stream.seek(position);
} }
streamPos = pPosition; streamPos = position;
} }
@Override @Override
@@ -330,7 +331,9 @@ public final class BufferedImageInputStream extends ImageInputStreamImpl impleme
@Override @Override
public void close() throws IOException { public void close() throws IOException {
if (stream != null) { if (stream != null) {
//stream.close(); // TODO: FixMe: Need to close underlying stream here!
// For call sites that relies on not closing, we should instead not close the buffered stream.
// stream.close();
stream = null; stream = null;
buffer = null; buffer = null;
} }
@@ -30,10 +30,11 @@
package com.twelvemonkeys.imageio.stream; package com.twelvemonkeys.imageio.stream;
import com.twelvemonkeys.imageio.spi.ProviderInfo;
import javax.imageio.spi.ImageInputStreamSpi; import javax.imageio.spi.ImageInputStreamSpi;
import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.ImageInputStream;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.util.Locale; import java.util.Locale;
/** /**
@@ -47,10 +48,14 @@ import java.util.Locale;
public class ByteArrayImageInputStreamSpi extends ImageInputStreamSpi { public class ByteArrayImageInputStreamSpi extends ImageInputStreamSpi {
public ByteArrayImageInputStreamSpi() { public ByteArrayImageInputStreamSpi() {
super("TwelveMonkeys", "1.0 BETA", byte[].class); this(new StreamProviderInfo());
} }
public ImageInputStream createInputStreamInstance(Object pInput, boolean pUseCache, File pCacheDir) throws IOException { private ByteArrayImageInputStreamSpi(ProviderInfo providerInfo) {
super(providerInfo.getVendorName(), providerInfo.getVersion(), byte[].class);
}
public ImageInputStream createInputStreamInstance(Object pInput, boolean pUseCache, File pCacheDir) {
if (pInput instanceof byte[]) { if (pInput instanceof byte[]) {
return new ByteArrayImageInputStream((byte[]) pInput); return new ByteArrayImageInputStream((byte[]) pInput);
} }
@@ -0,0 +1,9 @@
package com.twelvemonkeys.imageio.stream;
import com.twelvemonkeys.imageio.spi.ProviderInfo;
final class StreamProviderInfo extends ProviderInfo {
StreamProviderInfo() {
super(StreamProviderInfo.class.getPackage());
}
}
@@ -30,6 +30,8 @@
package com.twelvemonkeys.imageio.stream; package com.twelvemonkeys.imageio.stream;
import com.twelvemonkeys.imageio.spi.ProviderInfo;
import javax.imageio.spi.ImageInputStreamSpi; import javax.imageio.spi.ImageInputStreamSpi;
import javax.imageio.stream.FileCacheImageInputStream; import javax.imageio.stream.FileCacheImageInputStream;
import javax.imageio.stream.FileImageInputStream; import javax.imageio.stream.FileImageInputStream;
@@ -53,7 +55,11 @@ import java.util.Locale;
// TODO: URI instead of URL? // TODO: URI instead of URL?
public class URLImageInputStreamSpi extends ImageInputStreamSpi { public class URLImageInputStreamSpi extends ImageInputStreamSpi {
public URLImageInputStreamSpi() { public URLImageInputStreamSpi() {
super("TwelveMonkeys", "1.0 BETA", URL.class); this(new StreamProviderInfo());
}
private URLImageInputStreamSpi(ProviderInfo providerInfo) {
super(providerInfo.getVendorName(), providerInfo.getVersion(), URL.class);
} }
// TODO: Create a URI or URLImageInputStream class, with a getUR[I|L] method, to allow for multiple file formats // TODO: Create a URI or URLImageInputStream class, with a getUR[I|L] method, to allow for multiple file formats
@@ -108,6 +114,11 @@ public class URLImageInputStreamSpi extends ImageInputStreamSpi {
} }
} }
@Override
public boolean canUseCacheFile() {
return true;
}
public String getDescription(final Locale pLocale) { public String getDescription(final Locale pLocale) {
return "Service provider that instantiates an ImageInputStream from a URL"; return "Service provider that instantiates an ImageInputStream from a URL";
} }
@@ -291,4 +291,21 @@ public final class IIOUtil {
System.arraycopy(srcRow, srcPos + x, destRow, destPos + x / samplePeriod, pixelStride); System.arraycopy(srcRow, srcPos + x, destRow, destPos + x / samplePeriod, pixelStride);
} }
} }
public static void subsampleRow(float[] srcRow, int srcPos, int srcWidth,
float[] destRow, int destPos,
int samplesPerPixel, int bitsPerSample, int samplePeriod) {
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");
Validate.isTrue(samplesPerPixel * bitsPerSample <= 32 || samplesPerPixel * bitsPerSample % 32 == 0,
"samplesPerPixel * bitsPerSample must be < 32 or a multiple of 32 ");
int pixelStride = bitsPerSample * samplesPerPixel / 32;
for (int x = 0; x < srcWidth * pixelStride; x += samplePeriod * pixelStride) {
// System.arraycopy should be intrinsic, but consider using direct array access for pixelStride == 1
System.arraycopy(srcRow, srcPos + x, destRow, destPos + x / samplePeriod, pixelStride);
}
}
} }
@@ -30,11 +30,11 @@
package com.twelvemonkeys.imageio; package com.twelvemonkeys.imageio;
import org.junit.Test; import static java.util.Collections.singleton;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import javax.imageio.IIOException;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageTypeSpecifier;
import java.awt.*; import java.awt.*;
import java.awt.color.ColorSpace; import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
@@ -44,8 +44,11 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import static java.util.Collections.singleton; import javax.imageio.IIOException;
import static org.junit.Assert.*; import javax.imageio.ImageReadParam;
import javax.imageio.ImageTypeSpecifier;
import org.junit.Test;
/** /**
* ImageReaderBaseTest * ImageReaderBaseTest
@@ -212,19 +215,19 @@ public class ImageReaderBaseTest {
assertEquals(TYPES.get(0).getBufferedImageType(), destination.getType()); assertEquals(TYPES.get(0).getBufferedImageType(), destination.getType());
} }
@Test(expected = IllegalArgumentException.class) @Test(expected = IIOException.class)
public void testGetDestinationParamDestinationExceedsIntegerMax() throws IIOException { public void testGetDestinationParamDestinationExceedsIntegerMax() throws IIOException {
ImageReadParam param = new ImageReadParam(); ImageReadParam param = new ImageReadParam();
param.setSourceRegion(new Rectangle(3 * Short.MAX_VALUE, 2 * Short.MAX_VALUE)); // 6 442 057 734 pixels param.setSourceRegion(new Rectangle(3 * Short.MAX_VALUE, 2 * Short.MAX_VALUE)); // 6 442 057 734 pixels
ImageReaderBase.getDestination(param, TYPES.iterator(), 6 * Short.MAX_VALUE, 4 * Short.MAX_VALUE); // 25 768 230 936 pixels ImageReaderBase.getDestination(param, TYPES.iterator(), 6 * Short.MAX_VALUE, 4 * Short.MAX_VALUE); // 25 768 230 936 pixels
} }
@Test(expected = IllegalArgumentException.class) @Test(expected = IIOException.class)
public void testGetDestinationDimensionExceedsIntegerMax() throws IIOException { public void testGetDestinationDimensionExceedsIntegerMax() throws IIOException {
ImageReaderBase.getDestination(null, TYPES.iterator(), 3 * Short.MAX_VALUE, 2 * Short.MAX_VALUE); // 6 442 057 734 pixels ImageReaderBase.getDestination(null, TYPES.iterator(), 3 * Short.MAX_VALUE, 2 * Short.MAX_VALUE); // 6 442 057 734 pixels
} }
@Test(expected = IllegalArgumentException.class) @Test(expected = IIOException.class)
public void testGetDestinationStorageExceedsIntegerMax() throws IIOException { public void testGetDestinationStorageExceedsIntegerMax() throws IIOException {
Set<ImageTypeSpecifier> byteTypes = singleton(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR)); Set<ImageTypeSpecifier> byteTypes = singleton(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR));
ImageReaderBase.getDestination(null, byteTypes.iterator(), Short.MAX_VALUE, Short.MAX_VALUE); // 1 073 676 289 pixels ImageReaderBase.getDestination(null, byteTypes.iterator(), Short.MAX_VALUE, Short.MAX_VALUE); // 1 073 676 289 pixels
@@ -35,6 +35,8 @@ import org.junit.Test;
import java.awt.image.*; import java.awt.image.*;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.*; import static org.junit.Assert.*;
public class DiscreteAlphaIndexColorModelTest { public class DiscreteAlphaIndexColorModelTest {
@@ -162,7 +164,7 @@ public class DiscreteAlphaIndexColorModelTest {
assertEquals(2, sampleModel.getHeight()); assertEquals(2, sampleModel.getHeight());
assertTrue(colorModel.isCompatibleSampleModel(sampleModel)); assertTrue(colorModel.isCompatibleSampleModel(sampleModel));
assertThat(sampleModel, CoreMatchers.is(PixelInterleavedSampleModel.class)); assertThat(sampleModel, instanceOf(PixelInterleavedSampleModel.class));
assertThat(sampleModel.getDataType(), CoreMatchers.equalTo(DataBuffer.TYPE_BYTE)); assertThat(sampleModel.getDataType(), CoreMatchers.equalTo(DataBuffer.TYPE_BYTE));
} }
@@ -180,7 +182,7 @@ public class DiscreteAlphaIndexColorModelTest {
assertEquals(2, sampleModel.getHeight()); assertEquals(2, sampleModel.getHeight());
assertTrue(colorModel.isCompatibleSampleModel(sampleModel)); assertTrue(colorModel.isCompatibleSampleModel(sampleModel));
assertThat(sampleModel, CoreMatchers.is(PixelInterleavedSampleModel.class)); assertThat(sampleModel, instanceOf(PixelInterleavedSampleModel.class));
assertThat(sampleModel.getDataType(), CoreMatchers.equalTo(DataBuffer.TYPE_USHORT)); assertThat(sampleModel.getDataType(), CoreMatchers.equalTo(DataBuffer.TYPE_USHORT));
} }
@@ -45,7 +45,7 @@ import static org.junit.Assert.*;
*/ */
public class ProviderInfoTest { public class ProviderInfoTest {
@Test @Test
public void testCreateNorma() { public void testCreateNormal() {
new ProviderInfo(Package.getPackage("java.util")); new ProviderInfo(Package.getPackage("java.util"));
} }
@@ -31,8 +31,8 @@
package com.twelvemonkeys.imageio.spi; package com.twelvemonkeys.imageio.spi;
import org.hamcrest.Description; import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
import org.junit.Test; import org.junit.Test;
import org.junit.internal.matchers.TypeSafeMatcher;
import javax.imageio.ImageReader; import javax.imageio.ImageReader;
import javax.imageio.ImageWriter; import javax.imageio.ImageWriter;
@@ -42,6 +42,7 @@ import javax.imageio.spi.ImageWriterSpi;
import java.util.List; import java.util.List;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.*; import static org.junit.Assert.*;
/** /**
@@ -62,52 +63,52 @@ public abstract class ReaderWriterProviderInfoTest {
} }
@Test @Test
public void readerClassName() throws Exception { public void readerClassName() {
assertClassExists(providerInfo.readerClassName(), ImageReader.class); assertClassExists(providerInfo.readerClassName(), ImageReader.class);
} }
@Test @Test
public void readerSpiClassNames() throws Exception { public void readerSpiClassNames() {
assertClassesExist(providerInfo.readerSpiClassNames(), ImageReaderSpi.class); assertClassesExist(providerInfo.readerSpiClassNames(), ImageReaderSpi.class);
} }
@Test @Test
public void inputTypes() throws Exception { public void inputTypes() {
assertNotNull(providerInfo.inputTypes()); assertNotNull(providerInfo.inputTypes());
} }
@Test @Test
public void writerClassName() throws Exception { public void writerClassName() {
assertClassExists(providerInfo.writerClassName(), ImageWriter.class); assertClassExists(providerInfo.writerClassName(), ImageWriter.class);
} }
@Test @Test
public void writerSpiClassNames() throws Exception { public void writerSpiClassNames() {
assertClassesExist(providerInfo.writerSpiClassNames(), ImageWriterSpi.class); assertClassesExist(providerInfo.writerSpiClassNames(), ImageWriterSpi.class);
} }
@Test @Test
public void outputTypes() throws Exception { public void outputTypes() {
assertNotNull(providerInfo.outputTypes()); assertNotNull(providerInfo.outputTypes());
} }
@Test @Test
public void nativeStreamMetadataFormatClassName() throws Exception { public void nativeStreamMetadataFormatClassName() {
assertClassExists(providerInfo.nativeStreamMetadataFormatClassName(), IIOMetadataFormat.class); assertClassExists(providerInfo.nativeStreamMetadataFormatClassName(), IIOMetadataFormat.class);
} }
@Test @Test
public void extraStreamMetadataFormatClassNames() throws Exception { public void extraStreamMetadataFormatClassNames() {
assertClassesExist(providerInfo.extraStreamMetadataFormatClassNames(), IIOMetadataFormat.class); assertClassesExist(providerInfo.extraStreamMetadataFormatClassNames(), IIOMetadataFormat.class);
} }
@Test @Test
public void nativeImageMetadataFormatClassName() throws Exception { public void nativeImageMetadataFormatClassName() {
assertClassExists(providerInfo.nativeImageMetadataFormatClassName(), IIOMetadataFormat.class); assertClassExists(providerInfo.nativeImageMetadataFormatClassName(), IIOMetadataFormat.class);
} }
@Test @Test
public void extraImageMetadataFormatClassNames() throws Exception { public void extraImageMetadataFormatClassNames() {
assertClassesExist(providerInfo.extraImageMetadataFormatClassNames(), IIOMetadataFormat.class); assertClassesExist(providerInfo.extraImageMetadataFormatClassNames(), IIOMetadataFormat.class);
} }
@@ -115,7 +116,7 @@ public abstract class ReaderWriterProviderInfoTest {
public void formatNames() { public void formatNames() {
String[] names = providerInfo.formatNames(); String[] names = providerInfo.formatNames();
assertNotNull(names); assertNotNull(names);
assertFalse(names.length == 0); assertNotEquals(0, names.length);
List<String> list = asList(names); List<String> list = asList(names);
@@ -132,7 +133,7 @@ public abstract class ReaderWriterProviderInfoTest {
public void suffixes() { public void suffixes() {
String[] suffixes = providerInfo.suffixes(); String[] suffixes = providerInfo.suffixes();
assertNotNull(suffixes); assertNotNull(suffixes);
assertFalse(suffixes.length == 0); assertNotEquals(0, suffixes.length);
for (String suffix : suffixes) { for (String suffix : suffixes) {
assertNotNull(suffix); assertNotNull(suffix);
@@ -144,7 +145,7 @@ public abstract class ReaderWriterProviderInfoTest {
public void mimeTypes() { public void mimeTypes() {
String[] mimeTypes = providerInfo.mimeTypes(); String[] mimeTypes = providerInfo.mimeTypes();
assertNotNull(mimeTypes); assertNotNull(mimeTypes);
assertFalse(mimeTypes.length == 0); assertNotEquals(0, mimeTypes.length);
for (String mimeType : mimeTypes) { for (String mimeType : mimeTypes) {
assertNotNull(mimeType); assertNotNull(mimeType);
@@ -32,16 +32,19 @@ package com.twelvemonkeys.imageio.stream;
import com.twelvemonkeys.io.ole2.CompoundDocument; import com.twelvemonkeys.io.ole2.CompoundDocument;
import com.twelvemonkeys.io.ole2.Entry; import com.twelvemonkeys.io.ole2.Entry;
import org.junit.Test; import org.junit.Test;
import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.MemoryCacheImageInputStream; import javax.imageio.stream.MemoryCacheImageInputStream;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.util.Random; import java.util.Random;
import static java.util.Arrays.fill; import static java.util.Arrays.fill;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
/** /**
* BufferedImageInputStreamTest * BufferedImageInputStreamTest
@@ -72,6 +75,257 @@ public class BufferedImageInputStreamTest {
} }
} }
@Test
public void testReadBit() throws IOException {
byte[] bytes = new byte[] {(byte) 0xF0, (byte) 0x0F};
// Create wrapper stream
BufferedImageInputStream stream = new BufferedImageInputStream(new ByteArrayImageInputStream(bytes));
// Read all bits
assertEquals(1, stream.readBit());
assertEquals(1, stream.getBitOffset());
assertEquals(0, stream.getStreamPosition());
assertEquals(1, stream.readBit());
assertEquals(2, stream.getBitOffset());
assertEquals(0, stream.getStreamPosition());
assertEquals(1, stream.readBit());
assertEquals(3, stream.getBitOffset());
assertEquals(0, stream.getStreamPosition());
assertEquals(1, stream.readBit());
assertEquals(4, stream.getBitOffset());
assertEquals(0, stream.getStreamPosition());
assertEquals(0, stream.readBit());
assertEquals(5, stream.getBitOffset());
assertEquals(0, stream.getStreamPosition());
assertEquals(0, stream.readBit());
assertEquals(6, stream.getBitOffset());
assertEquals(0, stream.getStreamPosition());
assertEquals(0, stream.readBit());
assertEquals(7, stream.getBitOffset());
assertEquals(0, stream.getStreamPosition());
assertEquals(0, stream.readBit()); // last bit
assertEquals(0, stream.getBitOffset());
assertEquals(1, stream.getStreamPosition());
// Full reset, read same sequence again
stream.seek(0);
assertEquals(0, stream.getBitOffset());
assertEquals(0, stream.getStreamPosition());
assertEquals(1, stream.readBit());
assertEquals(1, stream.readBit());
assertEquals(1, stream.readBit());
assertEquals(1, stream.readBit());
assertEquals(0, stream.readBit());
assertEquals(0, stream.readBit());
assertEquals(0, stream.readBit());
assertEquals(0, stream.readBit());
assertEquals(0, stream.getBitOffset());
assertEquals(1, stream.getStreamPosition());
// Full reset, read partial
stream.seek(0);
assertEquals(1, stream.readBit());
assertEquals(1, stream.readBit());
// Byte reset, read same sequence again
stream.setBitOffset(0);
assertEquals(1, stream.readBit());
assertEquals(1, stream.readBit());
assertEquals(1, stream.readBit());
assertEquals(1, stream.readBit());
assertEquals(0, stream.readBit());
// Byte reset, read partial sequence again
stream.setBitOffset(3);
assertEquals(1, stream.readBit());
assertEquals(0, stream.readBit());
assertEquals(0, stream.getStreamPosition());
// Byte reset, read partial sequence again
stream.setBitOffset(6);
assertEquals(0, stream.readBit());
assertEquals(0, stream.readBit());
assertEquals(1, stream.getStreamPosition());
// Read all bits, second byte
assertEquals(0, stream.readBit());
assertEquals(1, stream.getBitOffset());
assertEquals(1, stream.getStreamPosition());
assertEquals(0, stream.readBit());
assertEquals(2, stream.getBitOffset());
assertEquals(1, stream.getStreamPosition());
assertEquals(0, stream.readBit());
assertEquals(3, stream.getBitOffset());
assertEquals(1, stream.getStreamPosition());
assertEquals(0, stream.readBit());
assertEquals(4, stream.getBitOffset());
assertEquals(1, stream.getStreamPosition());
assertEquals(1, stream.readBit());
assertEquals(5, stream.getBitOffset());
assertEquals(1, stream.getStreamPosition());
assertEquals(1, stream.readBit());
assertEquals(6, stream.getBitOffset());
assertEquals(1, stream.getStreamPosition());
assertEquals(1, stream.readBit());
assertEquals(7, stream.getBitOffset());
assertEquals(1, stream.getStreamPosition());
assertEquals(1, stream.readBit()); // last bit
assertEquals(0, stream.getBitOffset());
assertEquals(2, stream.getStreamPosition());
}
@Test
public void testReadBits() throws IOException {
byte[] bytes = new byte[] {(byte) 0xF0, (byte) 0xCC, (byte) 0xAA};
// Create wrapper stream
BufferedImageInputStream stream = new BufferedImageInputStream(new ByteArrayImageInputStream(bytes));
// Read all bits, first byte
assertEquals(3, stream.readBits(2));
assertEquals(2, stream.getBitOffset());
assertEquals(0, stream.getStreamPosition());
assertEquals(3, stream.readBits(2));
assertEquals(4, stream.getBitOffset());
assertEquals(0, stream.getStreamPosition());
assertEquals(0, stream.readBits(2));
assertEquals(6, stream.getBitOffset());
assertEquals(0, stream.getStreamPosition());
assertEquals(0, stream.readBits(2));
assertEquals(0, stream.getBitOffset());
assertEquals(1, stream.getStreamPosition());
// Read all bits, second byte
assertEquals(3, stream.readBits(2));
assertEquals(2, stream.getBitOffset());
assertEquals(1, stream.getStreamPosition());
assertEquals(0, stream.readBits(2));
assertEquals(4, stream.getBitOffset());
assertEquals(1, stream.getStreamPosition());
assertEquals(3, stream.readBits(2));
assertEquals(6, stream.getBitOffset());
assertEquals(1, stream.getStreamPosition());
assertEquals(0, stream.readBits(2));
assertEquals(0, stream.getBitOffset());
assertEquals(2, stream.getStreamPosition());
// Read all bits, third byte
assertEquals(2, stream.readBits(2));
assertEquals(2, stream.getBitOffset());
assertEquals(2, stream.getStreamPosition());
assertEquals(2, stream.readBits(2));
assertEquals(4, stream.getBitOffset());
assertEquals(2, stream.getStreamPosition());
assertEquals(2, stream.readBits(2));
assertEquals(6, stream.getBitOffset());
assertEquals(2, stream.getStreamPosition());
assertEquals(2, stream.readBits(2));
assertEquals(0, stream.getBitOffset());
assertEquals(3, stream.getStreamPosition());
// Full reset, read same sequence again
stream.seek(0);
assertEquals(0, stream.getBitOffset());
assertEquals(0, stream.getStreamPosition());
// Read all bits, increasing size
assertEquals(7, stream.readBits(3)); // 111
assertEquals(3, stream.getBitOffset());
assertEquals(0, stream.getStreamPosition());
assertEquals(8, stream.readBits(4)); // 1000
assertEquals(7, stream.getBitOffset());
assertEquals(0, stream.getStreamPosition());
assertEquals(12, stream.readBits(5)); // 01100
assertEquals(4, stream.getBitOffset());
assertEquals(1, stream.getStreamPosition());
assertEquals(50, stream.readBits(6)); // 110010
assertEquals(2, stream.getBitOffset());
assertEquals(2, stream.getStreamPosition());
assertEquals(42, stream.readBits(6)); // 101010
assertEquals(0, stream.getBitOffset());
assertEquals(3, stream.getStreamPosition());
// Full reset, read same sequence again
stream.seek(0);
assertEquals(0, stream.getBitOffset());
assertEquals(0, stream.getStreamPosition());
// Read all bits multi-byte
assertEquals(0xF0C, stream.readBits(12)); // 111100001100
assertEquals(4, stream.getBitOffset());
assertEquals(1, stream.getStreamPosition());
assertEquals(0xCAA, stream.readBits(12)); // 110010101010
assertEquals(0, stream.getBitOffset());
assertEquals(3, stream.getStreamPosition());
// Full reset, read same sequence again, all bits in one go
stream.seek(0);
assertEquals(0, stream.getBitOffset());
assertEquals(0, stream.getStreamPosition());
assertEquals(0xF0CCAA, stream.readBits(24));
}
@Test
public void testReadBitsRandom() throws IOException {
long value = random.nextLong();
byte[] bytes = new byte[8];
ByteBuffer.wrap(bytes).putLong(value);
// Create wrapper stream
BufferedImageInputStream stream = new BufferedImageInputStream(new ByteArrayImageInputStream(bytes));
for (int i = 1; i < 64; i++) {
stream.seek(0);
assertEquals(i + " bits differ", value >>> (64L - i), stream.readBits(i));
}
}
@Test
public void testClose() throws IOException {
// Create wrapper stream
ImageInputStream mock = mock(ImageInputStream.class);
BufferedImageInputStream stream = new BufferedImageInputStream(mock);
stream.close();
verify(mock, never()).close();
}
// TODO: Write other tests // TODO: Write other tests
// TODO: Create test that exposes read += -1 (eof) bug // TODO: Create test that exposes read += -1 (eof) bug
@@ -0,0 +1,16 @@
package com.twelvemonkeys.imageio.stream;
import javax.imageio.spi.ImageInputStreamSpi;
public class ByteArrayImageInputStreamSpiTest extends ImageInputStreamSpiTest<byte[]> {
@Override
protected ImageInputStreamSpi createProvider() {
return new ByteArrayImageInputStreamSpi();
}
@Override
protected byte[] createInput() {
return new byte[0];
}
}
@@ -0,0 +1,84 @@
package com.twelvemonkeys.imageio.stream;
import org.junit.Test;
import javax.imageio.ImageIO;
import javax.imageio.spi.ImageInputStreamSpi;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.util.Locale;
import static org.junit.Assert.*;
abstract class ImageInputStreamSpiTest<T> {
private final ImageInputStreamSpi provider = createProvider();
@SuppressWarnings("unchecked")
private final Class<T> inputClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
protected abstract ImageInputStreamSpi createProvider();
protected abstract T createInput();
@Test
public void testInputClass() {
assertEquals(inputClass, provider.getInputClass());
}
@Test
public void testVendorName() {
assertNotNull(provider.getVendorName());
assertEquals("TwelveMonkeys", provider.getVendorName());
}
@Test
public void testVersion() {
assertNotNull(provider.getVersion());
}
@Test
public void testDescription() {
assertNotNull(provider.getDescription(null));
assertNotNull(provider.getDescription(Locale.ENGLISH));
}
@Test(expected = IllegalArgumentException.class)
public void createNull() throws IOException {
provider.createInputStreamInstance(null);
}
@Test(expected = IllegalArgumentException.class)
public void createNullCached() throws IOException {
provider.createInputStreamInstance(null, true, ImageIO.getCacheDirectory());
}
@Test
public void createCachedNullCache() throws IOException {
try {
provider.createInputStreamInstance(createInput(), true, null);
}
catch (IllegalArgumentException expected) {
// All good
assertFalse(provider.needsCacheFile());
}
}
@Test
public void create() throws IOException {
assertNotNull(provider.createInputStreamInstance(createInput()));
}
@Test
public void createCached() throws IOException {
if (provider.canUseCacheFile()) {
assertNotNull(provider.createInputStreamInstance(createInput(), true, ImageIO.getCacheDirectory()));
}
}
@Test
public void createNonCached() throws IOException {
if (!provider.needsCacheFile()) {
assertNotNull(provider.createInputStreamInstance(createInput(), false, ImageIO.getCacheDirectory()));
}
}
}
@@ -0,0 +1,23 @@
package com.twelvemonkeys.imageio.stream;
import com.twelvemonkeys.imageio.spi.ProviderInfo;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
public class StreamProviderInfoTest {
private final ProviderInfo providerInfo = new StreamProviderInfo();
@Test
public void testVendorName() {
assertNotNull(providerInfo.getVendorName());
assertEquals("TwelveMonkeys", providerInfo.getVendorName());
}
@Test
public void testVersion() {
assertNotNull(providerInfo.getVersion());
}
}
@@ -0,0 +1,16 @@
package com.twelvemonkeys.imageio.stream;
import javax.imageio.spi.ImageInputStreamSpi;
import java.net.URL;
public class URLImageInputStreamSpiTest extends ImageInputStreamSpiTest<URL> {
@Override
protected ImageInputStreamSpi createProvider() {
return new URLImageInputStreamSpi();
}
@Override
protected URL createInput() {
return getClass().getResource("/empty-stream.txt");
}
}
@@ -31,6 +31,7 @@
package com.twelvemonkeys.imageio.util; package com.twelvemonkeys.imageio.util;
import com.twelvemonkeys.imageio.stream.URLImageInputStreamSpi; import com.twelvemonkeys.imageio.stream.URLImageInputStreamSpi;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.mockito.InOrder; import org.mockito.InOrder;
@@ -49,6 +50,7 @@ import java.awt.image.RenderedImage;
import java.awt.image.SampleModel; import java.awt.image.SampleModel;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@@ -69,27 +71,26 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
// TODO: Should we really test if the provider is installed? // TODO: Should we really test if the provider is installed?
// - Pro: Tests the META-INF/services config // - Pro: Tests the META-INF/services config
// - Con: Not all providers should be installed at runtime... // - Con: Not all providers should be installed at runtime...
// TODO: Create own subclass for testing the Spis?
static { static {
IIORegistry.getDefaultInstance().registerServiceProvider(new URLImageInputStreamSpi()); IIORegistry.getDefaultInstance().registerServiceProvider(new URLImageInputStreamSpi());
ImageIO.setUseCache(false); ImageIO.setUseCache(false);
} }
protected abstract List<TestData> getTestData(); @SuppressWarnings("unchecked")
private final Class<T> readerClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
protected final ImageReaderSpi provider = createProvider();
protected abstract ImageReaderSpi createProvider(); protected abstract ImageReaderSpi createProvider();
protected abstract Class<T> getReaderClass(); protected final T createReader() throws IOException {
return readerClass.cast(provider.createReaderInstance(null));
protected T createReader() {
try {
return getReaderClass().newInstance();
}
catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
} }
protected abstract List<TestData> getTestData();
protected abstract List<String> getFormatNames(); protected abstract List<String> getFormatNames();
protected abstract List<String> getSuffixes(); protected abstract List<String> getSuffixes();
@@ -101,9 +102,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
protected static void failBecause(String message, Throwable exception) { protected static void failBecause(String message, Throwable exception) {
AssertionError error = new AssertionError(message); throw new AssertionError(message, exception);
error.initCause(exception);
throw error;
} }
protected void assertProviderInstalledForName(final String pFormat, final Class<? extends ImageReader> pReaderClass) { protected void assertProviderInstalledForName(final String pFormat, final Class<? extends ImageReader> pReaderClass) {
@@ -123,17 +122,20 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
boolean found = false; boolean found = false;
while (pReaders.hasNext()) { while (pReaders.hasNext()) {
ImageReader reader = pReaders.next(); ImageReader reader = pReaders.next();
if (reader.getClass() == pReaderClass) { if (reader.getClass() == pReaderClass && isOurProvider(reader.getOriginatingProvider())) {
found = true; found = true;
} }
} }
assertTrue(String.format("%s not installed for %s", pReaderClass.getSimpleName(), pFormat), found); assertTrue(String.format("%s not provided by %s for '%s'", pReaderClass.getSimpleName(), provider.getClass().getSimpleName(), pFormat), found);
}
private boolean isOurProvider(final ImageReaderSpi spi) {
return provider.getClass().isInstance(spi);
} }
@Test @Test
public void testProviderInstalledForNames() { public void testProviderInstalledForNames() {
Class<? extends ImageReader> readerClass = getReaderClass();
for (String name : getFormatNames()) { for (String name : getFormatNames()) {
assertProviderInstalledForName(name, readerClass); assertProviderInstalledForName(name, readerClass);
} }
@@ -141,7 +143,6 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
@Test @Test
public void testProviderInstalledForSuffixes() { public void testProviderInstalledForSuffixes() {
Class<? extends ImageReader> readerClass = getReaderClass();
for (String suffix : getSuffixes()) { for (String suffix : getSuffixes()) {
assertProviderInstalledForSuffix(suffix, readerClass); assertProviderInstalledForSuffix(suffix, readerClass);
} }
@@ -149,7 +150,6 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
@Test @Test
public void testProviderInstalledForMIMETypes() { public void testProviderInstalledForMIMETypes() {
Class<? extends ImageReader> readerClass = getReaderClass();
for (String type : getMIMETypes()) { for (String type : getMIMETypes()) {
assertProviderInstalledForMIMEType(type, readerClass); assertProviderInstalledForMIMEType(type, readerClass);
} }
@@ -159,7 +159,6 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
public void testProviderCanRead() throws IOException { public void testProviderCanRead() throws IOException {
List<TestData> testData = getTestData(); List<TestData> testData = getTestData();
ImageReaderSpi provider = createProvider();
for (TestData data : testData) { for (TestData data : testData) {
ImageInputStream stream = data.getInputStream(); ImageInputStream stream = data.getInputStream();
assertNotNull(stream); assertNotNull(stream);
@@ -172,7 +171,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
boolean canRead = false; boolean canRead = false;
try { try {
canRead = createProvider().canDecodeInput(null); canRead = provider.canDecodeInput(null);
} }
catch (IllegalArgumentException ignore) { catch (IllegalArgumentException ignore) {
} }
@@ -187,7 +186,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testSetInput() { public void testSetInput() throws IOException {
// Should just pass with no exceptions // Should just pass with no exceptions
ImageReader reader = createReader(); ImageReader reader = createReader();
assertNotNull(reader); assertNotNull(reader);
@@ -200,7 +199,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testSetInputNull() { public void testSetInputNull() throws IOException {
// Should just pass with no exceptions // Should just pass with no exceptions
ImageReader reader = createReader(); ImageReader reader = createReader();
assertNotNull(reader); assertNotNull(reader);
@@ -209,7 +208,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testRead() { public void testRead() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
for (TestData data : getTestData()) { for (TestData data : getTestData()) {
@@ -243,7 +242,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testReadIndexNegative() { public void testReadIndexNegative() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
TestData data = getTestData().get(0); TestData data = getTestData().get(0);
reader.setInput(data.getInputStream()); reader.setInput(data.getInputStream());
@@ -264,7 +263,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testReadIndexOutOfBounds() { public void testReadIndexOutOfBounds() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
TestData data = getTestData().get(0); TestData data = getTestData().get(0);
reader.setInput(data.getInputStream()); reader.setInput(data.getInputStream());
@@ -285,7 +284,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testReadNoInput() { public void testReadNoInput() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
// Do not set input // Do not set input
@@ -325,7 +324,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testReadIndexNegativeWithParam() { public void testReadIndexNegativeWithParam() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
TestData data = getTestData().get(0); TestData data = getTestData().get(0);
reader.setInput(data.getInputStream()); reader.setInput(data.getInputStream());
@@ -347,7 +346,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testReadIndexOutOfBoundsWithParam() { public void testReadIndexOutOfBoundsWithParam() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
TestData data = getTestData().get(0); TestData data = getTestData().get(0);
reader.setInput(data.getInputStream()); reader.setInput(data.getInputStream());
@@ -369,7 +368,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testReadNoInputWithParam() { public void testReadNoInputWithParam() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
// Do not set input // Do not set input
@@ -390,7 +389,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testReadWithNewParam() { public void testReadWithNewParam() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
TestData data = getTestData().get(0); TestData data = getTestData().get(0);
reader.setInput(data.getInputStream()); reader.setInput(data.getInputStream());
@@ -411,7 +410,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testReadWithDefaultParam() { public void testReadWithDefaultParam() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
TestData data = getTestData().get(0); TestData data = getTestData().get(0);
reader.setInput(data.getInputStream()); reader.setInput(data.getInputStream());
@@ -432,7 +431,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testReadWithNullParam() { public void testReadWithNullParam() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
TestData data = getTestData().get(0); TestData data = getTestData().get(0);
reader.setInput(data.getInputStream()); reader.setInput(data.getInputStream());
@@ -453,7 +452,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testReadWithSizeParam() { public void testReadWithSizeParam() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
TestData data = getTestData().get(0); TestData data = getTestData().get(0);
reader.setInput(data.getInputStream()); reader.setInput(data.getInputStream());
@@ -479,7 +478,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testReadWithSubsampleParamDimensions() { public void testReadWithSubsampleParamDimensions() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
TestData data = getTestData().get(0); TestData data = getTestData().get(0);
reader.setInput(data.getInputStream()); reader.setInput(data.getInputStream());
@@ -530,6 +529,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
// TODO: Subsample all test data // TODO: Subsample all test data
// TODO: Subsample with varying ratios and offsets // TODO: Subsample with varying ratios and offsets
@SuppressWarnings("SameParameterValue")
protected final void assertSubsampledImageDataEquals(String message, BufferedImage expected, BufferedImage actual, ImageReadParam param) throws IOException { protected final void assertSubsampledImageDataEquals(String message, BufferedImage expected, BufferedImage actual, ImageReadParam param) throws IOException {
assertNotNull("Expected image was null", expected); assertNotNull("Expected image was null", expected);
assertNotNull("Actual image was null!", actual); assertNotNull("Actual image was null!", actual);
@@ -553,10 +553,10 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
int actualRGB = actual.getRGB(x, y); int actualRGB = actual.getRGB(x, y);
try { try {
assertEquals(String.format("%s alpha at (%d, %d)", message, x, y), (expectedRGB >>> 24) & 0xff, (actualRGB >>> 24) & 0xff, 5); assertEquals((expectedRGB >>> 24) & 0xff, (actualRGB >>> 24) & 0xff, 5);
assertEquals(String.format("%s red at (%d, %d)", message, x, y), (expectedRGB >> 16) & 0xff, (actualRGB >> 16) & 0xff, 5); assertEquals((expectedRGB >> 16) & 0xff, (actualRGB >> 16) & 0xff, 5);
assertEquals(String.format("%s green at (%d, %d)", message, x, y), (expectedRGB >> 8) & 0xff, (actualRGB >> 8) & 0xff, 5); assertEquals((expectedRGB >> 8) & 0xff, (actualRGB >> 8) & 0xff, 5);
assertEquals(String.format("%s blue at (%d, %d)", message, x, y), expectedRGB & 0xff, actualRGB & 0xff, 5); assertEquals(expectedRGB & 0xff, actualRGB & 0xff, 5);
} }
catch (AssertionError e) { catch (AssertionError e) {
File tempExpected = File.createTempFile("junit-expected-", ".png"); File tempExpected = File.createTempFile("junit-expected-", ".png");
@@ -566,7 +566,6 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
System.err.println("tempActual.getAbsolutePath(): " + tempActual.getAbsolutePath()); System.err.println("tempActual.getAbsolutePath(): " + tempActual.getAbsolutePath());
ImageIO.write(actual, "PNG", tempActual); ImageIO.write(actual, "PNG", tempActual);
assertEquals(String.format("%s ARGB at (%d, %d)", message, x, y), String.format("#%08x", expectedRGB), String.format("#%08x", actualRGB)); assertEquals(String.format("%s ARGB at (%d, %d)", message, x, y), String.format("#%08x", expectedRGB), String.format("#%08x", actualRGB));
} }
} }
@@ -595,7 +594,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testReadWithSourceRegionParam() { public void testReadWithSourceRegionParam() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
TestData data = getTestData().get(0); TestData data = getTestData().get(0);
reader.setInput(data.getInputStream()); reader.setInput(data.getInputStream());
@@ -626,40 +625,53 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
protected void assertReadWithSourceRegionParamEqualImage(final Rectangle r, final TestData data, final int imageIndex) throws IOException { protected void assertReadWithSourceRegionParamEqualImage(final Rectangle r, final TestData data, final int imageIndex) throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
reader.setInput(data.getInputStream()); try (ImageInputStream inputStream = data.getInputStream()) {
ImageReadParam param = reader.getDefaultReadParam(); reader.setInput(inputStream);
ImageReadParam param = reader.getDefaultReadParam();
// Read full image and get sub image for comparison // Read full image and get sub image for comparison
final BufferedImage roi = reader.read(imageIndex, param).getSubimage(r.x, r.y, r.width, r.height); BufferedImage original = reader.read(imageIndex, param);
final BufferedImage roi = original.getSubimage(r.x, r.y, r.width, r.height);
param.setSourceRegion(r); param.setSourceRegion(r);
final BufferedImage image = reader.read(imageIndex, param); final BufferedImage image = reader.read(imageIndex, param);
// try { assertNotNull("Image was null!", image);
// SwingUtilities.invokeAndWait(new Runnable() { assertEquals("Read image has wrong width: " + image.getWidth(), r.width, image.getWidth());
// public void run() { assertEquals("Read image has wrong height: " + image.getHeight(), r.height, image.getHeight());
// JPanel panel = new JPanel(new FlowLayout());
// panel.add(new JLabel(new BufferedImageIcon(roi, r.width * 10, r.height * 10, true)));
// panel.add(new JLabel(new BufferedImageIcon(image, r.width * 10, r.height * 10, true)));
// JOptionPane.showConfirmDialog(null, panel);
// }
// });
// }
// catch (Exception e) {
// throw new RuntimeException(e);
// }
assertNotNull("Image was null!", image); try {
assertEquals("Read image has wrong width: " + image.getWidth(), r.width, image.getWidth()); assertImageDataEquals("Images differ", roi, image);
assertEquals("Read image has wrong height: " + image.getHeight(), r.height, image.getHeight()); }
assertImageDataEquals("Images differ", roi, image); catch (AssertionError e) {
File tempExpected = File.createTempFile("junit-expected-", ".png");
System.err.println("tempExpected.getAbsolutePath(): " + tempExpected.getAbsolutePath());
reader.dispose(); Graphics2D graphics = original.createGraphics();
try {
graphics.setColor(Color.RED);
graphics.draw(r);
}
finally {
graphics.dispose();
}
ImageIO.write(original, "PNG", tempExpected);
File tempActual = File.createTempFile("junit-actual-", ".png");
System.err.println("tempActual.getAbsolutePath(): " + tempActual.getAbsolutePath());
ImageIO.write(image, "PNG", tempActual);
throw e;
}
}
finally {
reader.dispose();
}
} }
@Test @Test
public void testReadWithSizeAndSourceRegionParam() { public void testReadWithSizeAndSourceRegionParam() throws IOException {
// TODO: Is this test correct??? // TODO: Is this test correct???
ImageReader reader = createReader(); ImageReader reader = createReader();
TestData data = getTestData().get(0); TestData data = getTestData().get(0);
@@ -690,7 +702,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testReadWithSubsampleAndSourceRegionParam() { public void testReadWithSubsampleAndSourceRegionParam() throws IOException {
// NOTE: The "standard" (com.sun.imageio.plugin.*) ImageReaders pass // NOTE: The "standard" (com.sun.imageio.plugin.*) ImageReaders pass
// this test, so the test should be correct... // this test, so the test should be correct...
ImageReader reader = createReader(); ImageReader reader = createReader();
@@ -716,7 +728,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testReadAsRenderedImageIndexNegative() { public void testReadAsRenderedImageIndexNegative() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
TestData data = getTestData().get(0); TestData data = getTestData().get(0);
reader.setInput(data.getInputStream()); reader.setInput(data.getInputStream());
@@ -739,7 +751,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testReadAsRenderedImageIndexOutOfBounds() throws IIOException { public void testReadAsRenderedImageIndexOutOfBounds() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
TestData data = getTestData().get(0); TestData data = getTestData().get(0);
reader.setInput(data.getInputStream()); reader.setInput(data.getInputStream());
@@ -766,7 +778,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testReadAsRenderedImageNoInput() { public void testReadAsRenderedImageNoInput() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
// Do not set input // Do not set input
@@ -788,7 +800,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testReadAsRenderedImage() { public void testReadAsRenderedImage() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
TestData data = getTestData().get(0); TestData data = getTestData().get(0);
reader.setInput(data.getInputStream()); reader.setInput(data.getInputStream());
@@ -811,7 +823,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testReadAsRenderedImageWithDefaultParam() { public void testReadAsRenderedImageWithDefaultParam() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
TestData data = getTestData().get(0); TestData data = getTestData().get(0);
reader.setInput(data.getInputStream()); reader.setInput(data.getInputStream());
@@ -834,7 +846,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testGetDefaultReadParam() { public void testGetDefaultReadParam() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
ImageReadParam param = reader.getDefaultReadParam(); ImageReadParam param = reader.getDefaultReadParam();
assertNotNull(param); assertNotNull(param);
@@ -842,7 +854,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testGetFormatName() { public void testGetFormatName() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
TestData data = getTestData().get(0); TestData data = getTestData().get(0);
reader.setInput(data.getInputStream()); reader.setInput(data.getInputStream());
@@ -858,7 +870,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testGetMinIndex() { public void testGetMinIndex() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
TestData data = getTestData().get(0); TestData data = getTestData().get(0);
reader.setInput(data.getInputStream()); reader.setInput(data.getInputStream());
@@ -874,7 +886,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testGetMinIndexNoInput() { public void testGetMinIndexNoInput() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
int num = 0; int num = 0;
@@ -888,7 +900,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testGetNumImages() { public void testGetNumImages() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
TestData data = getTestData().get(0); TestData data = getTestData().get(0);
reader.setInput(data.getInputStream()); reader.setInput(data.getInputStream());
@@ -917,7 +929,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testGetNumImagesNoInput() { public void testGetNumImagesNoInput() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
int num = -1; int num = -1;
@@ -945,7 +957,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testGetWidth() { public void testGetWidth() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
TestData data = getTestData().get(0); TestData data = getTestData().get(0);
reader.setInput(data.getInputStream()); reader.setInput(data.getInputStream());
@@ -962,7 +974,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testGetWidthIndexOutOfBounds() { public void testGetWidthIndexOutOfBounds() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
TestData data = getTestData().get(0); TestData data = getTestData().get(0);
reader.setInput(data.getInputStream()); reader.setInput(data.getInputStream());
@@ -983,13 +995,13 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testGetWidthNoInput() { public void testGetWidthNoInput() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
int width = 0; int width = 0;
try { try {
width = reader.getWidth(0); width = reader.getWidth(0);
fail("Width read without imput"); fail("Width read without input");
} }
catch (IllegalStateException ignore) { catch (IllegalStateException ignore) {
} }
@@ -1001,7 +1013,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testGetHeight() { public void testGetHeight() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
TestData data = getTestData().get(0); TestData data = getTestData().get(0);
reader.setInput(data.getInputStream()); reader.setInput(data.getInputStream());
@@ -1018,13 +1030,13 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testGetHeightNoInput() { public void testGetHeightNoInput() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
int height = 0; int height = 0;
try { try {
height = reader.getHeight(0); height = reader.getHeight(0);
fail("height read without imput"); fail("height read without input");
} }
catch (IllegalStateException ignore) { catch (IllegalStateException ignore) {
} }
@@ -1036,7 +1048,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testGetHeightIndexOutOfBounds() { public void testGetHeightIndexOutOfBounds() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
TestData data = getTestData().get(0); TestData data = getTestData().get(0);
reader.setInput(data.getInputStream()); reader.setInput(data.getInputStream());
@@ -1057,7 +1069,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testGetAspectRatio() { public void testGetAspectRatio() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
TestData data = getTestData().get(0); TestData data = getTestData().get(0);
reader.setInput(data.getInputStream()); reader.setInput(data.getInputStream());
@@ -1075,7 +1087,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testGetAspectRatioNoInput() { public void testGetAspectRatioNoInput() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
float aspectRatio = 0f; float aspectRatio = 0f;
@@ -1093,7 +1105,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testGetAspectRatioIndexOutOfBounds() { public void testGetAspectRatioIndexOutOfBounds() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
TestData data = getTestData().get(0); TestData data = getTestData().get(0);
reader.setInput(data.getInputStream()); reader.setInput(data.getInputStream());
@@ -1114,13 +1126,13 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testDisposeBeforeRead() { public void testDisposeBeforeRead() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
reader.dispose(); // Just pass with no exceptions reader.dispose(); // Just pass with no exceptions
} }
@Test @Test
public void testDisposeAfterRead() { public void testDisposeAfterRead() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
TestData data = getTestData().get(0); TestData data = getTestData().get(0);
reader.setInput(data.getInputStream()); reader.setInput(data.getInputStream());
@@ -1128,21 +1140,21 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testAddIIOReadProgressListener() { public void testAddIIOReadProgressListener() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
reader.addIIOReadProgressListener(mock(IIOReadProgressListener.class)); reader.addIIOReadProgressListener(mock(IIOReadProgressListener.class));
reader.dispose(); reader.dispose();
} }
@Test @Test
public void testAddIIOReadProgressListenerNull() { public void testAddIIOReadProgressListenerNull() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
reader.addIIOReadProgressListener(null); reader.addIIOReadProgressListener(null);
reader.dispose(); reader.dispose();
} }
@Test @Test
public void testAddIIOReadProgressListenerCallbacks() { public void testAddIIOReadProgressListenerCallbacks() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
TestData data = getTestData().get(0); TestData data = getTestData().get(0);
reader.setInput(data.getInputStream()); reader.setInput(data.getInputStream());
@@ -1166,7 +1178,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testMultipleAddIIOReadProgressListenerCallbacks() { public void testMultipleAddIIOReadProgressListenerCallbacks() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
TestData data = getTestData().get(0); TestData data = getTestData().get(0);
reader.setInput(data.getInputStream()); reader.setInput(data.getInputStream());
@@ -1204,21 +1216,21 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testRemoveIIOReadProgressListenerNull() { public void testRemoveIIOReadProgressListenerNull() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
reader.removeIIOReadProgressListener(null); reader.removeIIOReadProgressListener(null);
reader.dispose(); reader.dispose();
} }
@Test @Test
public void testRemoveIIOReadProgressListenerNone() { public void testRemoveIIOReadProgressListenerNone() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
reader.removeIIOReadProgressListener(mock(IIOReadProgressListener.class)); reader.removeIIOReadProgressListener(mock(IIOReadProgressListener.class));
reader.dispose(); reader.dispose();
} }
@Test @Test
public void testRemoveIIOReadProgressListener() { public void testRemoveIIOReadProgressListener() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
TestData data = getTestData().get(0); TestData data = getTestData().get(0);
reader.setInput(data.getInputStream()); reader.setInput(data.getInputStream());
@@ -1240,7 +1252,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testRemoveIIOReadProgressListenerMultiple() { public void testRemoveIIOReadProgressListenerMultiple() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
TestData data = getTestData().get(0); TestData data = getTestData().get(0);
reader.setInput(data.getInputStream()); reader.setInput(data.getInputStream());
@@ -1272,7 +1284,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testRemoveAllIIOReadProgressListeners() { public void testRemoveAllIIOReadProgressListeners() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
TestData data = getTestData().get(0); TestData data = getTestData().get(0);
reader.setInput(data.getInputStream()); reader.setInput(data.getInputStream());
@@ -1295,7 +1307,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testRemoveAllIIOReadProgressListenersMultiple() { public void testRemoveAllIIOReadProgressListenersMultiple() throws IOException {
ImageReader reader = createReader(); ImageReader reader = createReader();
TestData data = getTestData().get(0); TestData data = getTestData().get(0);
reader.setInput(data.getInputStream()); reader.setInput(data.getInputStream());
@@ -1322,7 +1334,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
@Test @Test
public void testAbort() { public void testAbort() throws IOException {
final ImageReader reader = createReader(); final ImageReader reader = createReader();
TestData data = getTestData().get(0); TestData data = getTestData().get(0);
reader.setInput(data.getInputStream()); reader.setInput(data.getInputStream());
@@ -1336,7 +1348,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
// Create a listener that just makes the reader abort immediately... // Create a listener that just makes the reader abort immediately...
IIOReadProgressListener abortingListener = mock(IIOReadProgressListener.class, "Aborter"); IIOReadProgressListener abortingListener = mock(IIOReadProgressListener.class, "Aborter");
Answer<Void> abort = new Answer<Void>() { Answer<Void> abort = new Answer<Void>() {
public Void answer(InvocationOnMock invocation) throws Throwable { public Void answer(InvocationOnMock invocation) {
reader.abort(); reader.abort();
return null; return null;
} }
@@ -1387,7 +1399,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
} }
} }
assertTrue("ImageTypeSepcifier from getRawImageType should be in the iterator from getImageTypes", rawFound); assertTrue("ImageTypeSpecifier from getRawImageType should be in the iterator from getImageTypes", rawFound);
} }
reader.dispose(); reader.dispose();
} }
@@ -1569,7 +1581,6 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
reader.dispose(); reader.dispose();
} }
@SuppressWarnings("ConstantConditions")
@Test @Test
public void testSetDestinationOffsetNull() throws IOException { public void testSetDestinationOffsetNull() throws IOException {
final ImageReader reader = createReader(); final ImageReader reader = createReader();
@@ -1620,7 +1631,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
assertEquals(expectedModel.getDataType(), resultModel.getDataType()); assertEquals(expectedModel.getDataType(), resultModel.getDataType());
assertEquals(expectedModel.getNumBands(), resultModel.getNumBands()); assertEquals(expectedModel.getNumBands(), resultModel.getNumBands());
assertEquals(expectedModel.getNumDataElements(), resultModel.getNumDataElements()); assertEquals(expectedModel.getNumDataElements(), resultModel.getNumDataElements());
assertTrue(Arrays.equals(expectedModel.getSampleSize(), resultModel.getSampleSize())); assertArrayEquals(expectedModel.getSampleSize(), resultModel.getSampleSize());
assertEquals(expectedModel.getTransferType(), resultModel.getTransferType()); assertEquals(expectedModel.getTransferType(), resultModel.getTransferType());
for (int i = 0; i < expectedModel.getNumBands(); i++) { for (int i = 0; i < expectedModel.getNumBands(); i++) {
assertEquals(expectedModel.getSampleSize(i), resultModel.getSampleSize(i)); assertEquals(expectedModel.getSampleSize(i), resultModel.getSampleSize(i));
@@ -1692,20 +1703,18 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
@Ignore("TODO: Implement") @Ignore("TODO: Implement")
@Test @Test
public void testSetDestinationBands() throws IOException { public void testSetDestinationBands() {
throw new UnsupportedOperationException("Method testSetDestinationBands not implemented"); // TODO: Implement throw new UnsupportedOperationException("Method testSetDestinationBands not implemented"); // TODO: Implement
} }
@Ignore("TODO: Implement") @Ignore("TODO: Implement")
@Test @Test
public void testSetSourceBands() throws IOException { public void testSetSourceBands() {
throw new UnsupportedOperationException("Method testSetDestinationBands not implemented"); // TODO: Implement throw new UnsupportedOperationException("Method testSetDestinationBands not implemented"); // TODO: Implement
} }
@Test @Test
public void testProviderAndMetadataFormatNamesMatch() throws IOException { public void testProviderAndMetadataFormatNamesMatch() throws IOException {
ImageReaderSpi provider = createProvider();
ImageReader reader = createReader(); ImageReader reader = createReader();
reader.setInput(getTestData().get(0).getInputStream()); reader.setInput(getTestData().get(0).getInputStream());
@@ -1826,7 +1835,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
@Override @Override
public String toString() { public String toString() {
return getClass().getSimpleName() + ": " + String.valueOf(input); return String.format("%s: %s", getClass().getSimpleName(), input);
} }
} }
} }
@@ -31,6 +31,7 @@
package com.twelvemonkeys.imageio.util; package com.twelvemonkeys.imageio.util;
import com.twelvemonkeys.imageio.stream.URLImageInputStreamSpi; import com.twelvemonkeys.imageio.stream.URLImageInputStreamSpi;
import org.junit.Test; import org.junit.Test;
import org.mockito.InOrder; import org.mockito.InOrder;
@@ -39,12 +40,14 @@ import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter; import javax.imageio.ImageWriter;
import javax.imageio.event.IIOWriteProgressListener; import javax.imageio.event.IIOWriteProgressListener;
import javax.imageio.spi.IIORegistry; import javax.imageio.spi.IIORegistry;
import javax.imageio.spi.ImageWriterSpi;
import javax.imageio.stream.ImageOutputStream; import javax.imageio.stream.ImageOutputStream;
import java.awt.*; import java.awt.*;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage; import java.awt.image.RenderedImage;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.net.URL; import java.net.URL;
import java.util.List; import java.util.List;
@@ -59,7 +62,7 @@ import static org.mockito.Mockito.*;
* @author last modified by $Author: haku $ * @author last modified by $Author: haku $
* @version $Id: ImageReaderAbstractTestCase.java,v 1.0 18.nov.2004 17:38:33 haku Exp $ * @version $Id: ImageReaderAbstractTestCase.java,v 1.0 18.nov.2004 17:38:33 haku Exp $
*/ */
public abstract class ImageWriterAbstractTest { public abstract class ImageWriterAbstractTest<T extends ImageWriter> {
// TODO: Move static block + getClassLoaderResource to common superclass for reader/writer test cases or delegate. // TODO: Move static block + getClassLoaderResource to common superclass for reader/writer test cases or delegate.
@@ -68,7 +71,16 @@ public abstract class ImageWriterAbstractTest {
ImageIO.setUseCache(false); ImageIO.setUseCache(false);
} }
protected abstract ImageWriter createImageWriter(); @SuppressWarnings("unchecked")
private final Class<T> writerClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
protected final ImageWriterSpi provider = createProvider();
protected abstract ImageWriterSpi createProvider();
protected final T createWriter() throws IOException {
return writerClass.cast(provider.createWriterInstance(null));
}
protected abstract List<? extends RenderedImage> getTestData(); protected abstract List<? extends RenderedImage> getTestData();
@@ -104,22 +116,22 @@ public abstract class ImageWriterAbstractTest {
@Test @Test
public void testSetOutput() throws IOException { public void testSetOutput() throws IOException {
// Should just pass with no exceptions // Should just pass with no exceptions
ImageWriter writer = createImageWriter(); ImageWriter writer = createWriter();
assertNotNull(writer); assertNotNull(writer);
writer.setOutput(ImageIO.createImageOutputStream(new ByteArrayOutputStream())); writer.setOutput(ImageIO.createImageOutputStream(new ByteArrayOutputStream()));
} }
@Test @Test
public void testSetOutputNull() { public void testSetOutputNull() throws IOException {
// Should just pass with no exceptions // Should just pass with no exceptions
ImageWriter writer = createImageWriter(); ImageWriter writer = createWriter();
assertNotNull(writer); assertNotNull(writer);
writer.setOutput(null); writer.setOutput(null);
} }
@Test @Test
public void testWrite() throws IOException { public void testWrite() throws IOException {
ImageWriter writer = createImageWriter(); ImageWriter writer = createWriter();
for (RenderedImage testData : getTestData()) { for (RenderedImage testData : getTestData()) {
ByteArrayOutputStream buffer = new ByteArrayOutputStream(); ByteArrayOutputStream buffer = new ByteArrayOutputStream();
@@ -136,10 +148,9 @@ public abstract class ImageWriterAbstractTest {
} }
} }
@SuppressWarnings("ConstantConditions")
@Test @Test
public void testWriteNull() throws IOException { public void testWriteNull() throws IOException {
ImageWriter writer = createImageWriter(); ImageWriter writer = createWriter();
ByteArrayOutputStream buffer = new ByteArrayOutputStream(); ByteArrayOutputStream buffer = new ByteArrayOutputStream();
writer.setOutput(ImageIO.createImageOutputStream(buffer)); writer.setOutput(ImageIO.createImageOutputStream(buffer));
@@ -156,8 +167,8 @@ public abstract class ImageWriterAbstractTest {
} }
@Test(expected = IllegalStateException.class) @Test(expected = IllegalStateException.class)
public void testWriteNoOutput() { public void testWriteNoOutput() throws IOException {
ImageWriter writer = createImageWriter(); ImageWriter writer = createWriter();
try { try {
writer.write(getTestData(0)); writer.write(getTestData(0));
@@ -168,8 +179,8 @@ public abstract class ImageWriterAbstractTest {
} }
@Test @Test
public void testGetDefaultWriteParam() { public void testGetDefaultWriteParam() throws IOException {
ImageWriter writer = createImageWriter(); ImageWriter writer = createWriter();
ImageWriteParam param = writer.getDefaultWriteParam(); ImageWriteParam param = writer.getDefaultWriteParam();
assertNotNull("Default ImageWriteParam is null", param); assertNotNull("Default ImageWriteParam is null", param);
} }
@@ -178,20 +189,20 @@ public abstract class ImageWriterAbstractTest {
// TODO: Source region and subsampling at least // TODO: Source region and subsampling at least
@Test @Test
public void testAddIIOWriteProgressListener() { public void testAddIIOWriteProgressListener() throws IOException {
ImageWriter writer = createImageWriter(); ImageWriter writer = createWriter();
writer.addIIOWriteProgressListener(mock(IIOWriteProgressListener.class)); writer.addIIOWriteProgressListener(mock(IIOWriteProgressListener.class));
} }
@Test @Test
public void testAddIIOWriteProgressListenerNull() { public void testAddIIOWriteProgressListenerNull() throws IOException {
ImageWriter writer = createImageWriter(); ImageWriter writer = createWriter();
writer.addIIOWriteProgressListener(null); writer.addIIOWriteProgressListener(null);
} }
@Test @Test
public void testAddIIOWriteProgressListenerCallbacks() throws IOException { public void testAddIIOWriteProgressListenerCallbacks() throws IOException {
ImageWriter writer = createImageWriter(); ImageWriter writer = createWriter();
ByteArrayOutputStream buffer = new ByteArrayOutputStream(); ByteArrayOutputStream buffer = new ByteArrayOutputStream();
writer.setOutput(ImageIO.createImageOutputStream(buffer)); writer.setOutput(ImageIO.createImageOutputStream(buffer));
@@ -214,7 +225,7 @@ public abstract class ImageWriterAbstractTest {
@Test @Test
public void testMultipleAddIIOWriteProgressListenerCallbacks() throws IOException { public void testMultipleAddIIOWriteProgressListenerCallbacks() throws IOException {
ImageWriter writer = createImageWriter(); ImageWriter writer = createWriter();
ByteArrayOutputStream buffer = new ByteArrayOutputStream(); ByteArrayOutputStream buffer = new ByteArrayOutputStream();
writer.setOutput(ImageIO.createImageOutputStream(buffer)); writer.setOutput(ImageIO.createImageOutputStream(buffer));
@@ -250,20 +261,20 @@ public abstract class ImageWriterAbstractTest {
} }
@Test @Test
public void testRemoveIIOWriteProgressListenerNull() { public void testRemoveIIOWriteProgressListenerNull() throws IOException {
ImageWriter writer = createImageWriter(); ImageWriter writer = createWriter();
writer.removeIIOWriteProgressListener(null); writer.removeIIOWriteProgressListener(null);
} }
@Test @Test
public void testRemoveIIOWriteProgressListenerNone() { public void testRemoveIIOWriteProgressListenerNone() throws IOException {
ImageWriter writer = createImageWriter(); ImageWriter writer = createWriter();
writer.removeIIOWriteProgressListener(mock(IIOWriteProgressListener.class)); writer.removeIIOWriteProgressListener(mock(IIOWriteProgressListener.class));
} }
@Test @Test
public void testRemoveIIOWriteProgressListener() throws IOException { public void testRemoveIIOWriteProgressListener() throws IOException {
ImageWriter writer = createImageWriter(); ImageWriter writer = createWriter();
ByteArrayOutputStream buffer = new ByteArrayOutputStream(); ByteArrayOutputStream buffer = new ByteArrayOutputStream();
writer.setOutput(ImageIO.createImageOutputStream(buffer)); writer.setOutput(ImageIO.createImageOutputStream(buffer));
@@ -284,7 +295,7 @@ public abstract class ImageWriterAbstractTest {
@Test @Test
public void testRemoveIIOWriteProgressListenerMultiple() throws IOException { public void testRemoveIIOWriteProgressListenerMultiple() throws IOException {
ImageWriter writer = createImageWriter(); ImageWriter writer = createWriter();
ByteArrayOutputStream buffer = new ByteArrayOutputStream(); ByteArrayOutputStream buffer = new ByteArrayOutputStream();
writer.setOutput(ImageIO.createImageOutputStream(buffer)); writer.setOutput(ImageIO.createImageOutputStream(buffer));
@@ -316,7 +327,7 @@ public abstract class ImageWriterAbstractTest {
@Test @Test
public void testRemoveAllIIOWriteProgressListeners() throws IOException { public void testRemoveAllIIOWriteProgressListeners() throws IOException {
ImageWriter writer = createImageWriter(); ImageWriter writer = createWriter();
ByteArrayOutputStream buffer = new ByteArrayOutputStream(); ByteArrayOutputStream buffer = new ByteArrayOutputStream();
writer.setOutput(ImageIO.createImageOutputStream(buffer)); writer.setOutput(ImageIO.createImageOutputStream(buffer));
@@ -339,7 +350,7 @@ public abstract class ImageWriterAbstractTest {
@Test @Test
public void testRemoveAllIIOWriteProgressListenersMultiple() throws IOException { public void testRemoveAllIIOWriteProgressListenersMultiple() throws IOException {
ImageWriter writer = createImageWriter(); ImageWriter writer = createWriter();
ByteArrayOutputStream buffer = new ByteArrayOutputStream(); ByteArrayOutputStream buffer = new ByteArrayOutputStream();
writer.setOutput(ImageIO.createImageOutputStream(buffer)); writer.setOutput(ImageIO.createImageOutputStream(buffer));
@@ -31,6 +31,7 @@
package com.twelvemonkeys.imageio.util; package com.twelvemonkeys.imageio.util;
import com.twelvemonkeys.imageio.color.ColorSpaces; import com.twelvemonkeys.imageio.color.ColorSpaces;
import org.junit.Test; import org.junit.Test;
import javax.imageio.ImageTypeSpecifier; import javax.imageio.ImageTypeSpecifier;
@@ -39,7 +40,8 @@ import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer; import java.awt.image.DataBuffer;
import java.awt.image.PixelInterleavedSampleModel; import java.awt.image.PixelInterleavedSampleModel;
import static org.hamcrest.core.Is.is; import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.*; import static org.junit.Assert.*;
public class UInt32ImageTypeSpecifierTest { public class UInt32ImageTypeSpecifierTest {
@@ -55,13 +57,13 @@ public class UInt32ImageTypeSpecifierTest {
assertEquals(1, spec.getNumComponents()); assertEquals(1, spec.getNumComponents());
assertEquals(32, spec.getBitsPerBand(0)); assertEquals(32, spec.getBitsPerBand(0));
assertThat(spec.getColorModel(), is(ComponentColorModel.class)); assertThat(spec.getColorModel(), instanceOf(ComponentColorModel.class));
assertFalse(spec.getColorModel().hasAlpha()); assertFalse(spec.getColorModel().hasAlpha());
assertFalse(spec.getColorModel().isAlphaPremultiplied()); assertFalse(spec.getColorModel().isAlphaPremultiplied());
assertEquals(1, spec.getColorModel().getNumComponents()); assertEquals(1, spec.getColorModel().getNumComponents());
assertEquals(1, spec.getColorModel().getNumColorComponents()); assertEquals(1, spec.getColorModel().getNumColorComponents());
assertThat(spec.getSampleModel(), is(PixelInterleavedSampleModel.class)); assertThat(spec.getSampleModel(), instanceOf(PixelInterleavedSampleModel.class));
assertEquals(1, spec.getSampleModel().getNumBands()); assertEquals(1, spec.getSampleModel().getNumBands());
assertEquals(1, spec.getSampleModel().getNumDataElements()); assertEquals(1, spec.getSampleModel().getNumDataElements());
} }
@@ -74,13 +76,13 @@ public class UInt32ImageTypeSpecifierTest {
assertEquals(32, spec.getBitsPerBand(0)); assertEquals(32, spec.getBitsPerBand(0));
assertEquals(32, spec.getBitsPerBand(1)); assertEquals(32, spec.getBitsPerBand(1));
assertThat(spec.getColorModel(), is(ComponentColorModel.class)); assertThat(spec.getColorModel(), instanceOf(ComponentColorModel.class));
assertTrue(spec.getColorModel().hasAlpha()); assertTrue(spec.getColorModel().hasAlpha());
assertFalse(spec.getColorModel().isAlphaPremultiplied()); assertFalse(spec.getColorModel().isAlphaPremultiplied());
assertEquals(2, spec.getColorModel().getNumComponents()); assertEquals(2, spec.getColorModel().getNumComponents());
assertEquals(1, spec.getColorModel().getNumColorComponents()); assertEquals(1, spec.getColorModel().getNumColorComponents());
assertThat(spec.getSampleModel(), is(PixelInterleavedSampleModel.class)); assertThat(spec.getSampleModel(), instanceOf(PixelInterleavedSampleModel.class));
assertEquals(2, spec.getSampleModel().getNumBands()); assertEquals(2, spec.getSampleModel().getNumBands());
assertEquals(2, spec.getSampleModel().getNumDataElements()); assertEquals(2, spec.getSampleModel().getNumDataElements());
} }
@@ -95,13 +97,13 @@ public class UInt32ImageTypeSpecifierTest {
assertEquals(32, spec.getBitsPerBand(1)); assertEquals(32, spec.getBitsPerBand(1));
assertEquals(32, spec.getBitsPerBand(2)); assertEquals(32, spec.getBitsPerBand(2));
assertThat(spec.getColorModel(), is(ComponentColorModel.class)); assertThat(spec.getColorModel(), instanceOf(ComponentColorModel.class));
assertFalse(spec.getColorModel().hasAlpha()); assertFalse(spec.getColorModel().hasAlpha());
assertFalse(spec.getColorModel().isAlphaPremultiplied()); assertFalse(spec.getColorModel().isAlphaPremultiplied());
assertEquals(3, spec.getColorModel().getNumComponents()); assertEquals(3, spec.getColorModel().getNumComponents());
assertEquals(3, spec.getColorModel().getNumColorComponents()); assertEquals(3, spec.getColorModel().getNumColorComponents());
assertThat(spec.getSampleModel(), is(PixelInterleavedSampleModel.class)); assertThat(spec.getSampleModel(), instanceOf(PixelInterleavedSampleModel.class));
assertEquals(3, spec.getSampleModel().getNumBands()); assertEquals(3, spec.getSampleModel().getNumBands());
assertEquals(3, spec.getSampleModel().getNumDataElements()); assertEquals(3, spec.getSampleModel().getNumDataElements());
} }
@@ -116,13 +118,13 @@ public class UInt32ImageTypeSpecifierTest {
assertEquals(32, spec.getBitsPerBand(2)); assertEquals(32, spec.getBitsPerBand(2));
assertEquals(32, spec.getBitsPerBand(3)); assertEquals(32, spec.getBitsPerBand(3));
assertThat(spec.getColorModel(), is(ComponentColorModel.class)); assertThat(spec.getColorModel(), instanceOf(ComponentColorModel.class));
assertTrue(spec.getColorModel().hasAlpha()); assertTrue(spec.getColorModel().hasAlpha());
assertFalse(spec.getColorModel().isAlphaPremultiplied()); assertFalse(spec.getColorModel().isAlphaPremultiplied());
assertEquals(4, spec.getColorModel().getNumComponents()); assertEquals(4, spec.getColorModel().getNumComponents());
assertEquals(3, spec.getColorModel().getNumColorComponents()); assertEquals(3, spec.getColorModel().getNumColorComponents());
assertThat(spec.getSampleModel(), is(PixelInterleavedSampleModel.class)); assertThat(spec.getSampleModel(), instanceOf(PixelInterleavedSampleModel.class));
assertEquals(4, spec.getSampleModel().getNumBands()); assertEquals(4, spec.getSampleModel().getNumBands());
assertEquals(4, spec.getSampleModel().getNumDataElements()); assertEquals(4, spec.getSampleModel().getNumDataElements());
} }
@@ -137,13 +139,13 @@ public class UInt32ImageTypeSpecifierTest {
assertEquals(32, spec.getBitsPerBand(2)); assertEquals(32, spec.getBitsPerBand(2));
assertEquals(32, spec.getBitsPerBand(3)); assertEquals(32, spec.getBitsPerBand(3));
assertThat(spec.getColorModel(), is(ComponentColorModel.class)); assertThat(spec.getColorModel(), instanceOf(ComponentColorModel.class));
assertTrue(spec.getColorModel().hasAlpha()); assertTrue(spec.getColorModel().hasAlpha());
assertTrue(spec.getColorModel().isAlphaPremultiplied()); assertTrue(spec.getColorModel().isAlphaPremultiplied());
assertEquals(4, spec.getColorModel().getNumComponents()); assertEquals(4, spec.getColorModel().getNumComponents());
assertEquals(3, spec.getColorModel().getNumColorComponents()); assertEquals(3, spec.getColorModel().getNumColorComponents());
assertThat(spec.getSampleModel(), is(PixelInterleavedSampleModel.class)); assertThat(spec.getSampleModel(), instanceOf(PixelInterleavedSampleModel.class));
assertEquals(4, spec.getSampleModel().getNumBands()); assertEquals(4, spec.getSampleModel().getNumBands());
assertEquals(4, spec.getSampleModel().getNumDataElements()); assertEquals(4, spec.getSampleModel().getNumDataElements());
} }
@@ -159,13 +161,13 @@ public class UInt32ImageTypeSpecifierTest {
assertEquals(32, spec.getBitsPerBand(2)); assertEquals(32, spec.getBitsPerBand(2));
assertEquals(32, spec.getBitsPerBand(3)); assertEquals(32, spec.getBitsPerBand(3));
assertThat(spec.getColorModel(), is(ComponentColorModel.class)); assertThat(spec.getColorModel(), instanceOf(ComponentColorModel.class));
assertFalse(spec.getColorModel().hasAlpha()); assertFalse(spec.getColorModel().hasAlpha());
assertFalse(spec.getColorModel().isAlphaPremultiplied()); assertFalse(spec.getColorModel().isAlphaPremultiplied());
assertEquals(4, spec.getColorModel().getNumComponents()); assertEquals(4, spec.getColorModel().getNumComponents());
assertEquals(4, spec.getColorModel().getNumColorComponents()); assertEquals(4, spec.getColorModel().getNumColorComponents());
assertThat(spec.getSampleModel(), is(PixelInterleavedSampleModel.class)); assertThat(spec.getSampleModel(), instanceOf(PixelInterleavedSampleModel.class));
assertEquals(4, spec.getSampleModel().getNumBands()); assertEquals(4, spec.getSampleModel().getNumBands());
assertEquals(4, spec.getSampleModel().getNumDataElements()); assertEquals(4, spec.getSampleModel().getNumDataElements());
} }
@@ -181,13 +183,13 @@ public class UInt32ImageTypeSpecifierTest {
assertEquals(32, spec.getBitsPerBand(3)); assertEquals(32, spec.getBitsPerBand(3));
assertEquals(32, spec.getBitsPerBand(4)); assertEquals(32, spec.getBitsPerBand(4));
assertThat(spec.getColorModel(), is(ComponentColorModel.class)); assertThat(spec.getColorModel(), instanceOf(ComponentColorModel.class));
assertTrue(spec.getColorModel().hasAlpha()); assertTrue(spec.getColorModel().hasAlpha());
assertFalse(spec.getColorModel().isAlphaPremultiplied()); assertFalse(spec.getColorModel().isAlphaPremultiplied());
assertEquals(5, spec.getColorModel().getNumComponents()); assertEquals(5, spec.getColorModel().getNumComponents());
assertEquals(4, spec.getColorModel().getNumColorComponents()); assertEquals(4, spec.getColorModel().getNumColorComponents());
assertThat(spec.getSampleModel(), is(PixelInterleavedSampleModel.class)); assertThat(spec.getSampleModel(), instanceOf(PixelInterleavedSampleModel.class));
assertEquals(5, spec.getSampleModel().getNumBands()); assertEquals(5, spec.getSampleModel().getNumBands());
assertEquals(5, spec.getSampleModel().getNumDataElements()); assertEquals(5, spec.getSampleModel().getNumDataElements());
} }
+2 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId> <artifactId>imageio</artifactId>
<version>3.6</version> <version>3.6.5-SNAPSHOT</version>
</parent> </parent>
<artifactId>imageio-hdr</artifactId> <artifactId>imageio-hdr</artifactId>
<name>TwelveMonkeys :: ImageIO :: HDR plugin</name> <name>TwelveMonkeys :: ImageIO :: HDR plugin</name>
@@ -21,6 +21,7 @@
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-core</artifactId> <artifactId>imageio-core</artifactId>
<type>test-jar</type> <type>test-jar</type>
<scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
@@ -46,26 +46,16 @@ import java.util.List;
* @version $Id: TGAImageReaderTest.java,v 1.0 03.07.14 22:28 haraldk Exp$ * @version $Id: TGAImageReaderTest.java,v 1.0 03.07.14 22:28 haraldk Exp$
*/ */
public class HDRImageReaderTest extends ImageReaderAbstractTest<HDRImageReader> { public class HDRImageReaderTest extends ImageReaderAbstractTest<HDRImageReader> {
@Override
protected List<TestData> getTestData() {
return Arrays.asList(
new TestData(getClassLoaderResource("/hdr/memorial_o876.hdr"), new Dimension(512, 768))
);
}
@Override @Override
protected ImageReaderSpi createProvider() { protected ImageReaderSpi createProvider() {
return new HDRImageReaderSpi(); return new HDRImageReaderSpi();
} }
@Override @Override
protected Class<HDRImageReader> getReaderClass() { protected List<TestData> getTestData() {
return HDRImageReader.class; return Collections.singletonList(
} new TestData(getClassLoaderResource("/hdr/memorial_o876.hdr"), new Dimension(512, 768))
);
@Override
protected HDRImageReader createReader() {
return new HDRImageReader(createProvider());
} }
@Override @Override
@@ -0,0 +1,31 @@
package com.twelvemonkeys.imageio.plugins.hdr.tonemap;
import org.junit.Test;
import static org.junit.Assert.assertArrayEquals;
public class DefaultToneMapperTest {
private final DefaultToneMapper mapper = new DefaultToneMapper();
@Test
public void testMap0() {
float[] rgb = {0};
mapper.map(rgb);
assertArrayEquals(new float[]{0}, rgb, 0);
}
@Test
public void testMap1() {
float[] rgb = {1};
mapper.map(rgb);
assertArrayEquals(new float[]{0.5f}, rgb, 0);
}
@Test
public void testMapMax() {
float[] rgb = {Float.MAX_VALUE};
mapper.map(rgb);
assertArrayEquals(new float[]{1}, rgb, 0);
}
}
@@ -0,0 +1,38 @@
package com.twelvemonkeys.imageio.plugins.hdr.tonemap;
import org.junit.Test;
import static org.junit.Assert.assertArrayEquals;
public class GammaToneMapperTest {
private final GammaToneMapper mapper = new GammaToneMapper();
@Test
public void testMap0() {
float[] rgb = {0};
mapper.map(rgb);
assertArrayEquals(new float[]{0}, rgb, 0);
}
@Test
public void testMap1() {
float[] rgb = {1};
mapper.map(rgb);
assertArrayEquals(new float[]{0.5f}, rgb, 0);
}
@Test
public void testMap16() {
float[] rgb = {15.999999f};
mapper.map(rgb);
assertArrayEquals(new float[]{1}, rgb, 0);
}
@Test
public void testMapMax() {
float[] rgb = {Float.MAX_VALUE};
mapper.map(rgb);
assertArrayEquals(new float[]{1}, rgb, 0);
}
}
@@ -0,0 +1,31 @@
package com.twelvemonkeys.imageio.plugins.hdr.tonemap;
import org.junit.Test;
import static org.junit.Assert.assertArrayEquals;
public class NullToneMapperTest {
private final NullToneMapper mapper = new NullToneMapper();
@Test
public void testMap0() {
float[] rgb = {0};
mapper.map(rgb);
assertArrayEquals(new float[]{0}, rgb, 0);
}
@Test
public void testMap1() {
float[] rgb = {1};
mapper.map(rgb);
assertArrayEquals(new float[]{1}, rgb, 0);
}
@Test
public void testMapMax() {
float[] rgb = {Float.MAX_VALUE};
mapper.map(rgb);
assertArrayEquals(new float[]{Float.MAX_VALUE}, rgb, 0);
}
}
+2 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId> <artifactId>imageio</artifactId>
<version>3.6</version> <version>3.6.5-SNAPSHOT</version>
</parent> </parent>
<artifactId>imageio-icns</artifactId> <artifactId>imageio-icns</artifactId>
<name>TwelveMonkeys :: ImageIO :: ICNS plugin</name> <name>TwelveMonkeys :: ImageIO :: ICNS plugin</name>
@@ -19,6 +19,7 @@
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-core</artifactId> <artifactId>imageio-core</artifactId>
<type>test-jar</type> <type>test-jar</type>
<scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>
@@ -31,10 +31,10 @@
package com.twelvemonkeys.imageio.plugins.icns; package com.twelvemonkeys.imageio.plugins.icns;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest; import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import javax.imageio.ImageReader;
import javax.imageio.spi.ImageReaderSpi; import javax.imageio.spi.ImageReaderSpi;
import java.awt.*; import java.awt.*;
import java.io.IOException; import java.io.IOException;
@@ -49,7 +49,12 @@ import java.util.List;
* @author last modified by $Author: haraldk$ * @author last modified by $Author: haraldk$
* @version $Id: ICNSImageReaderTest.java,v 1.0 25.10.11 18:44 haraldk Exp$ * @version $Id: ICNSImageReaderTest.java,v 1.0 25.10.11 18:44 haraldk Exp$
*/ */
public class ICNSImageReaderTest extends ImageReaderAbstractTest { public class ICNSImageReaderTest extends ImageReaderAbstractTest<ICNSImageReader> {
@Override
protected ImageReaderSpi createProvider() {
return new ICNSImageReaderSpi();
}
@Override @Override
protected List<TestData> getTestData() { protected List<TestData> getTestData() {
return Arrays.asList( return Arrays.asList(
@@ -105,21 +110,6 @@ public class ICNSImageReaderTest extends ImageReaderAbstractTest {
); );
} }
@Override
protected ImageReaderSpi createProvider() {
return new ICNSImageReaderSpi();
}
@Override
protected ImageReader createReader() {
return new ICNSImageReader();
}
@Override
protected Class getReaderClass() {
return ICNSImageReader.class;
}
@Override @Override
protected List<String> getFormatNames() { protected List<String> getFormatNames() {
return Collections.singletonList("icns"); return Collections.singletonList("icns");
@@ -31,11 +31,13 @@
package com.twelvemonkeys.imageio.plugins.icns; package com.twelvemonkeys.imageio.plugins.icns;
import com.twelvemonkeys.imageio.util.ImageWriterAbstractTest; import com.twelvemonkeys.imageio.util.ImageWriterAbstractTest;
import org.junit.Test; import org.junit.Test;
import javax.imageio.IIOImage; import javax.imageio.IIOImage;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import javax.imageio.ImageWriter; import javax.imageio.ImageWriter;
import javax.imageio.spi.ImageWriterSpi;
import javax.imageio.stream.ImageOutputStream; import javax.imageio.stream.ImageOutputStream;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage; import java.awt.image.RenderedImage;
@@ -53,13 +55,11 @@ import static org.junit.Assert.assertTrue;
* @author last modified by $Author: harald.kuhr$ * @author last modified by $Author: harald.kuhr$
* @version $Id: ICNSImageWriterTest.java,v 1.0 25/08/2018 harald.kuhr Exp$ * @version $Id: ICNSImageWriterTest.java,v 1.0 25/08/2018 harald.kuhr Exp$
*/ */
public class ICNSImageWriterTest extends ImageWriterAbstractTest { public class ICNSImageWriterTest extends ImageWriterAbstractTest<ICNSImageWriter> {
private final ICNSImageWriterSpi provider = new ICNSImageWriterSpi();
@Override @Override
protected ImageWriter createImageWriter() { protected ImageWriterSpi createProvider() {
return provider.createWriterInstance(null); return new ICNSImageWriterSpi();
} }
@Override @Override
@@ -81,7 +81,7 @@ public class ICNSImageWriterTest extends ImageWriterAbstractTest {
@Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class)
public void testWriteNonSquare() throws IOException { public void testWriteNonSquare() throws IOException {
// ICNS only supports square icons (except some arcane 16x12 we don't currently support) // ICNS only supports square icons (except some arcane 16x12 we don't currently support)
ImageWriter writer = createImageWriter(); ImageWriter writer = createWriter();
try (ImageOutputStream stream = ImageIO.createImageOutputStream(new ByteArrayOutputStream())) { try (ImageOutputStream stream = ImageIO.createImageOutputStream(new ByteArrayOutputStream())) {
writer.setOutput(stream); writer.setOutput(stream);
@@ -97,7 +97,7 @@ public class ICNSImageWriterTest extends ImageWriterAbstractTest {
@Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class)
public void testWriteBadSize() throws IOException { public void testWriteBadSize() throws IOException {
// ICNS only supports sizes in multiples of 2 (16, 32, 64, ..., 1024 + 48 and 96) // ICNS only supports sizes in multiples of 2 (16, 32, 64, ..., 1024 + 48 and 96)
ImageWriter writer = createImageWriter(); ImageWriter writer = createWriter();
try (ImageOutputStream stream = ImageIO.createImageOutputStream(new ByteArrayOutputStream())) { try (ImageOutputStream stream = ImageIO.createImageOutputStream(new ByteArrayOutputStream())) {
writer.setOutput(stream); writer.setOutput(stream);
@@ -111,8 +111,8 @@ public class ICNSImageWriterTest extends ImageWriterAbstractTest {
} }
@Test @Test
public void testSequencesSupported() { public void testSequencesSupported() throws IOException {
ImageWriter writer = createImageWriter(); ImageWriter writer = createWriter();
try { try {
assertTrue(writer.canWriteSequence()); assertTrue(writer.canWriteSequence());
} }
@@ -124,7 +124,7 @@ public class ICNSImageWriterTest extends ImageWriterAbstractTest {
@Test(expected = IllegalStateException.class) @Test(expected = IllegalStateException.class)
public void testWriteSequenceNotStarted() throws IOException { public void testWriteSequenceNotStarted() throws IOException {
// ICNS only supports sizes in multiples of 2 (16, 32, 64, ..., 1024 + 48 and 96) // ICNS only supports sizes in multiples of 2 (16, 32, 64, ..., 1024 + 48 and 96)
ImageWriter writer = createImageWriter(); ImageWriter writer = createWriter();
try (ImageOutputStream stream = ImageIO.createImageOutputStream(new ByteArrayOutputStream())) { try (ImageOutputStream stream = ImageIO.createImageOutputStream(new ByteArrayOutputStream())) {
writer.setOutput(stream); writer.setOutput(stream);
@@ -141,7 +141,7 @@ public class ICNSImageWriterTest extends ImageWriterAbstractTest {
@Test(expected = IllegalStateException.class) @Test(expected = IllegalStateException.class)
public void testEndSequenceNotStarted() throws IOException { public void testEndSequenceNotStarted() throws IOException {
// ICNS only supports sizes in multiples of 2 (16, 32, 64, ..., 1024 + 48 and 96) // ICNS only supports sizes in multiples of 2 (16, 32, 64, ..., 1024 + 48 and 96)
ImageWriter writer = createImageWriter(); ImageWriter writer = createWriter();
try (ImageOutputStream stream = ImageIO.createImageOutputStream(new ByteArrayOutputStream())) { try (ImageOutputStream stream = ImageIO.createImageOutputStream(new ByteArrayOutputStream())) {
writer.setOutput(stream); writer.setOutput(stream);
@@ -155,7 +155,7 @@ public class ICNSImageWriterTest extends ImageWriterAbstractTest {
@Test(expected = IllegalStateException.class) @Test(expected = IllegalStateException.class)
public void testPrepareSequenceAlreadyStarted() throws IOException { public void testPrepareSequenceAlreadyStarted() throws IOException {
// ICNS only supports sizes in multiples of 2 (16, 32, 64, ..., 1024 + 48 and 96) // ICNS only supports sizes in multiples of 2 (16, 32, 64, ..., 1024 + 48 and 96)
ImageWriter writer = createImageWriter(); ImageWriter writer = createWriter();
try (ImageOutputStream stream = ImageIO.createImageOutputStream(new ByteArrayOutputStream())) { try (ImageOutputStream stream = ImageIO.createImageOutputStream(new ByteArrayOutputStream())) {
writer.setOutput(stream); writer.setOutput(stream);
@@ -169,7 +169,7 @@ public class ICNSImageWriterTest extends ImageWriterAbstractTest {
@Test @Test
public void testWriteSequence() throws IOException { public void testWriteSequence() throws IOException {
ImageWriter writer = createImageWriter(); ImageWriter writer = createWriter();
ByteArrayOutputStream output = new ByteArrayOutputStream(); ByteArrayOutputStream output = new ByteArrayOutputStream();
try (ImageOutputStream stream = ImageIO.createImageOutputStream(output)) { try (ImageOutputStream stream = ImageIO.createImageOutputStream(output)) {
+2 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId> <artifactId>imageio</artifactId>
<version>3.6</version> <version>3.6.5-SNAPSHOT</version>
</parent> </parent>
<artifactId>imageio-iff</artifactId> <artifactId>imageio-iff</artifactId>
<name>TwelveMonkeys :: ImageIO :: IFF plugin</name> <name>TwelveMonkeys :: ImageIO :: IFF plugin</name>
@@ -22,6 +22,7 @@
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-core</artifactId> <artifactId>imageio-core</artifactId>
<type>test-jar</type> <type>test-jar</type>
<scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>
@@ -31,6 +31,7 @@
package com.twelvemonkeys.imageio.plugins.iff; package com.twelvemonkeys.imageio.plugins.iff;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest; import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
import org.junit.Test; import org.junit.Test;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
@@ -54,6 +55,12 @@ import static org.junit.Assert.*;
* @version $Id: IFFImageReaderTestCase.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$ * @version $Id: IFFImageReaderTestCase.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$
*/ */
public class IFFImageReaderTest extends ImageReaderAbstractTest<IFFImageReader> { public class IFFImageReaderTest extends ImageReaderAbstractTest<IFFImageReader> {
@Override
protected ImageReaderSpi createProvider() {
return new IFFImageReaderSpi();
}
@Override
protected List<TestData> getTestData() { protected List<TestData> getTestData() {
return Arrays.asList( return Arrays.asList(
// 32 bit - Ok // 32 bit - Ok
@@ -85,22 +92,17 @@ public class IFFImageReaderTest extends ImageReaderAbstractTest<IFFImageReader>
); );
} }
protected ImageReaderSpi createProvider() { @Override
return new IFFImageReaderSpi();
}
protected Class<IFFImageReader> getReaderClass() {
return IFFImageReader.class;
}
protected List<String> getFormatNames() { protected List<String> getFormatNames() {
return Collections.singletonList("iff"); return Collections.singletonList("iff");
} }
@Override
protected List<String> getSuffixes() { protected List<String> getSuffixes() {
return Arrays.asList("iff", "ilbm", "ham", "ham8", "lbm"); return Arrays.asList("iff", "ilbm", "ham", "ham8", "lbm");
} }
@Override
protected List<String> getMIMETypes() { protected List<String> getMIMETypes() {
return Arrays.asList("image/iff", "image/x-iff"); return Arrays.asList("image/iff", "image/x-iff");
} }
@@ -32,10 +32,12 @@ package com.twelvemonkeys.imageio.plugins.iff;
import com.twelvemonkeys.image.MonochromeColorModel; import com.twelvemonkeys.image.MonochromeColorModel;
import com.twelvemonkeys.imageio.util.ImageWriterAbstractTest; import com.twelvemonkeys.imageio.util.ImageWriterAbstractTest;
import org.junit.Test; import org.junit.Test;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import javax.imageio.ImageWriter; import javax.imageio.ImageWriter;
import javax.imageio.spi.ImageWriterSpi;
import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream; import javax.imageio.stream.ImageOutputStream;
import java.awt.color.ColorSpace; import java.awt.color.ColorSpace;
@@ -56,12 +58,10 @@ import static org.junit.Assert.*;
* @author last modified by $Author: haraldk$ * @author last modified by $Author: haraldk$
* @version $Id: JPEG2000ImageWriterTest.java,v 1.0 20.01.12 12:19 haraldk Exp$ * @version $Id: JPEG2000ImageWriterTest.java,v 1.0 20.01.12 12:19 haraldk Exp$
*/ */
public class IFFImageWriterTest extends ImageWriterAbstractTest { public class IFFImageWriterTest extends ImageWriterAbstractTest<IFFImageWriter> {
private final IFFImageWriterSpi provider = new IFFImageWriterSpi();
@Override @Override
protected ImageWriter createImageWriter() { protected ImageWriterSpi createProvider() {
return new IFFImageWriter(provider); return new IFFImageWriterSpi();
} }
@Override @Override
@@ -81,7 +81,7 @@ public class IFFImageWriterTest extends ImageWriterAbstractTest {
@Test @Test
public void testWriteReadCompare() throws IOException { public void testWriteReadCompare() throws IOException {
ImageWriter writer = createImageWriter(); ImageWriter writer = createWriter();
List<? extends RenderedImage> testData = getTestData(); List<? extends RenderedImage> testData = getTestData();
@@ -116,9 +116,7 @@ public class IFFImageWriterTest extends ImageWriterAbstractTest {
assertSameData(original, written); assertSameData(original, written);
} }
catch (IOException e) { catch (IOException e) {
AssertionError fail = new AssertionError("Failure writing test data " + i + " " + e); throw new AssertionError("Failure writing test data " + i + " " + e, e);
fail.initCause(e);
throw fail;
} }
} }
} }
+2 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId> <artifactId>imageio</artifactId>
<version>3.6</version> <version>3.6.5-SNAPSHOT</version>
</parent> </parent>
<artifactId>imageio-jpeg</artifactId> <artifactId>imageio-jpeg</artifactId>
<name>TwelveMonkeys :: ImageIO :: JPEG plugin</name> <name>TwelveMonkeys :: ImageIO :: JPEG plugin</name>
@@ -21,6 +21,7 @@
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-core</artifactId> <artifactId>imageio-core</artifactId>
<type>test-jar</type> <type>test-jar</type>
<scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
@@ -41,7 +41,6 @@ import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegment;
import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegmentUtil; import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegmentUtil;
import com.twelvemonkeys.imageio.metadata.tiff.TIFF; import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
import com.twelvemonkeys.imageio.metadata.tiff.TIFFReader; import com.twelvemonkeys.imageio.metadata.tiff.TIFFReader;
import com.twelvemonkeys.imageio.stream.BufferedImageInputStream;
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream; import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
import com.twelvemonkeys.imageio.stream.SubImageInputStream; import com.twelvemonkeys.imageio.stream.SubImageInputStream;
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers; import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
@@ -57,13 +56,13 @@ import javax.imageio.metadata.IIOMetadataFormatImpl;
import javax.imageio.metadata.IIOMetadataNode; import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.spi.ImageReaderSpi; import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.MemoryCacheImageInputStream;
import java.awt.*; import java.awt.*;
import java.awt.color.ColorSpace; import java.awt.color.ColorSpace;
import java.awt.color.ICC_ColorSpace; import java.awt.color.ICC_ColorSpace;
import java.awt.color.ICC_Profile; import java.awt.color.ICC_Profile;
import java.awt.image.*; import java.awt.image.*;
import java.io.*; import java.io.*;
import java.nio.ByteOrder;
import java.util.List; import java.util.List;
import java.util.*; import java.util.*;
@@ -123,13 +122,11 @@ public final class JPEGImageReader extends ImageReaderBase {
private ImageReader thumbnailReader; private ImageReader thumbnailReader;
private List<ThumbnailReader> thumbnails; private List<ThumbnailReader> thumbnails;
private JPEGImage10MetadataCleaner metadataCleaner;
/** Cached list of JPEG segments we filter from the underlying stream */ /** Cached list of JPEG segments we filter from the underlying stream */
private List<Segment> segments; private List<Segment> segments;
private int currentStreamIndex = 0; private int currentStreamIndex = 0;
private List<Long> streamOffsets = new ArrayList<>(); private final List<Long> streamOffsets = new ArrayList<>();
protected JPEGImageReader(final ImageReaderSpi provider, final ImageReader delegate) { protected JPEGImageReader(final ImageReaderSpi provider, final ImageReader delegate) {
super(provider); super(provider);
@@ -158,8 +155,6 @@ public final class JPEGImageReader extends ImageReaderBase {
thumbnailReader.reset(); thumbnailReader.reset();
} }
metadataCleaner = null;
installListeners(); installListeners();
} }
@@ -183,19 +178,7 @@ public final class JPEGImageReader extends ImageReaderBase {
private boolean isLossless() throws IOException { private boolean isLossless() throws IOException {
assertInput(); assertInput();
try { return getSOF().marker == JPEG.SOF3;
if (getSOF().marker == JPEG.SOF3) {
return true;
}
}
catch (IIOException ignore) {
// May happen if no SOF is found, in case we'll just fall through
if (DEBUG) {
ignore.printStackTrace();
}
}
return false;
} }
@Override @Override
@@ -469,15 +452,12 @@ public final class JPEGImageReader extends ImageReaderBase {
} }
// We'll need a read param // We'll need a read param
Rectangle origSourceRegion;
if (param == null) { if (param == null) {
param = delegate.getDefaultReadParam(); param = delegate.getDefaultReadParam();
origSourceRegion = null;
}
else {
origSourceRegion = param.getSourceRegion();
} }
Rectangle origSourceRegion = param.getSourceRegion();
Rectangle srcRegion = new Rectangle(); Rectangle srcRegion = new Rectangle();
Rectangle dstRegion = new Rectangle(); Rectangle dstRegion = new Rectangle();
computeRegions(param, origWidth, origHeight, image, srcRegion, dstRegion); computeRegions(param, origWidth, origHeight, image, srcRegion, dstRegion);
@@ -741,26 +721,26 @@ public final class JPEGImageReader extends ImageReaderBase {
long lastKnownSOIOffset = streamOffsets.get(streamOffsets.size() - 1); long lastKnownSOIOffset = streamOffsets.get(streamOffsets.size() - 1);
imageInput.seek(lastKnownSOIOffset); imageInput.seek(lastKnownSOIOffset);
try (ImageInputStream stream = new BufferedImageInputStream(imageInput)) { // Extreme (10s -> 50ms) speedup if imageInput is FileIIS try {
for (int i = streamOffsets.size() - 1; i < imageIndex; i++) { for (int i = streamOffsets.size() - 1; i < imageIndex; i++) {
long start = 0; long start = 0;
if (DEBUG) { if (DEBUG) {
start = System.currentTimeMillis(); start = System.currentTimeMillis();
System.out.println(String.format("Start seeking for image index %d", i + 1)); System.out.printf("Start seeking for image index %d%n", i + 1);
} }
// Need to skip over segments, as they may contain JPEG markers (eg. JFXX or EXIF thumbnail) // Need to skip over segments, as they may contain JPEG markers (eg. JFXX or EXIF thumbnail)
JPEGSegmentUtil.readSegments(stream, Collections.<Integer, List<String>>emptyMap()); JPEGSegmentUtil.readSegments(imageInput, Collections.<Integer, List<String>>emptyMap());
// Now, search for EOI and following SOI... // Now, search for EOI and following SOI...
int marker; int marker;
while ((marker = stream.read()) != -1) { while ((marker = imageInput.read()) != -1) {
if (marker == 0xFF && (0xFF00 | stream.readUnsignedByte()) == JPEG.EOI) { if (marker == 0xFF && (0xFF00 | imageInput.readUnsignedByte()) == JPEG.EOI) {
// Found EOI, now the SOI should be nearby... // Found EOI, now the SOI should be nearby...
while ((marker = stream.read()) != -1) { while ((marker = imageInput.read()) != -1) {
if (marker == 0xFF && (0xFF00 | stream.readUnsignedByte()) == JPEG.SOI) { if (marker == 0xFF && (0xFF00 | imageInput.readUnsignedByte()) == JPEG.SOI) {
long nextSOIOffset = stream.getStreamPosition() - 2; long nextSOIOffset = imageInput.getStreamPosition() - 2;
imageInput.seek(nextSOIOffset); imageInput.seek(nextSOIOffset);
streamOffsets.add(nextSOIOffset); streamOffsets.add(nextSOIOffset);
@@ -774,10 +754,9 @@ public final class JPEGImageReader extends ImageReaderBase {
} }
if (DEBUG) { if (DEBUG) {
System.out.println(String.format("Seek in %d ms", System.currentTimeMillis() - start)); System.out.printf("Seek in %d ms%n", System.currentTimeMillis() - start);
} }
} }
} }
catch (EOFException eof) { catch (EOFException eof) {
IndexOutOfBoundsException ioobe = new IndexOutOfBoundsException("Image index " + imageIndex + " not found in stream"); IndexOutOfBoundsException ioobe = new IndexOutOfBoundsException("Image index " + imageIndex + " not found in stream");
@@ -837,9 +816,9 @@ public final class JPEGImageReader extends ImageReaderBase {
return JPEGSegmentUtil.readSegments(imageInput, JPEGSegmentUtil.ALL_SEGMENTS); return JPEGSegmentUtil.readSegments(imageInput, JPEGSegmentUtil.ALL_SEGMENTS);
} }
catch (IIOException | IllegalArgumentException ignore) { catch (IIOException | IllegalArgumentException e) {
if (DEBUG) { if (DEBUG) {
ignore.printStackTrace(); e.printStackTrace();
} }
} }
finally { finally {
@@ -903,16 +882,19 @@ public final class JPEGImageReader extends ImageReaderBase {
if (!exifSegments.isEmpty()) { if (!exifSegments.isEmpty()) {
Application exif = exifSegments.get(0); Application exif = exifSegments.get(0);
InputStream data = exif.data(); int offset = exif.identifier.length() + 2; // Incl. pad
if (data.read() == -1) { // Read pad if (exif.data.length <= offset) {
processWarningOccurred("Exif chunk has no data."); processWarningOccurred("Exif chunk has no data.");
} }
else { else {
ImageInputStream stream = new MemoryCacheImageInputStream(data); // TODO: Consider returning ByteArrayImageInputStream from Segment.data()
return (CompoundDirectory) new TIFFReader().read(stream); try (ImageInputStream stream = new ByteArrayImageInputStream(exif.data, offset, exif.data.length - offset)) {
return (CompoundDirectory) new TIFFReader().read(stream);
// TODO: Directory offset of thumbnail is wrong/relative to container stream, causing trouble for the TIFFReader... }
catch (IIOException e) {
processWarningOccurred("Exif chunk is present, but can't be read: " + e.getMessage());
}
} }
} }
@@ -1100,6 +1082,7 @@ public final class JPEGImageReader extends ImageReaderBase {
// Read JFIF thumbnails if present // Read JFIF thumbnails if present
JFIF jfif = getJFIF(); JFIF jfif = getJFIF();
if (jfif != null && jfif.thumbnail != null) { 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)); thumbnails.add(new JFIFThumbnailReader(thumbnailProgressDelegator, imageIndex, thumbnails.size(), jfif));
} }
@@ -1110,6 +1093,7 @@ public final class JPEGImageReader extends ImageReaderBase {
case JFXX.JPEG: case JFXX.JPEG:
case JFXX.INDEXED: case JFXX.INDEXED:
case JFXX.RGB: 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)); thumbnails.add(new JFXXThumbnailReader(thumbnailProgressDelegator, getThumbnailReader(), imageIndex, thumbnails.size(), jfxx));
break; break;
default: default:
@@ -1123,48 +1107,77 @@ public final class JPEGImageReader extends ImageReaderBase {
Application exif = exifSegments.get(0); Application exif = exifSegments.get(0);
// Identifier is "Exif\0" + 1 byte pad // Identifier is "Exif\0" + 1 byte pad
int offset = exif.identifier.length() + 2; int dataOffset = exif.identifier.length() + 2;
if (exif.data.length <= offset) { if (exif.data.length <= dataOffset) {
processWarningOccurred("Exif chunk has no data."); processWarningOccurred("Exif chunk has no data.");
} }
else { else {
ImageInputStream stream = new ByteArrayImageInputStream(exif.data, offset, exif.data.length - offset); ImageInputStream stream = new ByteArrayImageInputStream(exif.data, dataOffset, exif.data.length - dataOffset);
CompoundDirectory exifMetadata = (CompoundDirectory) new TIFFReader().read(stream); try {
CompoundDirectory exifMetadata = (CompoundDirectory) new TIFFReader().read(stream);
if (exifMetadata.directoryCount() == 2) { if (exifMetadata.directoryCount() == 2) {
Directory ifd1 = exifMetadata.getDirectory(1); Directory ifd1 = exifMetadata.getDirectory(1);
// Compression: 1 = no compression, 6 = JPEG compression (default) // Compression: 1 = no compression, 6 = JPEG compression (default)
Entry compressionEntry = ifd1.getEntryById(TIFF.TAG_COMPRESSION); Entry compressionEntry = ifd1.getEntryById(TIFF.TAG_COMPRESSION);
int compression = compressionEntry == null ? 6 : ((Number) compressionEntry.getValue()).intValue(); int compression = compressionEntry == null ? 6 : ((Number) compressionEntry.getValue()).intValue();
if (compression == 6) { if (compression == 6) {
if (ifd1.getEntryById(TIFF.TAG_JPEG_INTERCHANGE_FORMAT) != null) { Entry jpegOffEntry = ifd1.getEntryById(TIFF.TAG_JPEG_INTERCHANGE_FORMAT);
Entry jpegLength = ifd1.getEntryById(TIFF.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH); if (jpegOffEntry != null) {
Entry jpegLenEntry = ifd1.getEntryById(TIFF.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
if ((jpegLength == null || ((Number) jpegLength.getValue()).longValue() > 0)) { // Test if Exif thumbnail is contained within the Exif segment (offset + length <= segment.length)
thumbnails.add(new EXIFThumbnailReader(thumbnailProgressDelegator, getThumbnailReader(), 0, thumbnails.size(), ifd1, stream)); 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 { else {
processWarningOccurred("EXIF IFD with empty (zero-length) thumbnail"); 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 { else {
processWarningOccurred("EXIF IFD with JPEG thumbnail missing JPEGInterchangeFormat tag"); processWarningOccurred("EXIF IFD with unknown compression (expected 1 or 6): " + compression);
} }
} }
else if (compression == 1) { }
if (ifd1.getEntryById(TIFF.TAG_STRIP_OFFSETS) != null) { catch (IIOException e) {
thumbnails.add(new EXIFThumbnailReader(thumbnailProgressDelegator, getThumbnailReader(), 0, thumbnails.size(), ifd1, stream)); processWarningOccurred("Exif chunk present, but can't be read: " + e.getMessage());
}
else {
processWarningOccurred("EXIF IFD with uncompressed thumbnail missing StripOffsets tag");
}
}
else {
processWarningOccurred("EXIF IFD with unknown compression (expected 1 or 6): " + compression);
}
} }
} }
} }
@@ -1209,7 +1222,15 @@ public final class JPEGImageReader extends ImageReaderBase {
public BufferedImage readThumbnail(int imageIndex, int thumbnailIndex) throws IOException { public BufferedImage readThumbnail(int imageIndex, int thumbnailIndex) throws IOException {
checkThumbnailBounds(imageIndex, thumbnailIndex); checkThumbnailBounds(imageIndex, thumbnailIndex);
return thumbnails.get(thumbnailIndex).read(); // processThumbnailStarted(imageIndex, thumbnailIndex);
// processThumbnailProgress(0f);
BufferedImage thumbnail = thumbnails.get(thumbnailIndex).read();;
// processThumbnailProgress(100f);
// processThumbnailComplete();
return thumbnail;
} }
// Metadata // Metadata
@@ -1386,7 +1407,7 @@ public final class JPEGImageReader extends ImageReaderBase {
final String arg = args[argIdx]; final String arg = args[argIdx];
if (arg.charAt(0) == '-') { if (arg.charAt(0) == '-') {
if (arg.equals("-s") || arg.equals("--subsample") && args.length > argIdx) { if (arg.equals("-s") || arg.equals("--subsample") && args.length > argIdx + 1) {
String[] sub = args[++argIdx].split(","); String[] sub = args[++argIdx].split(",");
try { try {
@@ -1405,7 +1426,7 @@ public final class JPEGImageReader extends ImageReaderBase {
System.err.println("Bad sub sampling (x,y): '" + args[argIdx] + "'"); System.err.println("Bad sub sampling (x,y): '" + args[argIdx] + "'");
} }
} }
else if (arg.equals("-r") || arg.equals("--roi") && args.length > argIdx) { else if (arg.equals("-r") || arg.equals("--roi") && args.length > argIdx + 1) {
String[] region = args[++argIdx].split(","); String[] region = args[++argIdx].split(",");
try { try {
@@ -572,12 +572,17 @@ final class JPEGSegmentImageInputStream extends ImageInputStreamImpl {
@Override @Override
public int read(final ImageInputStream stream) { public int read(final ImageInputStream stream) {
return data[pos++] & 0xff; return data.length > pos ? data[pos++] & 0xff : -1;
} }
@Override @Override
public int read(final ImageInputStream stream, byte[] b, int off, int len) { public int read(final ImageInputStream stream, byte[] b, int off, int len) {
int length = Math.min(data.length - pos, len); int dataLeft = data.length - pos;
if (dataLeft <= 0) {
return -1;
}
int length = Math.min(dataLeft, len);
System.arraycopy(data, pos, b, off, length); System.arraycopy(data, pos, b, off, length);
pos += length; pos += length;
@@ -32,6 +32,7 @@ package com.twelvemonkeys.imageio.plugins.jpeg;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest; import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
import com.twelvemonkeys.lang.StringUtil; import com.twelvemonkeys.lang.StringUtil;
import org.hamcrest.core.IsInstanceOf; import org.hamcrest.core.IsInstanceOf;
import org.junit.Test; import org.junit.Test;
import org.mockito.internal.matchers.GreaterThan; import org.mockito.internal.matchers.GreaterThan;
@@ -60,6 +61,9 @@ import java.util.List;
import java.util.*; import java.util.*;
import static com.twelvemonkeys.imageio.util.IIOUtil.lookupProviderByName; import static com.twelvemonkeys.imageio.util.IIOUtil.lookupProviderByName;
import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.junit.Assume.assumeNoException; import static org.junit.Assume.assumeNoException;
import static org.junit.Assume.assumeNotNull; import static org.junit.Assume.assumeNotNull;
@@ -77,7 +81,10 @@ import static org.mockito.Mockito.*;
*/ */
public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader> { public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader> {
private static final JPEGImageReaderSpi SPI = new JPEGImageReaderSpi(lookupDelegateProvider()); @Override
protected ImageReaderSpi createProvider() {
return new JPEGImageReaderSpi(lookupDelegateProvider());
}
private static ImageReaderSpi lookupDelegateProvider() { private static ImageReaderSpi lookupDelegateProvider() {
return lookupProviderByName(IIORegistry.getDefaultInstance(), "com.sun.imageio.plugins.jpeg.JPEGImageReaderSpi", ImageReaderSpi.class); return lookupProviderByName(IIORegistry.getDefaultInstance(), "com.sun.imageio.plugins.jpeg.JPEGImageReaderSpi", ImageReaderSpi.class);
@@ -133,26 +140,6 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
// More test data in specific tests below // More test data in specific tests below
} }
@Override
protected ImageReaderSpi createProvider() {
return SPI;
}
@Override
protected JPEGImageReader createReader() {
try {
return (JPEGImageReader) SPI.createReaderInstance();
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
protected Class<JPEGImageReader> getReaderClass() {
return JPEGImageReader.class;
}
@Override @Override
protected boolean allowsNullRawImageType() { protected boolean allowsNullRawImageType() {
return true; return true;
@@ -205,7 +192,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
} }
} }
private static void assertJPEGPixelsEqual(byte[] expected, byte[] actual, int actualOffset) { private static void assertJPEGPixelsEqual(byte[] expected, byte[] actual, @SuppressWarnings("SameParameterValue") int actualOffset) {
for (int i = 0; i < expected.length; i++) { for (int i = 0; i < expected.length; i++) {
assertEquals(String.format("Difference in pixel %d", i), expected[i], actual[i + actualOffset], 5); assertEquals(String.format("Difference in pixel %d", i), expected[i], actual[i + actualOffset], 5);
} }
@@ -550,7 +537,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
} }
@Test @Test
public void testBrokenReadRasterAfterGetMetadataException() { public void testBrokenReadRasterAfterGetMetadataException() throws IOException {
// See issue #107, from PDFBox team // See issue #107, from PDFBox team
JPEGImageReader reader = createReader(); JPEGImageReader reader = createReader();
@@ -585,7 +572,6 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
public void testSPIRecognizesBrokenJPEG() throws IOException { public void testSPIRecognizesBrokenJPEG() throws IOException {
// TODO: There's a bug in com.sun.imageio.plugins.png.PNGImageReaderSpi.canDecode // TODO: There's a bug in com.sun.imageio.plugins.png.PNGImageReaderSpi.canDecode
// causing files < 8 bytes to not be recognized as anything... // causing files < 8 bytes to not be recognized as anything...
ImageReaderSpi provider = createProvider();
for (TestData data : getBrokenTestData()) { for (TestData data : getBrokenTestData()) {
assertTrue(data.toString(), provider.canDecodeInput(data.getInputStream())); assertTrue(data.toString(), provider.canDecodeInput(data.getInputStream()));
} }
@@ -594,7 +580,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
// TODO: Consider wrapping the delegate in JPEGImageReader with methods that don't throw // TODO: Consider wrapping the delegate in JPEGImageReader with methods that don't throw
// runtime exceptions, and instead throw IIOException? // runtime exceptions, and instead throw IIOException?
@Test @Test
public void testBrokenGetRawImageType() { public void testBrokenGetRawImageType() throws IOException {
JPEGImageReader reader = createReader(); JPEGImageReader reader = createReader();
try { try {
@@ -620,7 +606,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
} }
@Test(timeout = 200) @Test(timeout = 200)
public void testBrokenGetRawImageTypeIgnoreMetadata() { public void testBrokenGetRawImageTypeIgnoreMetadata() throws IOException {
JPEGImageReader reader = createReader(); JPEGImageReader reader = createReader();
try { try {
@@ -646,7 +632,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
} }
@Test @Test
public void testBrokenGetImageTypes() { public void testBrokenGetImageTypes() throws IOException {
JPEGImageReader reader = createReader(); JPEGImageReader reader = createReader();
try { try {
@@ -672,7 +658,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
} }
@Test(timeout = 200) @Test(timeout = 200)
public void testBrokenGetImageTypesIgnoreMetadata() { public void testBrokenGetImageTypesIgnoreMetadata() throws IOException {
JPEGImageReader reader = createReader(); JPEGImageReader reader = createReader();
try { try {
@@ -698,7 +684,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
} }
@Test @Test
public void testBrokenRead() { public void testBrokenRead() throws IOException {
JPEGImageReader reader = createReader(); JPEGImageReader reader = createReader();
try { try {
@@ -724,7 +710,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
} }
@Test @Test
public void testBrokenGetDimensions() { public void testBrokenGetDimensions() throws IOException {
JPEGImageReader reader = createReader(); JPEGImageReader reader = createReader();
try { try {
@@ -753,7 +739,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
} }
@Test @Test
public void testBrokenGetImageMetadata() { public void testBrokenGetImageMetadata() throws IOException {
JPEGImageReader reader = createReader(); JPEGImageReader reader = createReader();
try { try {
@@ -1504,9 +1490,9 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
throw new AssertionError(String.format("Reading metadata failed for %s image %s: %s", testData, i, e.getMessage()), e); throw new AssertionError(String.format("Reading metadata failed for %s image %s: %s", testData, i, e.getMessage()), e);
} }
} }
catch (IIOException ignore) { catch (IIOException warn) {
// The reference reader will fail on certain images, we'll just ignore that // The reference reader will fail on certain images, we'll just ignore that
System.err.println(String.format("WARNING: Reading reference metadata failed for %s image %s: %s", testData, i, ignore.getMessage())); System.err.printf("WARNING: Reading reference metadata failed for %s image %s: %s%n", testData, i, warn.getMessage());
} }
} }
} }
@@ -1975,4 +1961,36 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
reader.dispose(); reader.dispose();
} }
} }
@Test(timeout = 1000L)
public void testInfiniteLoopCorrupt() throws IOException {
ImageReader reader = createReader();
try (ImageInputStream iis = ImageIO.createImageInputStream(getClassLoaderResource("/broken-jpeg/110115680-6d6dce80-7d84-11eb-99df-4cb21df3b09f.jpeg"))) {
reader.setInput(iis);
try {
reader.read(0, null);
}
catch (IIOException expected) {
assertThat(expected.getMessage(), allOf(containsString("SOF"), containsString("stream")));
}
}
}
@Test(timeout = 1000L)
public void testInfiniteLoopCorruptRaster() throws IOException {
ImageReader reader = createReader();
try (ImageInputStream iis = ImageIO.createImageInputStream(getClassLoaderResource("/broken-jpeg/110115680-6d6dce80-7d84-11eb-99df-4cb21df3b09f.jpeg"))) {
reader.setInput(iis);
try {
reader.readRaster(0, null);
}
catch (IIOException expected) {
assertThat(expected.getMessage(), allOf(containsString("SOF"), containsString("stream")));
}
}
}
} }
@@ -33,6 +33,7 @@ package com.twelvemonkeys.imageio.plugins.jpeg;
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream; import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
import com.twelvemonkeys.imageio.util.IIOUtil; import com.twelvemonkeys.imageio.util.IIOUtil;
import com.twelvemonkeys.imageio.util.ImageWriterAbstractTest; import com.twelvemonkeys.imageio.util.ImageWriterAbstractTest;
import org.junit.Test; import org.junit.Test;
import org.w3c.dom.NodeList; import org.w3c.dom.NodeList;
@@ -47,17 +48,23 @@ import javax.imageio.stream.ImageOutputStream;
import javax.imageio.stream.MemoryCacheImageOutputStream; import javax.imageio.stream.MemoryCacheImageOutputStream;
import java.awt.*; import java.awt.*;
import java.awt.color.ColorSpace; import java.awt.color.ColorSpace;
import java.awt.color.ICC_Profile;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.RenderedImage; import java.awt.image.RenderedImage;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Arrays; import java.util.Arrays;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/** /**
* JPEGImageWriterTest * JPEGImageWriterTest
@@ -66,22 +73,14 @@ import static org.junit.Assert.assertNotNull;
* @author last modified by $Author: haraldk$ * @author last modified by $Author: haraldk$
* @version $Id: JPEGImageWriterTest.java,v 1.0 06.02.12 17:05 haraldk Exp$ * @version $Id: JPEGImageWriterTest.java,v 1.0 06.02.12 17:05 haraldk Exp$
*/ */
public class JPEGImageWriterTest extends ImageWriterAbstractTest { public class JPEGImageWriterTest extends ImageWriterAbstractTest<JPEGImageWriter> {
private static final JPEGImageWriterSpi SPI = new JPEGImageWriterSpi(lookupDelegateProvider());
private static ImageWriterSpi lookupDelegateProvider() { private static ImageWriterSpi lookupDelegateProvider() {
return IIOUtil.lookupProviderByName(IIORegistry.getDefaultInstance(), "com.sun.imageio.plugins.jpeg.JPEGImageWriterSpi", ImageWriterSpi.class); return IIOUtil.lookupProviderByName(IIORegistry.getDefaultInstance(), "com.sun.imageio.plugins.jpeg.JPEGImageWriterSpi", ImageWriterSpi.class);
} }
@Override @Override
protected ImageWriter createImageWriter() { protected ImageWriterSpi createProvider() {
try { return new JPEGImageWriterSpi(lookupDelegateProvider());
return SPI.createWriterInstance();
}
catch (IOException e) {
throw new RuntimeException(e);
}
} }
@Override @Override
@@ -97,14 +96,18 @@ public class JPEGImageWriterTest extends ImageWriterAbstractTest {
} }
@Test @Test
public void testReaderForWriter() { public void testReaderForWriter() throws IOException {
ImageWriter writer = createImageWriter(); ImageWriter writer = createWriter();
ImageReader reader = ImageIO.getImageReader(writer); ImageReader reader = ImageIO.getImageReader(writer);
assertNotNull(reader); assertNotNull(reader);
assertEquals(writer.getClass().getPackage(), reader.getClass().getPackage()); assertEquals(writer.getClass().getPackage(), reader.getClass().getPackage());
} }
private ByteArrayOutputStream transcode(final ImageReader reader, final URL resource, final ImageWriter writer, int outCSType) throws IOException { private ByteArrayOutputStream transcode(final ImageReader reader, final URL resource, final ImageWriter writer, int outCSType) throws IOException {
return transcode(reader, resource, writer, outCSType, true);
}
private ByteArrayOutputStream transcode(final ImageReader reader, final URL resource, final ImageWriter writer, int outCSType, boolean embedICCProfile) throws IOException {
try (ImageInputStream input = ImageIO.createImageInputStream(resource)) { try (ImageInputStream input = ImageIO.createImageInputStream(resource)) {
reader.setInput(input); reader.setInput(input);
ImageTypeSpecifier specifier = null; ImageTypeSpecifier specifier = null;
@@ -125,6 +128,14 @@ public class JPEGImageWriterTest extends ImageWriterAbstractTest {
readParam.setDestinationType(specifier); readParam.setDestinationType(specifier);
IIOImage image = reader.readAll(0, readParam); IIOImage image = reader.readAll(0, readParam);
if (!embedICCProfile) {
// Get rid of the color model/icc profile
ColorSpace fakeCS = mock(ColorSpace.class);
when(fakeCS.getType()).thenReturn(ColorSpace.TYPE_CMYK);
when(fakeCS.getNumComponents()).thenReturn(4);
specifier = new ImageTypeSpecifier(new ComponentColorModel(fakeCS, false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE), image.getRenderedImage().getSampleModel());
}
// Write it back // Write it back
ByteArrayOutputStream bytes = new ByteArrayOutputStream(1024); ByteArrayOutputStream bytes = new ByteArrayOutputStream(1024);
try (ImageOutputStream output = new MemoryCacheImageOutputStream(bytes)) { try (ImageOutputStream output = new MemoryCacheImageOutputStream(bytes)) {
@@ -140,7 +151,7 @@ public class JPEGImageWriterTest extends ImageWriterAbstractTest {
@Test @Test
public void testTranscodeWithMetadataRGBtoRGB() throws IOException { public void testTranscodeWithMetadataRGBtoRGB() throws IOException {
ImageWriter writer = createImageWriter(); ImageWriter writer = createWriter();
ImageReader reader = ImageIO.getImageReader(writer); ImageReader reader = ImageIO.getImageReader(writer);
ByteArrayOutputStream stream = transcode(reader, getClassLoaderResource("/jpeg/jfif-jfxx-thumbnail-olympus-d320l.jpg"), writer, ColorSpace.TYPE_RGB); ByteArrayOutputStream stream = transcode(reader, getClassLoaderResource("/jpeg/jfif-jfxx-thumbnail-olympus-d320l.jpg"), writer, ColorSpace.TYPE_RGB);
@@ -151,11 +162,18 @@ public class JPEGImageWriterTest extends ImageWriterAbstractTest {
reader.setInput(new ByteArrayImageInputStream(stream.toByteArray())); reader.setInput(new ByteArrayImageInputStream(stream.toByteArray()));
BufferedImage image = reader.read(0); BufferedImage image = reader.read(0);
assertNotNull(image); assertNotNull(image);
// Test color space type RGB (encoded as YCbCr) in standard metadata
IIOMetadata metadata = reader.getImageMetadata(0);
IIOMetadataNode standard = (IIOMetadataNode) metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName);
NodeList colorSpaceType = standard.getElementsByTagName("ColorSpaceType");
assertEquals(1, colorSpaceType.getLength());
assertEquals("YCbCr", ((IIOMetadataNode) colorSpaceType.item(0)).getAttribute("name"));
} }
@Test @Test
public void testTranscodeWithMetadataCMYKtoCMYK() throws IOException { public void testTranscodeWithMetadataCMYKtoCMYK() throws IOException {
ImageWriter writer = createImageWriter(); ImageWriter writer = createWriter();
ImageReader reader = ImageIO.getImageReader(writer); ImageReader reader = ImageIO.getImageReader(writer);
ByteArrayOutputStream stream = transcode(reader, getClassLoaderResource("/jpeg/cmyk-sample-multiple-chunk-icc.jpg"), writer, ColorSpace.TYPE_CMYK); ByteArrayOutputStream stream = transcode(reader, getClassLoaderResource("/jpeg/cmyk-sample-multiple-chunk-icc.jpg"), writer, ColorSpace.TYPE_CMYK);
@@ -168,11 +186,65 @@ public class JPEGImageWriterTest extends ImageWriterAbstractTest {
assertEquals(100, image.getWidth()); assertEquals(100, image.getWidth());
assertEquals(100, image.getHeight()); assertEquals(100, image.getHeight());
// Test color space type CMYK in standard metadata
IIOMetadata metadata = reader.getImageMetadata(0); IIOMetadata metadata = reader.getImageMetadata(0);
IIOMetadataNode standard = (IIOMetadataNode) metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName); IIOMetadataNode standard = (IIOMetadataNode) metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName);
NodeList colorSpaceType = standard.getElementsByTagName("ColorSpaceType"); NodeList colorSpaceType = standard.getElementsByTagName("ColorSpaceType");
assertEquals(1, colorSpaceType.getLength()); assertEquals(1, colorSpaceType.getLength());
assertEquals("CMYK", ((IIOMetadataNode) colorSpaceType.item(0)).getAttribute("name")); assertEquals("CMYK", ((IIOMetadataNode) colorSpaceType.item(0)).getAttribute("name"));
// Test APP2/ICC_PROFILE segments form native metadata
IIOMetadataNode nativeMeta = (IIOMetadataNode) metadata.getAsTree(JPEGImage10Metadata.JAVAX_IMAGEIO_JPEG_IMAGE_1_0);
NodeList unknown = nativeMeta.getElementsByTagName("unknown");
assertEquals(11, unknown.getLength()); // We write longer segments than the original, so we get less segments
ByteArrayOutputStream iccSegments = new ByteArrayOutputStream(1024 * 1024);
for (int i = 0; i < unknown.getLength(); i++) {
IIOMetadataNode node = (IIOMetadataNode) unknown.item(i);
byte[] data = (byte[]) node.getUserObject();
// 226 -> E2, FFE2 -> APP2 marker, ICC_PROFILE
String markerId = "ICC_PROFILE";
if (node.getAttribute("MarkerTag").equals("226")
&& markerId.equals(new String(data, 0, markerId.length(), StandardCharsets.US_ASCII))) {
int offset = markerId.length() + 3; // ICC_PROFILE + null + index + count
iccSegments.write(Arrays.copyOfRange(data, offset, data.length));
}
}
ICC_Profile profile = ICC_Profile.getInstance(iccSegments.toByteArray());
assertNotNull(profile); // Assumption, we either have a valid profile, or getInstance blew up...
assertEquals(ColorSpace.TYPE_CMYK, profile.getColorSpaceType());
}
@Test
public void testTranscodeWithMetadataCMYKtoCMYKNoProfile() throws IOException {
ImageWriter writer = createWriter();
ImageReader reader = ImageIO.getImageReader(writer);
// TODO: Add flag to allow removing the ICC profile from image
ByteArrayOutputStream stream = transcode(reader, getClassLoaderResource("/jpeg/cmyk-sample-multiple-chunk-icc.jpg"), writer, ColorSpace.TYPE_CMYK, false);
reader.reset();
reader.setInput(new ByteArrayImageInputStream(stream.toByteArray()));
BufferedImage image = reader.read(0);
assertNotNull(image);
assertEquals(100, image.getWidth());
assertEquals(100, image.getHeight());
// Test color space type CMYK in standard metadata
IIOMetadata metadata = reader.getImageMetadata(0);
IIOMetadataNode standard = (IIOMetadataNode) metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName);
NodeList colorSpaceType = standard.getElementsByTagName("ColorSpaceType");
assertEquals(1, colorSpaceType.getLength());
assertEquals("CMYK", ((IIOMetadataNode) colorSpaceType.item(0)).getAttribute("name"));
// Test APP2/ICC_PROFILE segments form native metadata
IIOMetadataNode nativeMeta = (IIOMetadataNode) metadata.getAsTree(JPEGImage10Metadata.JAVAX_IMAGEIO_JPEG_IMAGE_1_0);
NodeList unknown = nativeMeta.getElementsByTagName("unknown");
assertEquals(0, unknown.getLength());
} }
// TODO: YCCK // TODO: YCCK
@@ -34,6 +34,7 @@ import com.twelvemonkeys.imageio.metadata.jpeg.JPEG;
import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegment; import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegment;
import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegmentUtil; import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegmentUtil;
import com.twelvemonkeys.imageio.stream.URLImageInputStreamSpi; import com.twelvemonkeys.imageio.stream.URLImageInputStreamSpi;
import org.junit.Test; import org.junit.Test;
import org.mockito.internal.matchers.LessOrEqual; import org.mockito.internal.matchers.LessOrEqual;
@@ -46,7 +47,9 @@ import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.util.List; import java.util.List;
import static org.junit.Assert.*; import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
/** /**
* JPEGSegmentImageInputStreamTest * JPEGSegmentImageInputStreamTest
@@ -225,4 +228,29 @@ public class JPEGSegmentImageInputStreamTest {
assertEquals(-1, iis.read()); assertEquals(-1, iis.read());
assertEquals(0x2012, iis.getStreamPosition()); assertEquals(0x2012, iis.getStreamPosition());
} }
@Test(timeout = 1000L)
public void testInfiniteLoopCorrupt() throws IOException {
try (ImageInputStream stream = new JPEGSegmentImageInputStream(ImageIO.createImageInputStream(getClassLoaderResource("/broken-jpeg/110115680-6d6dce80-7d84-11eb-99df-4cb21df3b09f.jpeg")))) {
long length = 0;
while (stream.read() != -1) {
length++;
}
assertEquals(25504L, length); // Sanity check: same as file size, except..?
}
try (ImageInputStream stream = new JPEGSegmentImageInputStream(ImageIO.createImageInputStream(getClassLoaderResource("/broken-jpeg/110115680-6d6dce80-7d84-11eb-99df-4cb21df3b09f.jpeg")))) {
long length = 0;
byte[] buffer = new byte[1024];
int read;
while ((read = stream.read(buffer)) != -1) {
length += read;
}
assertEquals(25504L, length); // Sanity check: same as file size, except..?
}
}
} }
Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

+2 -1
View File
@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId> <artifactId>imageio</artifactId>
<version>3.6</version> <version>3.6.5-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>imageio-metadata</artifactId> <artifactId>imageio-metadata</artifactId>
@@ -21,6 +21,7 @@
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-core</artifactId> <artifactId>imageio-core</artifactId>
<type>test-jar</type> <type>test-jar</type>
<scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>
@@ -30,12 +30,12 @@
package com.twelvemonkeys.imageio.metadata; package com.twelvemonkeys.imageio.metadata;
import com.twelvemonkeys.lang.Validate;
import com.twelvemonkeys.util.CollectionUtil;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.util.Arrays; import java.util.Arrays;
import com.twelvemonkeys.lang.Validate;
import com.twelvemonkeys.util.CollectionUtil;
/** /**
* AbstractEntry * AbstractEntry
* *
@@ -84,17 +84,21 @@ public abstract class AbstractEntry implements Entry {
} }
public String getValueAsString() { public String getValueAsString() {
if (valueCount() > 1) { int count = valueCount();
if (valueCount() < 16) {
if (count == 0 && value != null && value.getClass().isArray() && Array.getLength(value) == 0) {
return "";
}
if (count > 1) {
if (count < 16) {
return arrayToString(value); return arrayToString(value);
} }
else { else {
String first = arrayToString(CollectionUtil.subArray(value, 0, 4)); String first = arrayToString(CollectionUtil.subArray(value, 0, 4));
String last = arrayToString(CollectionUtil.subArray(value, valueCount() - 4, 4)); String last = arrayToString(CollectionUtil.subArray(value, count - 4, 4));
return String.format("%s ... %s (%d)", first.substring(0, first.length() - 1), last.substring(1), valueCount()); return String.format("%s ... %s (%d)", first.substring(0, first.length() - 1), last.substring(1), count);
} }
} }
if (value != null && value.getClass().isArray() && Array.getLength(value) == 1) { if (value != null && value.getClass().isArray() && Array.getLength(value) == 1) {
return String.valueOf(Array.get(value, 0)); return String.valueOf(Array.get(value, 0));
} }
@@ -41,7 +41,6 @@ package com.twelvemonkeys.imageio.metadata.exif;
* *
* @deprecated Use com.twelvemonkeys.imageio.metadata.tiff.Rational instead. * @deprecated Use com.twelvemonkeys.imageio.metadata.tiff.Rational instead.
*/ */
@SuppressWarnings("deprecation")
public final class Rational extends Number implements Comparable<Rational> { public final class Rational extends Number implements Comparable<Rational> {
private final com.twelvemonkeys.imageio.metadata.tiff.Rational delegate; private final com.twelvemonkeys.imageio.metadata.tiff.Rational delegate;
@@ -48,7 +48,7 @@ import java.io.ByteArrayOutputStream;
import java.io.EOFException; import java.io.EOFException;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.Charset; import java.nio.charset.StandardCharsets;
import java.util.*; import java.util.*;
import static com.twelvemonkeys.lang.Validate.notNull; import static com.twelvemonkeys.lang.Validate.notNull;
@@ -154,7 +154,7 @@ public final class JPEGSegmentUtil {
} }
static String asAsciiString(final byte[] data, final int offset, final int length) { static String asAsciiString(final byte[] data, final int offset, final int length) {
return new String(data, offset, length, Charset.forName("ascii")); return new String(data, offset, length, StandardCharsets.US_ASCII);
} }
static void readSOI(final ImageInputStream stream) throws IOException { static void readSOI(final ImageInputStream stream) throws IOException {
@@ -348,6 +348,7 @@ public final class JPEGSegmentUtil {
else if ("Photoshop 3.0".equals(segment.identifier())) { else if ("Photoshop 3.0".equals(segment.identifier())) {
// TODO: The "Photoshop 3.0" segment contains several image resources, of which one might contain // TODO: The "Photoshop 3.0" segment contains several image resources, of which one might contain
// IPTC metadata. Probably duplicated in the XMP though... // IPTC metadata. Probably duplicated in the XMP though...
// TODO: Merge multiple APP13 segments to single resource block
ImageInputStream stream = new ByteArrayImageInputStream(segment.data, segment.offset(), segment.length()); ImageInputStream stream = new ByteArrayImageInputStream(segment.data, segment.offset(), segment.length());
Directory psd = new PSDReader().read(stream); Directory psd = new PSDReader().read(stream);
Entry iccEntry = psd.getEntryById(PSD.RES_ICC_PROFILE); Entry iccEntry = psd.getEntryById(PSD.RES_ICC_PROFILE);
@@ -359,6 +360,7 @@ public final class JPEGSegmentUtil {
System.err.println(TIFFReader.HexDump.dump(segment.data)); System.err.println(TIFFReader.HexDump.dump(segment.data));
} }
else if ("ICC_PROFILE".equals(segment.identifier())) { else if ("ICC_PROFILE".equals(segment.identifier())) {
// TODO: Merge multiple APP2 segments to single ICC Profile
// Skip // Skip
} }
else { else {
@@ -143,7 +143,7 @@ public final class Rational extends Number implements Comparable<Rational> {
double thisVal = doubleValue(); double thisVal = doubleValue();
double otherVal = pOther.doubleValue(); double otherVal = pOther.doubleValue();
return thisVal < otherVal ? -1 : thisVal == otherVal ? 0 : 1; return Double.compare(thisVal, otherVal);
} }
/// Object overrides /// Object overrides
@@ -30,15 +30,8 @@
package com.twelvemonkeys.imageio.metadata.tiff; package com.twelvemonkeys.imageio.metadata.tiff;
import com.twelvemonkeys.imageio.metadata.Directory; import static com.twelvemonkeys.imageio.metadata.tiff.TIFFEntry.getValueLength;
import com.twelvemonkeys.imageio.metadata.Entry;
import com.twelvemonkeys.imageio.metadata.MetadataReader;
import com.twelvemonkeys.lang.StringUtil;
import com.twelvemonkeys.lang.Validate;
import javax.imageio.IIOException;
import javax.imageio.ImageIO;
import javax.imageio.stream.ImageInputStream;
import java.io.EOFException; import java.io.EOFException;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@@ -46,7 +39,15 @@ import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.*; import java.util.*;
import static com.twelvemonkeys.imageio.metadata.tiff.TIFFEntry.getValueLength; import javax.imageio.IIOException;
import javax.imageio.ImageIO;
import javax.imageio.stream.ImageInputStream;
import com.twelvemonkeys.imageio.metadata.Directory;
import com.twelvemonkeys.imageio.metadata.Entry;
import com.twelvemonkeys.imageio.metadata.MetadataReader;
import com.twelvemonkeys.lang.StringUtil;
import com.twelvemonkeys.lang.Validate;
/** /**
* TIFFReader * TIFFReader
@@ -336,7 +337,8 @@ public final class TIFFReader extends MetadataReader {
else { else {
long valueOffset = readOffset(pInput); // This is the *value* iff the value size is <= offsetSize long valueOffset = readOffset(pInput); // This is the *value* iff the value size is <= offsetSize
if (length > 0 && length < valueOffset + valueLength) { // Note: This a precaution
if (count >= Integer.MAX_VALUE || length > 0 && length < valueOffset + valueLength) {
value = new EOFException(String.format("TIFF value offset or size too large: %d/%d bytes (length: %d bytes)", valueOffset, valueLength, length)); value = new EOFException(String.format("TIFF value offset or size too large: %d/%d bytes (length: %d bytes)", valueOffset, valueLength, length));
} }
else { else {
@@ -367,7 +369,7 @@ public final class TIFFReader extends MetadataReader {
long pos = pInput.getStreamPosition(); long pos = pInput.getStreamPosition();
try { try {
pInput.seek(pOffset); pInput.seek(pOffset);
return readValue(pInput, pType, pCount); return readValue(pInput, pType, pCount, longOffsets);
} }
catch (EOFException e) { catch (EOFException e) {
// TODO: Add warning listener API and report problem to client code // TODO: Add warning listener API and report problem to client code
@@ -379,10 +381,10 @@ public final class TIFFReader extends MetadataReader {
} }
private Object readValueInLine(final ImageInputStream pInput, final short pType, final int pCount) throws IOException { private Object readValueInLine(final ImageInputStream pInput, final short pType, final int pCount) throws IOException {
return readValue(pInput, pType, pCount); return readValue(pInput, pType, pCount, longOffsets);
} }
private static Object readValue(final ImageInputStream pInput, final short pType, final int pCount) throws IOException { private static Object readValue(final ImageInputStream pInput, final short pType, final int pCount, boolean bigTIFF) throws IOException {
// TODO: Review value "widening" for the unsigned types. Right now it's inconsistent. Should we leave it to client code? // TODO: Review value "widening" for the unsigned types. Right now it's inconsistent. Should we leave it to client code?
// TODO: New strategy: Leave data as is, instead perform the widening in TIFFEntry.getValue. // TODO: New strategy: Leave data as is, instead perform the widening in TIFFEntry.getValue.
// TODO: Add getValueByte/getValueUnsignedByte/getValueShort/getValueUnsignedShort/getValueInt/etc... in API. // TODO: Add getValueByte/getValueUnsignedByte/getValueShort/getValueUnsignedShort/getValueInt/etc... in API.
@@ -513,24 +515,24 @@ public final class TIFFReader extends MetadataReader {
case TIFF.TYPE_LONG8: case TIFF.TYPE_LONG8:
case TIFF.TYPE_SLONG8: case TIFF.TYPE_SLONG8:
case TIFF.TYPE_IFD8: case TIFF.TYPE_IFD8:
// TODO: Assert BigTiff (version == 43) if (bigTIFF) {
if (pCount == 1) {
long val = pInput.readLong();
if (pType != TIFF.TYPE_SLONG8 && val < 0) {
throw new IIOException(String.format("Value > %s", Long.MAX_VALUE));
}
if (pCount == 1) { return val;
long val = pInput.readLong();
if (pType != TIFF.TYPE_SLONG8 && val < 0) {
throw new IIOException(String.format("Value > %s", Long.MAX_VALUE));
} }
return val; long[] longs = new long[pCount];
} for (int i = 0; i < pCount; i++) {
longs[i] = pInput.readLong();
}
long[] longs = new long[pCount]; return longs;
for (int i = 0; i < pCount; i++) {
longs[i] = pInput.readLong();
} }
return longs;
default: default:
// Spec says skip unknown values // Spec says skip unknown values
return new Unknown(pType, pCount, pos); return new Unknown(pType, pCount, pos);
@@ -31,10 +31,11 @@
package com.twelvemonkeys.imageio.metadata; package com.twelvemonkeys.imageio.metadata;
import com.twelvemonkeys.imageio.stream.URLImageInputStreamSpi; import com.twelvemonkeys.imageio.stream.URLImageInputStreamSpi;
import org.hamcrest.Description; import org.hamcrest.Description;
import org.hamcrest.Matcher; import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import org.junit.Test; import org.junit.Test;
import org.junit.internal.matchers.TypeSafeMatcher;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import javax.imageio.spi.IIORegistry; import javax.imageio.spi.IIORegistry;
@@ -58,7 +59,7 @@ public abstract class MetadataReaderAbstractTest {
ImageIO.setUseCache(false); ImageIO.setUseCache(false);
} }
protected final URL getResource(final String name) throws IOException { protected final URL getResource(final String name) {
return getClass().getResource(name); return getClass().getResource(name);
} }
@@ -35,6 +35,7 @@ import com.twelvemonkeys.imageio.metadata.Directory;
import com.twelvemonkeys.imageio.metadata.MetadataReaderAbstractTest; import com.twelvemonkeys.imageio.metadata.MetadataReaderAbstractTest;
import com.twelvemonkeys.imageio.metadata.tiff.TIFF; import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
import com.twelvemonkeys.imageio.stream.SubImageInputStream; import com.twelvemonkeys.imageio.stream.SubImageInputStream;
import org.junit.Test; import org.junit.Test;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
@@ -44,6 +45,7 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.*; import static org.junit.Assert.*;
/** /**
@@ -32,14 +32,15 @@ package com.twelvemonkeys.imageio.metadata.iptc;
import com.twelvemonkeys.imageio.metadata.Directory; import com.twelvemonkeys.imageio.metadata.Directory;
import com.twelvemonkeys.imageio.metadata.MetadataReaderAbstractTest; import com.twelvemonkeys.imageio.metadata.MetadataReaderAbstractTest;
import org.junit.Test; import org.junit.Test;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
/** /**
* IPTCReaderTest * IPTCReaderTest
@@ -30,6 +30,20 @@
package com.twelvemonkeys.imageio.metadata.tiff; package com.twelvemonkeys.imageio.metadata.tiff;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.*;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import javax.imageio.ImageIO;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.MemoryCacheImageInputStream;
import org.junit.Test;
import com.twelvemonkeys.imageio.metadata.CompoundDirectory; import com.twelvemonkeys.imageio.metadata.CompoundDirectory;
import com.twelvemonkeys.imageio.metadata.Directory; import com.twelvemonkeys.imageio.metadata.Directory;
import com.twelvemonkeys.imageio.metadata.Entry; import com.twelvemonkeys.imageio.metadata.Entry;
@@ -37,16 +51,6 @@ import com.twelvemonkeys.imageio.metadata.MetadataReaderAbstractTest;
import com.twelvemonkeys.imageio.metadata.exif.EXIF; import com.twelvemonkeys.imageio.metadata.exif.EXIF;
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream; import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
import com.twelvemonkeys.imageio.stream.SubImageInputStream; import com.twelvemonkeys.imageio.stream.SubImageInputStream;
import org.junit.Test;
import javax.imageio.ImageIO;
import javax.imageio.stream.ImageInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.junit.Assert.*;
/** /**
* TIFFReaderTest * TIFFReaderTest
@@ -346,6 +350,21 @@ public class TIFFReaderTest extends MetadataReaderAbstractTest {
} }
} }
@Test
public void testReadWithoutOOME() throws IOException {
// This EXIF segment from a JPEG contains an Interop IFD, containing a weird value that could be interpreted
// as a huge SLONG8 field (valid for BigTIFF only).
// OutOfMemoryError would only happen if length of stream is not known (ie. reading from underlying stream).
try (InputStream stream = getResource("/exif/exif-bad-interop-oome.bin").openStream()) {
CompoundDirectory directory = (CompoundDirectory) createReader().read(new MemoryCacheImageInputStream(stream));
assertEquals(2, directory.directoryCount());
assertEquals(11, directory.getDirectory(0).size());
assertEquals(6, directory.getDirectory(1).size());
assertEquals("Picasa", directory.getDirectory(0).getEntryById(TIFF.TAG_SOFTWARE).getValue());
assertEquals("2020:11:17 16:05:37", directory.getDirectory(0).getEntryById(TIFF.TAG_DATE_TIME).getValueAsString());
}
}
@Test(timeout = 200) @Test(timeout = 200)
public void testReadCyclicExifWithoutLoopOrOOME() throws IOException { public void testReadCyclicExifWithoutLoopOrOOME() throws IOException {
// This EXIF segment has an interesting bug... // This EXIF segment has an interesting bug...
@@ -34,6 +34,7 @@ import com.twelvemonkeys.imageio.metadata.CompoundDirectory;
import com.twelvemonkeys.imageio.metadata.Directory; import com.twelvemonkeys.imageio.metadata.Directory;
import com.twelvemonkeys.imageio.metadata.Entry; import com.twelvemonkeys.imageio.metadata.Entry;
import com.twelvemonkeys.imageio.metadata.MetadataReaderAbstractTest; import com.twelvemonkeys.imageio.metadata.MetadataReaderAbstractTest;
import org.junit.Test; import org.junit.Test;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
@@ -46,7 +47,9 @@ import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.instanceOf;
import static org.junit.Assert.*; import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/** /**
* XMPReaderTest * XMPReaderTest
@@ -377,6 +380,7 @@ public class XMPReaderTest extends MetadataReaderAbstractTest {
// photoshop|http://ns.adobe.com/photoshop/1.0/ // photoshop|http://ns.adobe.com/photoshop/1.0/
Directory photoshop = getDirectoryByNS(compound, XMP.NS_PHOTOSHOP); Directory photoshop = getDirectoryByNS(compound, XMP.NS_PHOTOSHOP);
assertNotNull(photoshop);
assertEquals(3, photoshop.size()); assertEquals(3, photoshop.size());
assertThat(photoshop.getEntryById("http://ns.adobe.com/photoshop/1.0/ColorMode"), hasValue("1")); assertThat(photoshop.getEntryById("http://ns.adobe.com/photoshop/1.0/ColorMode"), hasValue("1"));
assertThat(photoshop.getEntryById("http://ns.adobe.com/photoshop/1.0/ICCProfile"), hasValue("Dot Gain 20%")); assertThat(photoshop.getEntryById("http://ns.adobe.com/photoshop/1.0/ICCProfile"), hasValue("Dot Gain 20%"));
@@ -392,6 +396,8 @@ public class XMPReaderTest extends MetadataReaderAbstractTest {
// xapMM|http://ns.adobe.com/xap/1.0/mm/ // xapMM|http://ns.adobe.com/xap/1.0/mm/
Directory mm = getDirectoryByNS(compound, XMP.NS_XAP_MM); Directory mm = getDirectoryByNS(compound, XMP.NS_XAP_MM);
assertNotNull(mm);
assertEquals(3, mm.size()); assertEquals(3, mm.size());
assertThat(directory.getEntryById("http://ns.adobe.com/xap/1.0/mm/DocumentID"), hasValue("uuid:6DCA50CC7D53DD119F20F5A7EA4C9BEC")); assertThat(directory.getEntryById("http://ns.adobe.com/xap/1.0/mm/DocumentID"), hasValue("uuid:6DCA50CC7D53DD119F20F5A7EA4C9BEC"));
assertThat(directory.getEntryById("http://ns.adobe.com/xap/1.0/mm/InstanceID"), hasValue("uuid:6ECA50CC7D53DD119F20F5A7EA4C9BEC")); assertThat(directory.getEntryById("http://ns.adobe.com/xap/1.0/mm/InstanceID"), hasValue("uuid:6ECA50CC7D53DD119F20F5A7EA4C9BEC"));
@@ -416,6 +422,8 @@ public class XMPReaderTest extends MetadataReaderAbstractTest {
// dc|http://purl.org/dc/elements/1.1/ // dc|http://purl.org/dc/elements/1.1/
Directory dc = getDirectoryByNS(compound, XMP.NS_DC); Directory dc = getDirectoryByNS(compound, XMP.NS_DC);
assertNotNull(dc);
assertEquals(1, dc.size()); assertEquals(1, dc.size());
assertThat(dc.getEntryById("http://purl.org/dc/elements/1.1/format"), hasValue("image/jpeg")); assertThat(dc.getEntryById("http://purl.org/dc/elements/1.1/format"), hasValue("image/jpeg"));
@@ -430,6 +438,8 @@ public class XMPReaderTest extends MetadataReaderAbstractTest {
// tiff|http://ns.adobe.com/tiff/1.0/ // tiff|http://ns.adobe.com/tiff/1.0/
Directory tiff = getDirectoryByNS(compound, XMP.NS_TIFF); Directory tiff = getDirectoryByNS(compound, XMP.NS_TIFF);
assertNotNull(tiff);
assertEquals(5, tiff.size()); assertEquals(5, tiff.size());
assertThat(tiff.getEntryById("http://ns.adobe.com/tiff/1.0/Orientation"), hasValue("1")); assertThat(tiff.getEntryById("http://ns.adobe.com/tiff/1.0/Orientation"), hasValue("1"));
assertThat(tiff.getEntryById("http://ns.adobe.com/tiff/1.0/XResolution"), hasValue("720000/10000")); assertThat(tiff.getEntryById("http://ns.adobe.com/tiff/1.0/XResolution"), hasValue("720000/10000"));
@@ -447,6 +457,8 @@ public class XMPReaderTest extends MetadataReaderAbstractTest {
// xap|http://ns.adobe.com/xap/1.0/ // xap|http://ns.adobe.com/xap/1.0/
Directory xap = getDirectoryByNS(compound, XMP.NS_XAP); Directory xap = getDirectoryByNS(compound, XMP.NS_XAP);
assertNotNull(xap);
assertEquals(4, xap.size()); assertEquals(4, xap.size());
assertThat(xap.getEntryById("http://ns.adobe.com/xap/1.0/ModifyDate"), hasValue("2008-07-16T14:44:49-07:00")); assertThat(xap.getEntryById("http://ns.adobe.com/xap/1.0/ModifyDate"), hasValue("2008-07-16T14:44:49-07:00"));
assertThat(xap.getEntryById("http://ns.adobe.com/xap/1.0/CreatorTool"), hasValue("Adobe Photoshop CS3 Windows")); assertThat(xap.getEntryById("http://ns.adobe.com/xap/1.0/CreatorTool"), hasValue("Adobe Photoshop CS3 Windows"));
@@ -463,6 +475,8 @@ public class XMPReaderTest extends MetadataReaderAbstractTest {
// exif|http://ns.adobe.com/exif/1.0/ // exif|http://ns.adobe.com/exif/1.0/
Directory exif = getDirectoryByNS(compound, XMP.NS_EXIF); Directory exif = getDirectoryByNS(compound, XMP.NS_EXIF);
assertNotNull(exif);
assertEquals(4, exif.size()); assertEquals(4, exif.size());
assertThat(exif.getEntryById("http://ns.adobe.com/exif/1.0/ColorSpace"), hasValue("-1")); // SIC. Same as unsigned short 65535, meaning "uncalibrated"? assertThat(exif.getEntryById("http://ns.adobe.com/exif/1.0/ColorSpace"), hasValue("-1")); // SIC. Same as unsigned short 65535, meaning "uncalibrated"?
assertThat(exif.getEntryById("http://ns.adobe.com/exif/1.0/PixelXDimension"), hasValue("426")); assertThat(exif.getEntryById("http://ns.adobe.com/exif/1.0/PixelXDimension"), hasValue("426"));
+2 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId> <artifactId>imageio</artifactId>
<version>3.6</version> <version>3.6.5-SNAPSHOT</version>
</parent> </parent>
<artifactId>imageio-pcx</artifactId> <artifactId>imageio-pcx</artifactId>
<name>TwelveMonkeys :: ImageIO :: PCX plugin</name> <name>TwelveMonkeys :: ImageIO :: PCX plugin</name>
@@ -21,6 +21,7 @@
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-core</artifactId> <artifactId>imageio-core</artifactId>
<type>test-jar</type> <type>test-jar</type>
<scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>
@@ -31,6 +31,7 @@
package com.twelvemonkeys.imageio.plugins.dcx; package com.twelvemonkeys.imageio.plugins.dcx;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest; import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
import org.junit.Test; import org.junit.Test;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
@@ -54,12 +55,6 @@ import static org.junit.Assert.assertNotNull;
* @version $Id: DCXImageReaderTest.java,v 1.0 03.07.14 22:28 haraldk Exp$ * @version $Id: DCXImageReaderTest.java,v 1.0 03.07.14 22:28 haraldk Exp$
*/ */
public class DCXImageReaderTest extends ImageReaderAbstractTest<DCXImageReader> { public class DCXImageReaderTest extends ImageReaderAbstractTest<DCXImageReader> {
@Override
protected List<TestData> getTestData() {
return Collections.singletonList(
new TestData(getClassLoaderResource("/dcx/input.dcx"), new Dimension(70, 46)) // RLE encoded RGB (the only sample I've found)
);
}
@Override @Override
protected ImageReaderSpi createProvider() { protected ImageReaderSpi createProvider() {
@@ -67,13 +62,10 @@ public class DCXImageReaderTest extends ImageReaderAbstractTest<DCXImageReader>
} }
@Override @Override
protected Class<DCXImageReader> getReaderClass() { protected List<TestData> getTestData() {
return DCXImageReader.class; return Collections.singletonList(
} new TestData(getClassLoaderResource("/dcx/input.dcx"), new Dimension(70, 46)) // RLE encoded RGB (the only sample I've found)
);
@Override
protected DCXImageReader createReader() {
return new DCXImageReader(createProvider());
} }
@Override @Override
@@ -31,6 +31,7 @@
package com.twelvemonkeys.imageio.plugins.pcx; package com.twelvemonkeys.imageio.plugins.pcx;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest; import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
import org.junit.Test; import org.junit.Test;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
@@ -55,10 +56,15 @@ import static org.junit.Assert.assertNotNull;
* @version $Id: PCXImageReaderTest.java,v 1.0 03.07.14 22:28 haraldk Exp$ * @version $Id: PCXImageReaderTest.java,v 1.0 03.07.14 22:28 haraldk Exp$
*/ */
public class PCXImageReaderTest extends ImageReaderAbstractTest<PCXImageReader> { public class PCXImageReaderTest extends ImageReaderAbstractTest<PCXImageReader> {
@Override
protected ImageReaderSpi createProvider() {
return new PCXImageReaderSpi();
}
@Override @Override
protected List<TestData> getTestData() { protected List<TestData> getTestData() {
return Arrays.asList( return Arrays.asList(
new TestData(getClassLoaderResource("/pcx/MARBLES.PCX"), new Dimension(1419, 1001)), // RLE encoded RGB new TestData(getClassLoaderResource("/pcx/input.pcx"), new Dimension(70, 46)), // RLE encoded RGB
new TestData(getClassLoaderResource("/pcx/lena.pcx"), new Dimension(512, 512)), // RLE encoded RGB new TestData(getClassLoaderResource("/pcx/lena.pcx"), new Dimension(512, 512)), // RLE encoded RGB
new TestData(getClassLoaderResource("/pcx/lena2.pcx"), new Dimension(512, 512)), // RLE encoded, 256 color indexed (8 bps/1 channel) new TestData(getClassLoaderResource("/pcx/lena2.pcx"), new Dimension(512, 512)), // RLE encoded, 256 color indexed (8 bps/1 channel)
new TestData(getClassLoaderResource("/pcx/lena3.pcx"), new Dimension(512, 512)), // RLE encoded, 16 color indexed (4 bps/1 channel) new TestData(getClassLoaderResource("/pcx/lena3.pcx"), new Dimension(512, 512)), // RLE encoded, 16 color indexed (4 bps/1 channel)
@@ -70,6 +76,7 @@ public class PCXImageReaderTest extends ImageReaderAbstractTest<PCXImageReader>
new TestData(getClassLoaderResource("/pcx/lena9.pcx"), new Dimension(512, 512)), // RLE encoded, 2 color indexed (1 bps/1 channel) new TestData(getClassLoaderResource("/pcx/lena9.pcx"), new Dimension(512, 512)), // RLE encoded, 2 color indexed (1 bps/1 channel)
new TestData(getClassLoaderResource("/pcx/lena10.pcx"), new Dimension(512, 512)), // RLE encoded, 16 color indexed (4 bps/1 channel) (uses only 8 colors) new TestData(getClassLoaderResource("/pcx/lena10.pcx"), new Dimension(512, 512)), // RLE encoded, 16 color indexed (4 bps/1 channel) (uses only 8 colors)
new TestData(getClassLoaderResource("/pcx/DARKSTAR.PCX"), new Dimension(88, 52)), // RLE encoded monochrome (1 bps/1 channel) new TestData(getClassLoaderResource("/pcx/DARKSTAR.PCX"), new Dimension(88, 52)), // RLE encoded monochrome (1 bps/1 channel)
new TestData(getClassLoaderResource("/pcx/MARBLES.PCX"), new Dimension(1419, 1001)), // RLE encoded RGB
new TestData(getClassLoaderResource("/pcx/no-palette-monochrome.pcx"), new Dimension(128, 152)), // RLE encoded monochrome (1 bps/1 channel) new TestData(getClassLoaderResource("/pcx/no-palette-monochrome.pcx"), new Dimension(128, 152)), // RLE encoded monochrome (1 bps/1 channel)
// See cga-pcx.txt, however, the text seems to be in error, the bits can not not as described // See cga-pcx.txt, however, the text seems to be in error, the bits can not not as described
new TestData(getClassLoaderResource("/pcx/CGA_BW.PCX"), new Dimension(640, 200)), // RLE encoded indexed (CGA mode) new TestData(getClassLoaderResource("/pcx/CGA_BW.PCX"), new Dimension(640, 200)), // RLE encoded indexed (CGA mode)
@@ -79,21 +86,6 @@ public class PCXImageReaderTest extends ImageReaderAbstractTest<PCXImageReader>
); );
} }
@Override
protected ImageReaderSpi createProvider() {
return new PCXImageReaderSpi();
}
@Override
protected Class<PCXImageReader> getReaderClass() {
return PCXImageReader.class;
}
@Override
protected PCXImageReader createReader() {
return new PCXImageReader(createProvider());
}
@Override @Override
protected List<String> getFormatNames() { protected List<String> getFormatNames() {
return Arrays.asList("PCX", "pcx"); return Arrays.asList("PCX", "pcx");
Binary file not shown.
+2 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId> <artifactId>imageio</artifactId>
<version>3.6</version> <version>3.6.5-SNAPSHOT</version>
</parent> </parent>
<artifactId>imageio-pdf</artifactId> <artifactId>imageio-pdf</artifactId>
<name>TwelveMonkeys :: ImageIO :: PDF plugin</name> <name>TwelveMonkeys :: ImageIO :: PDF plugin</name>
@@ -22,6 +22,7 @@
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-core</artifactId> <artifactId>imageio-core</artifactId>
<type>test-jar</type> <type>test-jar</type>
<scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>
+2 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId> <artifactId>imageio</artifactId>
<version>3.6</version> <version>3.6.5-SNAPSHOT</version>
</parent> </parent>
<artifactId>imageio-pict</artifactId> <artifactId>imageio-pict</artifactId>
<name>TwelveMonkeys :: ImageIO :: PICT plugin</name> <name>TwelveMonkeys :: ImageIO :: PICT plugin</name>
@@ -19,6 +19,7 @@
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-core</artifactId> <artifactId>imageio-core</artifactId>
<type>test-jar</type> <type>test-jar</type>
<scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
@@ -60,6 +60,13 @@ public final class PICTImageReaderSpi extends ImageReaderSpiBase {
} }
ImageInputStream stream = (ImageInputStream) pSource; ImageInputStream stream = (ImageInputStream) pSource;
// PICT format don't have good magic and our method often gives false positives,
// We'll check for other known formats (BMP, GIF, JPEG, PNG, PSD, TIFF) first
if (isOtherFormat(stream)) {
return false;
}
stream.mark(); stream.mark();
try { try {
@@ -68,13 +75,12 @@ public final class PICTImageReaderSpi extends ImageReaderSpiBase {
return true; return true;
} }
else { else {
// Skip header 512 bytes for file-based streams // We need to reset AND set mark again, to make sure the reset call in
stream.reset();
// We need to set mark again, to make sure the reset call in
// the finally block will not consume existing marks // the finally block will not consume existing marks
stream.reset();
stream.mark(); stream.mark();
// Skip header 512 bytes for file-based streams
skipNullHeader(stream); skipNullHeader(stream);
} }
@@ -88,6 +94,60 @@ public final class PICTImageReaderSpi extends ImageReaderSpiBase {
} }
} }
// TODO: Candidate util method.
// Might need to be able to exclude some formats
// Might need to return the format matched...
static boolean isOtherFormat(final ImageInputStream stream) throws IOException {
stream.mark();
try {
byte[] signature = new byte[8];
stream.readFully(signature);
// BMP: off: 0, len: 2, magic: 'B', 'M'
// GIF: off: 0, len: 6, magic: 'G', 'I', 'F', '8', ('7' | '9'), 'a' (use only "GIF8"?)
// JPEG: off 0, len: 2, magic: 0xffd8 (SOI marker)
// PNG: off: 0, len: 8, magic: 0x89, 'P', 'N', 'G', 0x0d0a1a0a
// PSD: off: 0, len: 4, magic: '8', 'B', 'P', 'S'
// TIFF: off: 0, len: 4, magic: ('I', 'I', 0x00, (0x42 | 0x43)) | ('M', 'M', (0x42 | 0x43), 0x00) (43 is bigTiff)
if (signature[0] == 'B' && signature[1] == 'M') {
// BMP
return true;
}
if (signature[0] == 'G' && signature[1] == 'I' && signature[2] == 'F' && signature[3] == '8'
&& (signature[4] == '7' || signature[4] == '9') && signature[5] == 'a') {
// GIF
return true;
}
if (signature[0] == (byte) 0xFF && signature[1] == (byte) 0xD8 && signature[2] == (byte) 0xFF) {
// JPEG
return true;
}
if (signature[0] == (byte) 0x89 && signature[1] == 'P' && signature[2] == 'N' && signature[3] == 'G'
&& signature[4] == 0x0D && signature[5] == 0x0A && signature[6] == 0x1A && signature[7] == 0x0A) {
// PNG
return true;
}
if (signature[0] == '8' && signature[1] == 'B' && signature[2] == 'P' && signature[3] == 'S') {
// PSD
return true;
}
if ((signature[0] == 'I' && signature[1] == 'I' && (signature[2] == 42 || signature[2] == 43) && signature[3] == 0x00)
|| signature[0] == 'M' && signature[1] == 'M' && signature[2] == 0x00 && (signature[3] == 42 || signature[3] == 43)) {
// TIFF/BigTIFF
return true;
}
}
catch (EOFException ignore) {
// Can't be any of the formats
}
finally {
stream.reset();
}
return false;
}
static void skipNullHeader(final ImageInputStream pStream) throws IOException { static void skipNullHeader(final ImageInputStream pStream) throws IOException {
// NOTE: Only skip if FILE FORMAT, not needed for Mac OS DnD // NOTE: Only skip if FILE FORMAT, not needed for Mac OS DnD
// Spec says "platform dependent", may not be all nulls.. // Spec says "platform dependent", may not be all nulls..
@@ -102,10 +162,6 @@ public final class PICTImageReaderSpi extends ImageReaderSpiBase {
// Sanity check bounding box // Sanity check bounding box
int y1 = pStream.readUnsignedShort(); int y1 = pStream.readUnsignedShort();
int x1 = pStream.readUnsignedShort(); int x1 = pStream.readUnsignedShort();
// TODO: Figure out if frame can ever start at negative bounds...
// if (x1 != 0 || y1 != 0) {
// return false;
// }
int y2 = pStream.readUnsignedShort(); int y2 = pStream.readUnsignedShort();
int x2 = pStream.readUnsignedShort(); int x2 = pStream.readUnsignedShort();
@@ -33,6 +33,7 @@ package com.twelvemonkeys.imageio.plugins.pict;
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream; import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStreamSpi; import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStreamSpi;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest; import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
@@ -46,8 +47,8 @@ import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import static org.junit.Assert.assertEquals; import static com.twelvemonkeys.imageio.plugins.pict.PICTImageReaderSpi.isOtherFormat;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.*;
/** /**
* ICOImageReaderTestCase * ICOImageReaderTestCase
@@ -62,7 +63,10 @@ public class PICTImageReaderTest extends ImageReaderAbstractTest<PICTImageReader
IIORegistry.getDefaultInstance().registerServiceProvider(new ByteArrayImageInputStreamSpi()); IIORegistry.getDefaultInstance().registerServiceProvider(new ByteArrayImageInputStreamSpi());
} }
static ImageReaderSpi sProvider = new PICTImageReaderSpi(); @Override
protected ImageReaderSpi createProvider() {
return new PICTImageReaderSpi();
}
// TODO: Should also test the clipboard format (without 512 byte header) // TODO: Should also test the clipboard format (without 512 byte header)
protected List<TestData> getTestData() { protected List<TestData> getTestData() {
@@ -91,31 +95,53 @@ public class PICTImageReaderTest extends ImageReaderAbstractTest<PICTImageReader
); );
} }
protected ImageReaderSpi createProvider() {
return sProvider;
}
@Override @Override
protected PICTImageReader createReader() {
return new PICTImageReader(sProvider);
}
protected Class<PICTImageReader> getReaderClass() {
return PICTImageReader.class;
}
protected List<String> getFormatNames() { protected List<String> getFormatNames() {
return Collections.singletonList("pict"); return Collections.singletonList("pict");
} }
@Override
protected List<String> getSuffixes() { protected List<String> getSuffixes() {
return Arrays.asList("pct", "pict"); return Arrays.asList("pct", "pict");
} }
@Override
protected List<String> getMIMETypes() { protected List<String> getMIMETypes() {
return Arrays.asList("image/pict", "image/x-pict"); return Arrays.asList("image/pict", "image/x-pict");
} }
@Test
public void testIsOtherFormat() throws IOException {
assertFalse(isOtherFormat(new ByteArrayImageInputStream(new byte[8])));
// BMP
assertTrue(isOtherFormat(new ByteArrayImageInputStream(new byte[] {'B', 'M', 0, 0, 0, 0, 0, 0})));
// GIF
assertTrue(isOtherFormat(new ByteArrayImageInputStream(new byte[] {'G', 'I', 'F', '8', '7', 'a', 0, 0})));
assertTrue(isOtherFormat(new ByteArrayImageInputStream(new byte[] {'G', 'I', 'F', '8', '9', 'a', 0, 0})));
// JPEG (JFIF, EXIF, "raw")
assertTrue(isOtherFormat(new ByteArrayImageInputStream(new byte[] {(byte) 0xFF, (byte) 0xD8, (byte) 0xFF, (byte) 0xE0, 0, 0, 0, 0})));
assertTrue(isOtherFormat(new ByteArrayImageInputStream(new byte[] {(byte) 0xFF, (byte) 0xD8, (byte) 0xFF, (byte) 0xE1, 0, 0, 0, 0})));
assertTrue(isOtherFormat(new ByteArrayImageInputStream(new byte[] {(byte) 0xFF, (byte) 0xD8, (byte) 0xFF, (byte) 0xDB, 0, 0, 0, 0})));
assertTrue(isOtherFormat(new ByteArrayImageInputStream(new byte[] {(byte) 0xFF, (byte) 0xD8, (byte) 0xFF, (byte) 0xC4, 0, 0, 0, 0})));
// PNG
assertTrue(isOtherFormat(new ByteArrayImageInputStream(new byte[] {(byte) 0x89, 'P', 'N', 'G', 0x0D, 0x0A, 0x1A, 0x0A})));
// PSD
assertTrue(isOtherFormat(new ByteArrayImageInputStream(new byte[] {'8', 'B', 'P', 'S', 0, 0, 0, 0})));
// TIFF (Little Endian, Big Endian)
assertTrue(isOtherFormat(new ByteArrayImageInputStream(new byte[] {'I', 'I', 42, 0, 0, 0, 0, 0})));
assertTrue(isOtherFormat(new ByteArrayImageInputStream(new byte[] {'M', 'M', 0, 42, 0, 0, 0, 0})));
// BigTIFF (Little Endian, Big Endian)
assertTrue(isOtherFormat(new ByteArrayImageInputStream(new byte[] {'I', 'I', 43, 0, 0, 0, 0, 0})));
assertTrue(isOtherFormat(new ByteArrayImageInputStream(new byte[] {'M', 'M', 0, 43, 0, 0, 0, 0})));
}
@Ignore("Known issue") @Ignore("Known issue")
@Test @Test
@Override @Override
@@ -130,7 +156,7 @@ public class PICTImageReaderTest extends ImageReaderAbstractTest<PICTImageReader
stream.mark(); stream.mark();
stream.seek(123); stream.seek(123);
sProvider.canDecodeInput(stream); ((ImageReaderSpi) new PICTImageReaderSpi()).canDecodeInput(stream);
assertEquals(123, stream.getStreamPosition()); assertEquals(123, stream.getStreamPosition());
stream.reset(); stream.reset();
@@ -144,35 +170,39 @@ public class PICTImageReaderTest extends ImageReaderAbstractTest<PICTImageReader
public void testProviderNotMatchJPEG() throws IOException { public void testProviderNotMatchJPEG() throws IOException {
// This JPEG contains PICT magic bytes at locations a PICT would normally have them. // This JPEG contains PICT magic bytes at locations a PICT would normally have them.
// We should not claim to be able read it. // We should not claim to be able read it.
assertFalse(sProvider.canDecodeInput( assertFalse(((ImageReaderSpi) new PICTImageReaderSpi()).canDecodeInput(
new TestData(getClassLoaderResource("/jpeg/R-7439-1151526181.jpeg"), new TestData(getClassLoaderResource("/jpeg/R-7439-1151526181.jpeg"),
new Dimension(386, 396) new Dimension(386, 396)
))); )));
assertFalse(((ImageReaderSpi) new PICTImageReaderSpi()).canDecodeInput(
new TestData(getClassLoaderResource("/jpeg/89497426-adc19a00-d7ff-11ea-8ad1-0cbcd727b62a.jpeg"),
new Dimension(640, 480)
)));
} }
@Test @Test
public void testDataExtV2() throws IOException, InterruptedException { public void testDataExtV2() throws IOException {
PICTImageReader reader = createReader(); PICTImageReader reader = createReader();
reader.setInput(new ByteArrayImageInputStream(DATA_EXT_V2)); reader.setInput(new ByteArrayImageInputStream(DATA_EXT_V2));
reader.read(0); reader.read(0);
} }
@Test @Test
public void testDataV2() throws IOException, InterruptedException { public void testDataV2() throws IOException {
PICTImageReader reader = createReader(); PICTImageReader reader = createReader();
reader.setInput(new ByteArrayImageInputStream(DATA_V2)); reader.setInput(new ByteArrayImageInputStream(DATA_V2));
reader.read(0); reader.read(0);
} }
@Test @Test
public void testDataV1() throws IOException, InterruptedException { public void testDataV1() throws IOException {
PICTImageReader reader = createReader(); PICTImageReader reader = createReader();
reader.setInput(new ByteArrayImageInputStream(DATA_V1)); reader.setInput(new ByteArrayImageInputStream(DATA_V1));
reader.read(0); reader.read(0);
} }
@Test @Test
public void testDataV1OvalRect() throws IOException, InterruptedException { public void testDataV1OvalRect() throws IOException {
PICTImageReader reader = createReader(); PICTImageReader reader = createReader();
reader.setInput(new ByteArrayImageInputStream(DATA_V1_OVAL_RECT)); reader.setInput(new ByteArrayImageInputStream(DATA_V1_OVAL_RECT));
reader.read(0); reader.read(0);
@@ -193,7 +223,7 @@ public class PICTImageReaderTest extends ImageReaderAbstractTest<PICTImageReader
} }
@Test @Test
public void testDataV1CopyBits() throws IOException, InterruptedException { public void testDataV1CopyBits() throws IOException {
PICTImageReader reader = createReader(); PICTImageReader reader = createReader();
reader.setInput(new ByteArrayImageInputStream(DATA_V1_COPY_BITS)); reader.setInput(new ByteArrayImageInputStream(DATA_V1_COPY_BITS));
reader.read(0); reader.read(0);
@@ -32,10 +32,12 @@ package com.twelvemonkeys.imageio.plugins.pict;
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream; import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
import com.twelvemonkeys.imageio.util.ImageWriterAbstractTest; import com.twelvemonkeys.imageio.util.ImageWriterAbstractTest;
import org.junit.Test; import org.junit.Test;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import javax.imageio.ImageWriter; import javax.imageio.ImageWriter;
import javax.imageio.spi.ImageWriterSpi;
import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream; import javax.imageio.stream.ImageOutputStream;
import java.awt.color.ColorSpace; import java.awt.color.ColorSpace;
@@ -55,12 +57,10 @@ import static org.junit.Assert.*;
* @author last modified by $Author: haraldk$ * @author last modified by $Author: haraldk$
* @version $Id: PICTImageWriterTest.java,v 1.0 20.01.12 12:26 haraldk Exp$ * @version $Id: PICTImageWriterTest.java,v 1.0 20.01.12 12:26 haraldk Exp$
*/ */
public class PICTImageWriterTest extends ImageWriterAbstractTest { public class PICTImageWriterTest extends ImageWriterAbstractTest<PICTImageWriter> {
private final PICTImageWriterSpi provider = new PICTImageWriterSpi();
@Override @Override
protected ImageWriter createImageWriter() { protected ImageWriterSpi createProvider() {
return new PICTImageWriter(provider); return new PICTImageWriterSpi();
} }
@Override @Override
@@ -79,7 +79,7 @@ public class PICTImageWriterTest extends ImageWriterAbstractTest {
@Test @Test
public void testWriteReadCompare() throws IOException { public void testWriteReadCompare() throws IOException {
ImageWriter writer = createImageWriter(); ImageWriter writer = createWriter();
List<? extends RenderedImage> testData = getTestData(); List<? extends RenderedImage> testData = getTestData();
for (int i = 0; i < testData.size(); i++) { for (int i = 0; i < testData.size(); i++) {
Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

+2 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId> <artifactId>imageio</artifactId>
<version>3.6</version> <version>3.6.5-SNAPSHOT</version>
</parent> </parent>
<artifactId>imageio-pnm</artifactId> <artifactId>imageio-pnm</artifactId>
<name>TwelveMonkeys :: ImageIO :: PNM plugin</name> <name>TwelveMonkeys :: ImageIO :: PNM plugin</name>
@@ -22,6 +22,7 @@
<groupId>com.twelvemonkeys.imageio</groupId> <groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-core</artifactId> <artifactId>imageio-core</artifactId>
<type>test-jar</type> <type>test-jar</type>
<scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>
@@ -30,6 +30,8 @@
package com.twelvemonkeys.imageio.plugins.pnm; package com.twelvemonkeys.imageio.plugins.pnm;
import com.twelvemonkeys.imageio.ImageWriterBase;
import org.w3c.dom.NodeList; import org.w3c.dom.NodeList;
import javax.imageio.IIOImage; import javax.imageio.IIOImage;
@@ -40,27 +42,30 @@ import javax.imageio.spi.ImageWriterSpi;
import javax.imageio.stream.ImageOutputStream; import javax.imageio.stream.ImageOutputStream;
import java.awt.image.DataBuffer; import java.awt.image.DataBuffer;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Locale; import java.util.Locale;
import static java.nio.charset.StandardCharsets.UTF_8;
abstract class HeaderWriter { abstract class HeaderWriter {
protected static final Charset UTF8 = Charset.forName("UTF8");
protected final ImageOutputStream imageOutput; protected final ImageOutputStream imageOutput;
protected HeaderWriter(final ImageOutputStream imageOutput) { protected HeaderWriter(final ImageOutputStream imageOutput) {
this.imageOutput = imageOutput; this.imageOutput = imageOutput;
} }
public static void write(final IIOImage image, final ImageWriterSpi provider, final ImageOutputStream imageOutput) throws IOException { public static void write(final IIOImage image, final ImageWriterBase writer, final ImageOutputStream imageOutput) throws IOException {
// TODO: This is somewhat sketchy... createHeaderWriter(writer.getFormatName(), imageOutput).writeHeader(image, writer.getOriginatingProvider());
if (provider.getFormatNames()[0].equals("pam")) { }
new PAMHeaderWriter(imageOutput).writeHeader(image, provider);
private static HeaderWriter createHeaderWriter(final String formatName, final ImageOutputStream imageOutput) {
if (formatName.equals("pam")) {
return new PAMHeaderWriter(imageOutput);
} }
else if (provider.getFormatNames()[0].equals("pnm")) { else if (formatName.equals("pnm")) {
new PNMHeaderWriter(imageOutput).writeHeader(image, provider); return new PNMHeaderWriter(imageOutput);
} }
else { else {
throw new AssertionError("Unsupported provider: " + provider); throw new AssertionError("Unsupported format: " + formatName);
} }
} }
@@ -101,7 +106,7 @@ abstract class HeaderWriter {
protected final void writeComments(final IIOMetadata metadata, final ImageWriterSpi provider) throws IOException { protected final void writeComments(final IIOMetadata metadata, final ImageWriterSpi provider) throws IOException {
// TODO: Only write creator if not already present // TODO: Only write creator if not already present
imageOutput.write(String.format("# CREATOR: %s %s\n", provider.getVendorName(), provider.getDescription(Locale.getDefault())).getBytes(UTF8)); imageOutput.write(String.format("# CREATOR: %s %s\n", provider.getVendorName(), provider.getDescription(Locale.getDefault())).getBytes(UTF_8));
// Comments from metadata // Comments from metadata
if (metadata != null && metadata.isStandardMetadataFormatSupported()) { if (metadata != null && metadata.isStandardMetadataFormatSupported()) {
@@ -111,7 +116,7 @@ abstract class HeaderWriter {
for (int i = 0; i < textEntries.getLength(); i++) { for (int i = 0; i < textEntries.getLength(); i++) {
// TODO: Write on the format "# KEYWORD: value" (if keyword != comment)? // TODO: Write on the format "# KEYWORD: value" (if keyword != comment)?
IIOMetadataNode textEntry = (IIOMetadataNode) textEntries.item(i); IIOMetadataNode textEntry = (IIOMetadataNode) textEntries.item(i);
imageOutput.write(String.format("# %s", textEntry.getAttribute("value")).getBytes(UTF8)); imageOutput.write(String.format("# %s", textEntry.getAttribute("value")).getBytes(UTF_8));
} }
} }
} }
@@ -66,7 +66,7 @@ final class PAMHeaderParser extends HeaderParser {
int depth = -1; int depth = -1;
int maxVal = -1; int maxVal = -1;
TupleType tupleType = null; TupleType tupleType = null;
List<String> comments = new ArrayList<String>(); List<String> comments = new ArrayList<>();
String line; String line;
while ((line = input.readLine()) != null && !line.startsWith(ENDHDR)) { while ((line = input.readLine()) != null && !line.startsWith(ENDHDR)) {
@@ -35,6 +35,8 @@ import javax.imageio.spi.ImageWriterSpi;
import javax.imageio.stream.ImageOutputStream; import javax.imageio.stream.ImageOutputStream;
import java.io.IOException; import java.io.IOException;
import static java.nio.charset.StandardCharsets.UTF_8;
final class PAMHeaderWriter extends HeaderWriter { final class PAMHeaderWriter extends HeaderWriter {
public PAMHeaderWriter(final ImageOutputStream imageOutput) { public PAMHeaderWriter(final ImageOutputStream imageOutput) {
super(imageOutput); super(imageOutput);
@@ -48,14 +50,14 @@ final class PAMHeaderWriter extends HeaderWriter {
// Comments // Comments
writeComments(image.getMetadata(), provider); writeComments(image.getMetadata(), provider);
// Write width/height and number of channels // Write width/height and number of channels
imageOutput.write(String.format("WIDTH %s\nHEIGHT %s\n", getWidth(image), getHeight(image)).getBytes(UTF8)); imageOutput.write(String.format("WIDTH %s\nHEIGHT %s\n", getWidth(image), getHeight(image)).getBytes(UTF_8));
imageOutput.write(String.format("DEPTH %s\n", getNumBands(image)).getBytes(UTF8)); imageOutput.write(String.format("DEPTH %s\n", getNumBands(image)).getBytes(UTF_8));
// TODO: maxSample (8 or16 bit) // TODO: maxSample (8 or16 bit)
imageOutput.write(String.format("MAXVAL %s\n", getMaxVal(image)).getBytes(UTF8)); imageOutput.write(String.format("MAXVAL %s\n", getMaxVal(image)).getBytes(UTF_8));
// TODO: Determine tuple type based on input color model and image data // TODO: Determine tuple type based on input color model and image data
TupleType tupleType = getNumBands(image) > 3 ? TupleType.RGB_ALPHA : TupleType.RGB; TupleType tupleType = getNumBands(image) > 3 ? TupleType.RGB_ALPHA : TupleType.RGB;
imageOutput.write(String.format("TUPLTYPE %s\nENDHDR\n", tupleType).getBytes(UTF8)); imageOutput.write(String.format("TUPLTYPE %s\nENDHDR\n", tupleType).getBytes(UTF_8));
} }
} }
@@ -0,0 +1,75 @@
/*
* 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.plugins.pnm;
import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
import java.util.Locale;
public final class PAMImageReaderSpi extends ImageReaderSpiBase {
/**
* Creates a {@code PAMImageReaderSpi}.
*/
public PAMImageReaderSpi() {
super(new PAMProviderInfo());
}
@Override
public boolean canDecodeInput(final Object source) throws IOException {
if (!(source instanceof ImageInputStream)) {
return false;
}
ImageInputStream stream = (ImageInputStream) source;
stream.mark();
try {
return stream.readShort() == PNM.PAM && stream.readInt() != PNM.XV_THUMBNAIL_MAGIC;
}
finally {
stream.reset();
}
}
@Override
public ImageReader createReaderInstance(final Object extension) {
return new PNMImageReader(this);
}
@Override
public String getDescription(final Locale locale) {
return "NetPBM Portable Arbitrary Map (PAM) image reader";
}
}
@@ -30,39 +30,19 @@
package com.twelvemonkeys.imageio.plugins.pnm; package com.twelvemonkeys.imageio.plugins.pnm;
import com.twelvemonkeys.imageio.spi.ProviderInfo; import com.twelvemonkeys.imageio.spi.ImageWriterSpiBase;
import javax.imageio.ImageTypeSpecifier; import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriter; import javax.imageio.ImageWriter;
import javax.imageio.spi.ImageWriterSpi;
import javax.imageio.stream.ImageOutputStream;
import java.util.Locale; import java.util.Locale;
public final class PAMImageWriterSpi extends ImageWriterSpi { public final class PAMImageWriterSpi extends ImageWriterSpiBase {
/** /**
* Creates a {@code PAMImageWriterSpi}. * Creates a {@code PAMImageWriterSpi}.
*/ */
public PAMImageWriterSpi() { public PAMImageWriterSpi() {
this(new PNMProviderInfo()); super(new PAMProviderInfo());
}
private PAMImageWriterSpi(final ProviderInfo pProviderInfo) {
super(
pProviderInfo.getVendorName(),
pProviderInfo.getVersion(),
new String[] {"pam", "PAM"},
new String[] {"pam"},
new String[] {
// No official IANA record exists, these are conventional
"image/x-portable-arbitrarymap" // PAM
},
"com.twelvemonkeys.imageio.plugins.pnm.PNMImageWriter",
new Class[] {ImageOutputStream.class},
new String[] {"com.twelvemonkeys.imageio.plugins.pnm.PNMImageReaderSpi"},
true, null, null, null, null,
true, null, null, null, null
);
} }
public boolean canEncodeImage(final ImageTypeSpecifier pType) { public boolean canEncodeImage(final ImageTypeSpecifier pType) {
@@ -0,0 +1,72 @@
/*
* 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.plugins.pnm;
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
/**
* PAMProviderInfo.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: harald.kuhr$
* @version $Id: PAMProviderInfo.java,v 1.0 20/03/15 harald.kuhr Exp$
*/
class PAMProviderInfo extends ReaderWriterProviderInfo {
public PAMProviderInfo() {
super(PAMProviderInfo.class,
new String[] {
"pam",
"PAM"
},
new String[] {"pam"},
new String[] {
// No official IANA record exists, these are conventional
"image/x-portable-arbitrarymap"
},
"com.twelvemonkeys.imageio.plugins.pnm.PNMImageReader",
new String[] {
"com.twelvemonkeys.imageio.plugins.pnm.PAMImageReaderSpi",
"com.twelvemonkeys.imageio.plugins.pnm.PNMImageReaderSpi"
},
"com.twelvemonkeys.imageio.plugins.pnm.PNMImageWriter",
new String[] {
"com.twelvemonkeys.imageio.plugins.pnm.PAMImageWriterSpi",
"com.twelvemonkeys.imageio.plugins.pnm.PNMImageWriterSpi"
},
true, // supports standard stream metadata
null, null, // native stream format name and class
null, null, // extra stream formats
true, // supports standard image metadata
null, null,
null, null // extra image metadata formats
);
}
}
@@ -71,7 +71,7 @@ final class PFMHeaderParser extends HeaderParser {
int height = 0; int height = 0;
float maxSample = tupleType == TupleType.BLACKANDWHITE_WHITE_IS_ZERO ? 1 : 0; // PBM has no maxSample line float maxSample = tupleType == TupleType.BLACKANDWHITE_WHITE_IS_ZERO ? 1 : 0; // PBM has no maxSample line
List<String> comments = new ArrayList<String>(); List<String> comments = new ArrayList<>();
while (width == 0 || height == 0 || maxSample == 0) { while (width == 0 || height == 0 || maxSample == 0) {
String line = input.readLine(); String line = input.readLine();
@@ -45,6 +45,7 @@ final class PNMHeader {
private final TupleType tupleType; private final TupleType tupleType;
private final int width; private final int width;
private final int height; private final int height;
private final float maxSampleFloat;
private final int maxSample; private final int maxSample;
private final List<String> comments; private final List<String> comments;
@@ -57,8 +58,9 @@ final class PNMHeader {
this.height = isTrue(height > 0, height, "height must be greater than: %d"); this.height = isTrue(height > 0, height, "height must be greater than: %d");
isTrue(depth == tupleType.getSamplesPerPixel(), depth, String.format("incorrect depth for %s, expected %d: %d", tupleType, tupleType.getSamplesPerPixel(), depth)); isTrue(depth == tupleType.getSamplesPerPixel(), depth, String.format("incorrect depth for %s, expected %d: %d", tupleType, tupleType.getSamplesPerPixel(), depth));
this.maxSample = isTrue(tupleType.isValidMaxSample(maxSample), maxSample, "maxSample out of range: %d"); this.maxSample = isTrue(tupleType.isValidMaxSample(maxSample), maxSample, "maxSample out of range: %d");
this.maxSampleFloat = this.maxSample;
this.comments = Collections.unmodifiableList(new ArrayList<String>(comments)); this.comments = Collections.unmodifiableList(new ArrayList<>(comments));
byteOrder = ByteOrder.BIG_ENDIAN; byteOrder = ByteOrder.BIG_ENDIAN;
} }
@@ -70,10 +72,11 @@ final class PNMHeader {
this.height = isTrue(height > 0, height, "height must be greater than: %d"); this.height = isTrue(height > 0, height, "height must be greater than: %d");
isTrue(depth == tupleType.getSamplesPerPixel(), depth, String.format("incorrect depth for %s, expected %d: %d", tupleType, tupleType.getSamplesPerPixel(), depth)); isTrue(depth == tupleType.getSamplesPerPixel(), depth, String.format("incorrect depth for %s, expected %d: %d", tupleType, tupleType.getSamplesPerPixel(), depth));
this.maxSample = -1; this.maxSample = 1;
this.maxSampleFloat = maxSample;
this.byteOrder = byteOrder; this.byteOrder = byteOrder;
this.comments = Collections.unmodifiableList(new ArrayList<String>(comments)); this.comments = Collections.unmodifiableList(new ArrayList<>(comments));
} }
private boolean isValidFileType(final short fileType) { private boolean isValidFileType(final short fileType) {
@@ -118,11 +121,8 @@ final class PNMHeader {
if (maxSample <= PNM.MAX_VAL_16BIT) { if (maxSample <= PNM.MAX_VAL_16BIT) {
return 16; return 16;
} }
if ((maxSample & 0xffffffffL) <= PNM.MAX_VAL_32BIT) {
return 32;
}
throw new AssertionError("maxSample exceeds 32 bit"); return 32;
} }
public int getTransferType() { public int getTransferType() {
@@ -135,11 +135,8 @@ final class PNMHeader {
if (maxSample <= PNM.MAX_VAL_16BIT) { if (maxSample <= PNM.MAX_VAL_16BIT) {
return DataBuffer.TYPE_USHORT; return DataBuffer.TYPE_USHORT;
} }
if ((maxSample & 0xffffffffL) <= PNM.MAX_VAL_32BIT) {
return DataBuffer.TYPE_INT;
}
throw new AssertionError("maxSample exceeds 32 bit"); return DataBuffer.TYPE_INT;
} }
public List<String> getComments() { public List<String> getComments() {
@@ -68,7 +68,7 @@ final class PNMHeaderParser extends HeaderParser {
int height = 0; int height = 0;
int maxSample = tupleType == TupleType.BLACKANDWHITE_WHITE_IS_ZERO ? 1 : 0; // PBM has no maxSample line int maxSample = tupleType == TupleType.BLACKANDWHITE_WHITE_IS_ZERO ? 1 : 0; // PBM has no maxSample line
List<String> comments = new ArrayList<String>(); List<String> comments = new ArrayList<>();
while (width == 0 || height == 0 || maxSample == 0) { while (width == 0 || height == 0 || maxSample == 0) {
String line = input.readLine(); String line = input.readLine();
@@ -35,6 +35,8 @@ import javax.imageio.spi.ImageWriterSpi;
import javax.imageio.stream.ImageOutputStream; import javax.imageio.stream.ImageOutputStream;
import java.io.IOException; import java.io.IOException;
import static java.nio.charset.StandardCharsets.UTF_8;
final class PNMHeaderWriter extends HeaderWriter { final class PNMHeaderWriter extends HeaderWriter {
public PNMHeaderWriter(final ImageOutputStream imageOutput) { public PNMHeaderWriter(final ImageOutputStream imageOutput) {
super(imageOutput); super(imageOutput);
@@ -52,11 +54,11 @@ final class PNMHeaderWriter extends HeaderWriter {
writeComments(image.getMetadata(), provider); writeComments(image.getMetadata(), provider);
// Dimensions (width/height) // Dimensions (width/height)
imageOutput.write(String.format("%s %s\n", getWidth(image), getHeight(image)).getBytes(HeaderWriter.UTF8)); imageOutput.write(String.format("%s %s\n", getWidth(image), getHeight(image)).getBytes(UTF_8));
// MaxSample // MaxSample
if (type != PNM.PBM) { if (type != PNM.PBM) {
imageOutput.write(String.format("%s\n", getMaxVal(image)).getBytes(HeaderWriter.UTF8)); imageOutput.write(String.format("%s\n", getMaxVal(image)).getBytes(UTF_8));
} }
} }
} }

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