mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2026-05-19 00:00:03 -04:00
Compare commits
186 Commits
3.6-bugfix
...
camera-raw
| Author | SHA1 | Date | |
|---|---|---|---|
| 4b2ebec429 | |||
| 0498a1a095 | |||
| 4fd20f7fa0 | |||
| c9bd68623a | |||
| 4d2326c18d | |||
| 94eac2d6e5 | |||
| f63a33d541 | |||
| 00f8d87f36 | |||
| 4c2ab6da7b | |||
| b5088312e2 | |||
| f04f968f12 | |||
| 8896092e31 | |||
| 2f9768a1d4 | |||
| 06bcf22242 | |||
| 20c7f8e60e | |||
| 15a9ad0a9b | |||
| 7ae2d636dc | |||
| 12e756b23c | |||
| 4e2bf131d2 | |||
| d0c4a07556 | |||
| 21059c8d5a | |||
| fa7b530809 | |||
| 790cf3b32e | |||
| b1baaad23b | |||
| 7fa704ace5 | |||
| 8d07f4fe90 | |||
| 32bba6857b | |||
| ab7b08dfa9 | |||
| e0d6fa0d84 | |||
| 51bdd370da | |||
| ee2be3f88f | |||
| c5511833cc | |||
| 6ac8a5d8b4 | |||
| 3f7cb24407 | |||
| 8bf9f7a8f0 | |||
| 03ab9558a0 | |||
| 715bde8358 | |||
| 0151efb5f6 | |||
| bd796429c5 | |||
| b4ef5823f3 | |||
| 9adf0f4da3 | |||
| 01a4e55185 | |||
| 2e2ab11091 | |||
| 419ffc9373 | |||
| b67975eef7 | |||
| b32a861b2d | |||
| 6930168c93 | |||
| fac9f1a927 | |||
| 913a03608c | |||
| 46ce99e10f | |||
| 3e4460ac41 | |||
| 5b7fc25520 | |||
| 42196e8513 | |||
| bc07524e7a | |||
| 0011b9a480 | |||
| 7b09ec8919 | |||
| 9c8977062d | |||
| b01b820ec8 | |||
| b61f2c179c | |||
| 967f8e6984 | |||
| bb650e5280 | |||
| 3b34d6e7ce | |||
| db782cfe9e | |||
| 96223f9f9f | |||
| da45c5783d | |||
| 5b6c819ac4 | |||
| 6d41f2db86 | |||
| ba0bb7b903 | |||
| d03dc28764 | |||
| 20a785ea5e | |||
| 0286fa4268 | |||
| 85fb9e6af3 | |||
| 97a8806bfb | |||
| 970f4f3a7e | |||
| 6d192968d1 | |||
| f5959af2e1 | |||
| ea74ac2714 | |||
| 80c595cea8 | |||
| fbc738f2d4 | |||
| 3e3acf3332 | |||
| 0a77520d67 | |||
| 72cd3aade3 | |||
| 88bd9cd2ba | |||
| 5ee8678a29 | |||
| fb1937ae63 | |||
| de02e3d7e0 | |||
| ebaa69713f | |||
| 8a1a90dafd | |||
| 6f6e65be12 | |||
| 253f04066b | |||
| 74902b3fb4 | |||
| af1a6492d4 | |||
| 0da007ec8c | |||
| 9053fb3816 | |||
| c1d4e474f0 | |||
| 6bac13eb84 | |||
| 0e48ddd306 | |||
| 8682decbbc | |||
| bb615b90bf | |||
| cb0c320b45 | |||
| 73044bea58 | |||
| 3bb312e9e1 | |||
| c7d2f422b8 | |||
| 4dedf76ebc | |||
| 2376d16ffd | |||
| 1fe0bdd41f | |||
| 1b4d25342f | |||
| bc391550fb | |||
| b563f573de | |||
| 25150b421c | |||
| 94031a2913 | |||
| 64fb421b38 | |||
| 78af95d747 | |||
| 1d4f681b8f | |||
| eda2cd76db | |||
| 4adc60a6c6 | |||
| 0d5577a9a4 | |||
| 918f92aba7 | |||
| 7a24d55be7 | |||
| a84cc1c060 | |||
| 31cb79d2b9 | |||
| d995e7baa0 | |||
| e7fe6d5c22 | |||
| 918b698e50 | |||
| 2427b2323f | |||
| 0a8222fea3 | |||
| 60a00b89ae | |||
| 4c88efa19d | |||
| 17d65a1f6f | |||
| fcd03eb903 | |||
| 4e69efce28 | |||
| 16caec4a22 | |||
| 08282ea09d | |||
| c04fed1aff | |||
| 97e788883a | |||
| a16fce0749 | |||
| 26e2fa0168 | |||
| 120deb3ad4 | |||
| 0a9e2df5de | |||
| 6ffcb88872 | |||
| 960e764c7b | |||
| d88f27b251 | |||
| e5b3e9755e | |||
| 6c34fb211f | |||
| 9fdbc3b1fc | |||
| 622c6f40d4 | |||
| 107da17ca9 | |||
| f9871b73a3 | |||
| 7605b646fe | |||
| 19c62ac7da | |||
| a5e4412d1a | |||
| 651246566a | |||
| fe8f854b17 | |||
| a4d20a4af4 | |||
| 0643d5910a | |||
| c78a456985 | |||
| 27017576d3 | |||
| f1810be10a | |||
| 021aba1a98 | |||
| a0b68adff3 | |||
| fa4586663c | |||
| 623d13a517 | |||
| a7ebc1b52f | |||
| f54f4370c0 | |||
| 5040e9fe8a | |||
| fc72cd34a1 | |||
| 6d71a3d306 | |||
| 86f8cf52a5 | |||
| bda6544a5f | |||
| 49c7cd1979 | |||
| 9dae58d5a6 | |||
| ed14b97199 | |||
| b94135a91c | |||
| 7ff74465d3 | |||
| 1f5bf80f7e | |||
| 5c35c6e997 | |||
| 7692eeef5e | |||
| cdadbf7051 | |||
| 7c2423bc94 | |||
| f9837d4540 | |||
| 7728a912b1 | |||
| 923598636e | |||
| 6ab0e64107 | |||
| f69f706f60 | |||
| 53dd493cfa | |||
| 607502fbbd |
@@ -0,0 +1 @@
|
||||
github: haraldk
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: Reported bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**Version information**
|
||||
1. The version of the TwelveMonkeys ImageIO library in use.
|
||||
For example: 4.0.0
|
||||
|
||||
2. The *exact* output of `java --version` (or `java -version` for older Java releases).
|
||||
For example:
|
||||
|
||||
java version "1.8.0_271"
|
||||
Java(TM) SE Runtime Environment (build 1.8.0_271-b09)
|
||||
Java HotSpot(TM) 64-Bit Server VM (build 25.271-b09, mixed mode)
|
||||
|
||||
3. Extra information about OS version, server version, standalone program or web application packaging, executable wrapper, etc. Please state exact version numbers where applicable.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
|
||||
1. Compile the below sample code
|
||||
2. Download the sample image file
|
||||
3. Run the code with the sample file
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Example code**
|
||||
Preferably as a failing JUnit test, or a standalone program with a `main` method that showcases the problem.
|
||||
|
||||
Less is more. Don't add your entire project, only the code required to reproduce the problem. 😀
|
||||
|
||||
**Sample file(s)**
|
||||
Attach any sample files needed to reproduce the problem. Use a ZIP-file if the format is not directly supported by GitHub.
|
||||
|
||||
**Stak trace**
|
||||
Always include the stack trace you experience.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
Do not add screenshots of code or stack traces. 😀
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: New feature
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem or use case is.
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here, like links to specifications or sample files.
|
||||
@@ -0,0 +1,13 @@
|
||||
---
|
||||
name: Trouble shooting and programming help
|
||||
about: "General programming issues will reach a wider audience at StackOverflow. Tag
|
||||
questions with javax-imageio and/or twelvemonkeys \U0001F600 "
|
||||
title: ''
|
||||
labels: Trouble-shooting
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
General programming issues and problems will reach a much wider audience at StackOverflow, we suggest you ask them there. This will offload our work with maintaining the library, and make sure you get better help sooner.
|
||||
|
||||
Tag the question with `javax-imageio` and/or `twelvemonkeys` and we'll find them there.
|
||||
@@ -0,0 +1,17 @@
|
||||
**What is fixed**
|
||||
|
||||
Add link to the issue this PR fixes.
|
||||
|
||||
Fixes #42.
|
||||
|
||||
**Why is this change proposed**
|
||||
|
||||
If this change does *not* fix an open issue, briefly describe the rationale for this PR.
|
||||
|
||||
**What is changed**
|
||||
|
||||
Briefly describe the changes proposed in this pull request:
|
||||
|
||||
* Fixed rare exception happening in `x >= 42` case
|
||||
* Small optimization of `decompress()` method
|
||||
* Corrected API doc for `compress()` method to reflect current implementation
|
||||
+8
-8
@@ -1,14 +1,14 @@
|
||||
dist: trusty
|
||||
language: java
|
||||
jdk:
|
||||
- oraclejdk8
|
||||
# Oracle JDK 7 no longer supported, we use env matrix to test various CMM providers
|
||||
# - oraclejdk7
|
||||
# Some JPEGImageReader tests fail on OpenJDK, need to investigate/fix before enabling
|
||||
# - openjdk7
|
||||
env:
|
||||
- MAVEN_OPTS=-Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider
|
||||
- MAVEN_OPTS=""
|
||||
- oraclejdk8 # Legacy
|
||||
- oraclejdk11 # LTS
|
||||
- oraclejdk15 # Latest
|
||||
jobs:
|
||||
include:
|
||||
# Extra job, testing legacy CMM option
|
||||
- jdk: oraclejdk8
|
||||
env: MAVEN_OPTS=-Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.m2
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
## Latest
|
||||
|
||||
Master branch build status: [](https://travis-ci.org/haraldk/TwelveMonkeys)
|
||||
|
||||
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).
|
||||
[](https://travis-ci.org/haraldk/TwelveMonkeys)
|
||||
[](https://maven-badges.herokuapp.com/maven-central/com.twelvemonkeys.imageio/imageio)
|
||||
[](https://stackoverflow.com/questions/tagged/twelvemonkeys)
|
||||
[](https://paypal.me/haraldk76/100)
|
||||
|
||||
## About
|
||||
|
||||
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.
|
||||
|
||||
Support for formats is important, both to be able to read data found
|
||||
@@ -19,246 +17,64 @@ 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](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/bmp_metadata.html) & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| | CUR | MS Windows Cursor Format | ✔ | - | - |
|
||||
| | ICO | MS Windows Icon Format | ✔ | ✔ | - |
|
||||
| [HDR](https://github.com/haraldk/TwelveMonkeys/wiki/HDR-Plugin) | HDR | Radiance High Dynamic Range RGBE Format | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| [ICNS](https://github.com/haraldk/TwelveMonkeys/wiki/ICNS-Plugin) | ICNS | Apple Icon Image | ✔ | ✔ | - |
|
||||
| [IFF](https://github.com/haraldk/TwelveMonkeys/wiki/IFF-Plugin) | IFF | Commodore Amiga/Electronic Arts Interchange File Format | ✔ | ✔ | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| [JPEG](https://github.com/haraldk/TwelveMonkeys/wiki/JPEG-Plugin) | **JPEG** | Joint Photographers Expert Group | ✔ | ✔ | [Native](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/jpeg_metadata.html#image) & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| | JPEG Lossless | | ✔ | - | [Native](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/jpeg_metadata.html#image) & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| [PCX](https://github.com/haraldk/TwelveMonkeys/wiki/PCX-Plugin) | PCX | ZSoft Paintbrush Format | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| | DCX | Multi-page PCX fax document | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| [PICT](https://github.com/haraldk/TwelveMonkeys/wiki/PICT-Plugin) | PICT | Apple QuickTime Picture Format | ✔ | ✔ | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| | PNTG | Apple MacPaint Picture Format | ✔ | | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| [PNM](https://github.com/haraldk/TwelveMonkeys/wiki/PNM-Plugin) | PAM | NetPBM Portable Any Map | ✔ | ✔ | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| | PBM | NetPBM Portable Bit Map | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| | PGM | NetPBM Portable Grey Map | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| | PPM | NetPBM Portable Pix Map | ✔ | ✔ | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| | PFM | Portable Float Map | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| [PSD](https://github.com/haraldk/TwelveMonkeys/wiki/PSD-Plugin) | **PSD** | Adobe Photoshop Document | ✔ | - | Native & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| | PSB | Adobe Photoshop Large Document | ✔ | - | Native & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| [SGI](https://github.com/haraldk/TwelveMonkeys/wiki/SGI-Plugin) | SGI | Silicon Graphics Image Format | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| [TGA](https://github.com/haraldk/TwelveMonkeys/wiki/TGA-Plugin) | TGA | Truevision TGA Image Format | ✔ | ✔ | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
|ThumbsDB| Thumbs.db| MS Windows Thumbs DB | ✔ | - | - | OLE2 Compound Document based format only
|
||||
| [TIFF](https://github.com/haraldk/TwelveMonkeys/wiki/TIFF-Plugin) | **TIFF** | Aldus/Adobe Tagged Image File Format | ✔ | ✔ | [Native](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/tiff_metadata.html#ImageMetadata) & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| | BigTIFF | | ✔ | - | [Native](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/tiff_metadata.html#ImageMetadata) & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| [WebP](https://github.com/haraldk/TwelveMonkeys/wiki/WebP-Plugin) | **WebP** | Google WebP Format | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| XWD | XWD | X11 Window Dump Format | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
|
||||
#### 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
|
||||
|
||||
* 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+.*
|
||||
**Important note on using Batik:** *Please read [The Apache™ XML Graphics Project - Security](https://xmlgraphics.apache.org/security.html),
|
||||
and make sure you use version 1.14 or later.*
|
||||
|
||||
Note that GIF, PNG and WBMP formats are already supported through the ImageIO API, using the
|
||||
[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
|
||||
|
||||
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.
|
||||
|
||||
The basic and simplest form of writing is:
|
||||
|
||||
if (!ImageIO.write(image, format, file)) {
|
||||
// Handle image not written case
|
||||
}
|
||||
```java
|
||||
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.
|
||||
|
||||
@@ -269,50 +85,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:
|
||||
|
||||
```java
|
||||
// Create input stream
|
||||
ImageInputStream input = ImageIO.createImageInputStream(file);
|
||||
// Create input stream (in try-with-resource block to avoid leaks)
|
||||
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 {
|
||||
// Get the reader
|
||||
Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
|
||||
reader.setInput(input);
|
||||
|
||||
if (!readers.hasNext()) {
|
||||
throw new IllegalArgumentException("No reader for: " + file);
|
||||
}
|
||||
// Optionally, listen for read warnings, progress, etc.
|
||||
reader.addIIOReadWarningListener(...);
|
||||
reader.addIIOReadProgressListener(...);
|
||||
|
||||
ImageReader reader = readers.next();
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
|
||||
try {
|
||||
reader.setInput(input);
|
||||
// Optionally, control read settings like sub sampling, source region or destination etc.
|
||||
param.setSourceSubsampling(...);
|
||||
param.setSourceRegion(...);
|
||||
param.setDestination(...);
|
||||
// ...
|
||||
|
||||
// Optionally, listen for read warnings, progress, etc.
|
||||
reader.addIIOReadWarningListener(...);
|
||||
reader.addIIOReadProgressListener(...);
|
||||
// Finally read the image, using settings from param
|
||||
BufferedImage image = reader.read(0, param);
|
||||
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
|
||||
// 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();
|
||||
}
|
||||
// Optionally, read thumbnails, meta data, etc...
|
||||
int numThumbs = reader.getNumThumbnails(0);
|
||||
// ...
|
||||
}
|
||||
finally {
|
||||
// Close stream in finally block to avoid resource leaks
|
||||
input.close();
|
||||
// Dispose reader in finally block to avoid memory leaks
|
||||
reader.dispose();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Query the reader for source image dimensions using `reader.getWidth(n)` and `reader.getHeight(n)` without reading the
|
||||
@@ -324,86 +134,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:
|
||||
|
||||
```java
|
||||
// Get the writer
|
||||
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(format);
|
||||
// Get the writer
|
||||
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(format);
|
||||
|
||||
if (!writers.hasNext()) {
|
||||
throw new IllegalArgumentException("No writer for: " + format);
|
||||
}
|
||||
|
||||
ImageWriter writer = writers.next();
|
||||
|
||||
try {
|
||||
// Create output stream
|
||||
ImageOutputStream output = ImageIO.createImageOutputStream(file);
|
||||
|
||||
try {
|
||||
writer.setOutput(output);
|
||||
|
||||
// Optionally, listen to progress, warnings, etc.
|
||||
|
||||
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, 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();
|
||||
if (!writers.hasNext()) {
|
||||
throw new IllegalArgumentException("No writer for: " + format);
|
||||
}
|
||||
|
||||
ImageWriter writer = writers.next();
|
||||
|
||||
try {
|
||||
// Create output stream (in try-with-resource block to avoid leaks)
|
||||
try (ImageOutputStream output = ImageIO.createImageOutputStream(file)) {
|
||||
writer.setOutput(output);
|
||||
|
||||
// Optionally, listen to progress, warnings, etc.
|
||||
|
||||
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, provide thumbnails and image/stream metadata
|
||||
writer.write(..., new IIOImage(..., image, ...), param);
|
||||
}
|
||||
}
|
||||
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
|
||||
[Java Image I/O API Guide](http://docs.oracle.com/javase/7/docs/technotes/guides/imageio/spec/imageio_guideTOC.fm.html)
|
||||
[Java Image I/O API Guide](https://docs.oracle.com/javase/7/docs/technotes/guides/imageio/spec/imageio_guideTOC.fm.html)
|
||||
from Oracle.
|
||||
|
||||
#### Adobe Clipping Path support
|
||||
|
||||
#### 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).
|
||||
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.
|
||||
try (ImageInputStream stream = ImageIO.createImageInputStream(new File("image_with_path.jpg")) {
|
||||
BufferedImage image = Paths.readClipped(stream);
|
||||
|
||||
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>
|
||||
// Do something with the clipped image...
|
||||
}
|
||||
```
|
||||
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
|
||||
|
||||
@@ -411,15 +191,15 @@ The library comes with a resampling (image resizing) operation, that contains ma
|
||||
to provide excellent results at reasonable speed.
|
||||
|
||||
```java
|
||||
import com.twelvemonkeys.image.ResampleOp;
|
||||
import com.twelvemonkeys.image.ResampleOp;
|
||||
|
||||
...
|
||||
...
|
||||
|
||||
BufferedImage input = ...; // Image to resample
|
||||
int width, height = ...; // new width/height
|
||||
BufferedImage input = ...; // Image to resample
|
||||
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
|
||||
BufferedImage output = resampler.filter(input, null);
|
||||
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);
|
||||
```
|
||||
|
||||
#### Using the DiffusionDither
|
||||
@@ -428,30 +208,30 @@ The library comes with a dithering operation, that can be used to convert `Buffe
|
||||
Floyd-Steinberg error-diffusion dither.
|
||||
|
||||
```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();
|
||||
BufferedImage output = ditherer.filter(input, null);
|
||||
BufferedImageOp ditherer = new DiffusionDither();
|
||||
BufferedImage output = ditherer.filter(input, null);
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
Download the project (using [Git](http://git-scm.com/downloads)):
|
||||
Download the project (using [Git](https://git-scm.com/downloads)):
|
||||
|
||||
$ git clone git@github.com:haraldk/TwelveMonkeys.git
|
||||
|
||||
This should create a folder named `TwelveMonkeys` in your current directory. Change directory to the `TwelveMonkeys`
|
||||
folder, and issue the command below to build.
|
||||
|
||||
Build the project (using [Maven](http://maven.apache.org/download.cgi)):
|
||||
Build the project (using [Maven](https://maven.apache.org/download.cgi)):
|
||||
|
||||
$ mvn package
|
||||
|
||||
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.
|
||||
|
||||
@@ -473,10 +253,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:
|
||||
|
||||
```java
|
||||
Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName("JPEG");
|
||||
while (readers.hasNext()) {
|
||||
System.out.println("reader: " + readers.next());
|
||||
}
|
||||
Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName("JPEG");
|
||||
while (readers.hasNext()) {
|
||||
System.out.println("reader: " + readers.next());
|
||||
}
|
||||
```
|
||||
|
||||
The first line should print:
|
||||
@@ -488,119 +268,183 @@ The first line should print:
|
||||
To depend on the JPEG and TIFF plugin using Maven, add the following to your POM:
|
||||
|
||||
```xml
|
||||
...
|
||||
<dependencies>
|
||||
...
|
||||
<dependencies>
|
||||
...
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-jpeg</artifactId>
|
||||
<version>3.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-tiff</artifactId>
|
||||
<version>3.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-jpeg</artifactId>
|
||||
<version>3.7.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-tiff</artifactId>
|
||||
<version>3.7.0</version>
|
||||
</dependency>
|
||||
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.servlet</groupId>
|
||||
<artifactId>servlet</artifactId>
|
||||
<version>3.5</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.servlet</groupId>
|
||||
<artifactId>servlet</artifactId>
|
||||
<version>3.7.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
```
|
||||
|
||||
#### 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:
|
||||
|
||||
twelvemonkeys-common-lang-3.5.jar
|
||||
twelvemonkeys-common-io-3.5.jar
|
||||
twelvemonkeys-common-image-3.5.jar
|
||||
twelvemonkeys-imageio-core-3.5.jar
|
||||
twelvemonkeys-imageio-metadata-3.5.jar
|
||||
twelvemonkeys-imageio-jpeg-3.5.jar
|
||||
twelvemonkeys-imageio-tiff-3.5.jar
|
||||
twelvemonkeys-common-lang-3.7.0.jar
|
||||
twelvemonkeys-common-io-3.7.0.jar
|
||||
twelvemonkeys-common-image-3.7.0.jar
|
||||
twelvemonkeys-imageio-core-3.7.0.jar
|
||||
twelvemonkeys-imageio-metadata-3.7.0.jar
|
||||
twelvemonkeys-imageio-jpeg-3.7.0.jar
|
||||
twelvemonkeys-imageio-tiff-3.7.0.jar
|
||||
|
||||
#### Deploying the plugins in a web app
|
||||
|
||||
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
|
||||
|
||||
##### Latest version (3.5)
|
||||
##### Latest version (3.7.0)
|
||||
|
||||
Requires Java 7 or later.
|
||||
|
||||
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-io-3.5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-io/3.5/common-io-3.5.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-lang-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-lang/3.7.0/common-lang-3.7.0.jar)
|
||||
* [common-io-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-io/3.7.0/common-io-3.7.0.jar)
|
||||
* [common-image-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-image/3.7.0/common-image-3.7.0.jar)
|
||||
|
||||
ImageIO dependencies
|
||||
* [imageio-core-3.5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-core/3.5/imageio-core-3.5.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-core-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-core/3.7.0/imageio-core-3.7.0.jar)
|
||||
* [imageio-metadata-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-metadata/3.7.0/imageio-metadata-3.7.0.jar)
|
||||
|
||||
ImageIO plugins
|
||||
* [imageio-bmp-3.5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-bmp/3.5/imageio-bmp-3.5.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-tiff-3.5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tiff/3.5/imageio-tiff-3.5.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-psd-3.5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-psd/3.5/imageio-psd-3.5.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-iff-3.5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-iff/3.5/imageio-iff-3.5.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-pict-3.5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pict/3.5/imageio-pict-3.5.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-tga-3.5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tga/3.5/imageio-tga-3.5.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.5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-thumbsdb/3.5/imageio-thumbsdb-3.5.jar)
|
||||
* [imageio-bmp-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-bmp/3.7.0/imageio-bmp-3.7.0.jar)
|
||||
* [imageio-hdr-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-hdr/3.7.0/imageio-hdr-3.7.0.jar)
|
||||
* [imageio-icns-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-icns/3.7.0/imageio-icns-3.7.0.jar)
|
||||
* [imageio-iff-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-iff/3.7.0/imageio-iff-3.7.0.jar)
|
||||
* [imageio-jpeg-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jpeg/3.7.0/imageio-jpeg-3.7.0.jar)
|
||||
* [imageio-pcx-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pcx/3.7.0/imageio-pcx-3.7.0.jar)
|
||||
* [imageio-pict-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pict/3.7.0/imageio-pict-3.7.0.jar)
|
||||
* [imageio-pnm-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pnm/3.7.0/imageio-pnm-3.7.0.jar)
|
||||
* [imageio-psd-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-psd/3.7.0/imageio-psd-3.7.0.jar)
|
||||
* [imageio-sgi-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-sgi/3.7.0/imageio-sgi-3.7.0.jar)
|
||||
* [imageio-tga-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tga/3.7.0/imageio-tga-3.7.0.jar)
|
||||
* [imageio-thumbsdb-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-thumbsdb/3.7.0/imageio-thumbsdb-3.7.0.jar)
|
||||
* [imageio-tiff-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tiff/3.7.0/imageio-tiff-3.7.0.jar)
|
||||
* [imageio-webp-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-webp/3.7.0/imageio-webp-3.7.0.jar)
|
||||
* [imageio-xwd-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-xwd/3.7.0/imageio-xwd-3.7.0.jar)
|
||||
|
||||
ImageIO plugins requiring 3rd party libs
|
||||
* [imageio-batik-3.5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-batik/3.5/imageio-batik-3.5.jar)
|
||||
* [imageio-batik-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-batik/3.7.0/imageio-batik-3.7.0.jar)
|
||||
|
||||
Photoshop Path support for ImageIO
|
||||
* [imageio-clippath-3.5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-clippath/3.5/imageio-clippath-3.5.jar)
|
||||
* [imageio-clippath-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-clippath/3.7.0/imageio-clippath-3.7.0.jar)
|
||||
|
||||
Servlet support
|
||||
* [servlet-3.5.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/servlet/servlet/3.5/servlet-3.5.jar)
|
||||
* [servlet-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/servlet/servlet/3.7.0/servlet-3.7.0.jar)
|
||||
|
||||
##### Old version (3.0.x)
|
||||
|
||||
Use this version for projects that requires Java 6 or need the JMagick support. *Does not support Java 8 or later*.
|
||||
|
||||
Common dependencies
|
||||
* [common-lang-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-lang/3.0.2/common-lang-3.0.2.jar)
|
||||
* [common-io-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-io/3.0.2/common-io-3.0.2.jar)
|
||||
* [common-image-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-image/3.0.2/common-image-3.0.2.jar)
|
||||
* [common-lang-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-lang/3.0.2/common-lang-3.0.2.jar)
|
||||
* [common-io-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-io/3.0.2/common-io-3.0.2.jar)
|
||||
* [common-image-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-image/3.0.2/common-image-3.0.2.jar)
|
||||
|
||||
ImageIO dependencies
|
||||
* [imageio-core-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-core/3.0.2/imageio-core-3.0.2.jar)
|
||||
* [imageio-metadata-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-metadata/3.0.2/imageio-metadata-3.0.2.jar)
|
||||
* [imageio-core-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-core/3.0.2/imageio-core-3.0.2.jar)
|
||||
* [imageio-metadata-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-metadata/3.0.2/imageio-metadata-3.0.2.jar)
|
||||
|
||||
ImageIO plugins
|
||||
* [imageio-jpeg-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jpeg/3.0.2/imageio-jpeg-3.0.2.jar)
|
||||
* [imageio-tiff-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tiff/3.0.2/imageio-tiff-3.0.2.jar)
|
||||
* [imageio-psd-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-psd/3.0.2/imageio-psd-3.0.2.jar)
|
||||
* [imageio-pict-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pict/3.0.2/imageio-pict-3.0.2.jar)
|
||||
* [imageio-iff-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-iff/3.0.2/imageio-iff-3.0.2.jar)
|
||||
* [imageio-icns-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-icns/3.0.2/imageio-icns-3.0.2.jar)
|
||||
* [imageio-ico-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-ico/3.0.2/imageio-ico-3.0.2.jar)
|
||||
* [imageio-thumbsdb-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-thumbsdb/3.0.2/imageio-thumbsdb-3.0.2.jar)
|
||||
* [imageio-jpeg-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jpeg/3.0.2/imageio-jpeg-3.0.2.jar)
|
||||
* [imageio-tiff-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tiff/3.0.2/imageio-tiff-3.0.2.jar)
|
||||
* [imageio-psd-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-psd/3.0.2/imageio-psd-3.0.2.jar)
|
||||
* [imageio-pict-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pict/3.0.2/imageio-pict-3.0.2.jar)
|
||||
* [imageio-iff-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-iff/3.0.2/imageio-iff-3.0.2.jar)
|
||||
* [imageio-icns-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-icns/3.0.2/imageio-icns-3.0.2.jar)
|
||||
* [imageio-ico-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-ico/3.0.2/imageio-ico-3.0.2.jar)
|
||||
* [imageio-thumbsdb-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-thumbsdb/3.0.2/imageio-thumbsdb-3.0.2.jar)
|
||||
|
||||
ImageIO plugins requiring 3rd party libs
|
||||
* [imageio-batik-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-batik/3.0.2/imageio-batik-3.0.2.jar)
|
||||
* [imageio-jmagick-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jmagick/3.0.2/imageio-jmagick-3.0.2.jar)
|
||||
* [imageio-batik-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-batik/3.0.2/imageio-batik-3.0.2.jar)
|
||||
* [imageio-jmagick-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jmagick/3.0.2/imageio-jmagick-3.0.2.jar)
|
||||
|
||||
Servlet support
|
||||
* [servlet-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/servlet/servlet/3.0.2/servlet-3.0.2.jar)
|
||||
* [servlet-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/servlet/servlet/3.0.2/servlet-3.0.2.jar)
|
||||
|
||||
|
||||
## License
|
||||
|
||||
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](https://opensource.org/licenses/BSD-3-Clause):
|
||||
|
||||
Copyright (c) 2008-2018, Harald Kuhr
|
||||
Copyright (c) 2008-2020, Harald Kuhr
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -653,7 +497,7 @@ a: The TwelveMonkeys ImageIO project contains plug-ins for ImageIO. ImageIO uses
|
||||
|
||||
All you have have to do, is to make sure you have the TwelveMonkeys JARs in your classpath.
|
||||
|
||||
You can read more about the registry and the lookup mechanism in the [IIORegistry API doc](http://docs.oracle.com/javase/7/docs/api/javax/imageio/spi/IIORegistry.html).
|
||||
You can read more about the registry and the lookup mechanism in the [IIORegistry API doc](https://docs.oracle.com/javase/7/docs/api/javax/imageio/spi/IIORegistry.html).
|
||||
|
||||
The fine print: The TwelveMonkeys service providers for JPEG, BMP and TIFF, overrides the onRegistration method, and
|
||||
utilizes the pairwise partial ordering mechanism of the `IIOServiceRegistry` to make sure it is installed before
|
||||
@@ -661,10 +505,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
|
||||
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.
|
||||
|
||||
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:
|
||||
- It's not actively developed. No issues has been fixed for years.
|
||||
- To get full format support, you need native libs.
|
||||
|
||||
+21
-1
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys</groupId>
|
||||
<artifactId>twelvemonkeys</artifactId>
|
||||
<version>3.6</version>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>com.twelvemonkeys.bom</groupId>
|
||||
@@ -58,6 +58,16 @@
|
||||
<artifactId>imageio-bmp</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-cr2</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-dng</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-hdr</artifactId>
|
||||
@@ -78,6 +88,11 @@
|
||||
<artifactId>imageio-jpeg</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-nef</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-pcx</artifactId>
|
||||
@@ -123,6 +138,11 @@
|
||||
<artifactId>imageio-tiff</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-xwd</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- ImageIO 3rd party dependent plugins -->
|
||||
<dependency>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>3.6</version>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>common-image</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
@@ -13,6 +13,10 @@
|
||||
The TwelveMonkeys Common Image support
|
||||
</description>
|
||||
|
||||
<properties>
|
||||
<project.jpms.module.name>com.twelvemonkeys.common.image</project.jpms.module.name>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>3.6</version>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>common-io</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
@@ -13,6 +13,10 @@
|
||||
The TwelveMonkeys Common IO support
|
||||
</description>
|
||||
|
||||
<properties>
|
||||
<project.jpms.module.name>com.twelvemonkeys.common.io</project.jpms.module.name>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
|
||||
@@ -72,7 +72,7 @@ public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
|
||||
}
|
||||
|
||||
@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) ||
|
||||
((pOffset + pLength) > pBytes.length) || ((pOffset + pLength) < 0)) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
@@ -98,7 +98,7 @@ public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
|
||||
private void growIfNeeded(int pNewCount) {
|
||||
if (pNewCount > buf.length) {
|
||||
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);
|
||||
buf = newBuf;
|
||||
}
|
||||
@@ -113,7 +113,7 @@ public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
|
||||
// Non-synchronized version of toByteArray
|
||||
@Override
|
||||
public byte[] toByteArray() {
|
||||
byte newBuf[] = new byte[count];
|
||||
byte[] newBuf = new byte[count];
|
||||
System.arraycopy(buf, 0, newBuf, 0, count);
|
||||
|
||||
return newBuf;
|
||||
|
||||
@@ -346,7 +346,7 @@ public final class FileUtil {
|
||||
|
||||
/**
|
||||
* Gets the file (type) extension of the given file.
|
||||
* A file extension is the part of the filename, after the last occurence
|
||||
* A file extension is the part of the filename, after the last occurrence
|
||||
* of a period {@code '.'}.
|
||||
* If the filename contains no period, {@code null} is returned.
|
||||
*
|
||||
|
||||
@@ -32,13 +32,13 @@ package com.twelvemonkeys.io.enc;
|
||||
|
||||
import com.twelvemonkeys.io.FileUtil;
|
||||
import com.twelvemonkeys.lang.ObjectAbstractTest;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
@@ -73,7 +73,7 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] createData(final int pLength) throws Exception {
|
||||
private byte[] createData(final int pLength) {
|
||||
byte[] bytes = new byte[pLength];
|
||||
RANDOM.nextBytes(bytes);
|
||||
return bytes;
|
||||
@@ -82,9 +82,8 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
|
||||
private void runStreamTest(final int pLength) throws Exception {
|
||||
byte[] data = createData(pLength);
|
||||
ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
|
||||
OutputStream out = new EncoderStream(outBytes, createEncoder(), true);
|
||||
|
||||
try {
|
||||
try (OutputStream out = new EncoderStream(outBytes, createEncoder(), true)) {
|
||||
// Provoke failure for encoders that doesn't take array offset properly into account
|
||||
int off = (data.length + 1) / 2;
|
||||
out.write(data, 0, off);
|
||||
@@ -92,9 +91,6 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
|
||||
out.write(data, off, data.length - off);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
out.close();
|
||||
}
|
||||
|
||||
byte[] encoded = outBytes.toByteArray();
|
||||
|
||||
@@ -102,7 +98,7 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
|
||||
// System.err.println("encoded: " + Arrays.toString(encoded));
|
||||
|
||||
byte[] decoded = FileUtil.read(new DecoderStream(new ByteArrayInputStream(encoded), createCompatibleDecoder()));
|
||||
assertTrue(Arrays.equals(data, decoded));
|
||||
assertArrayEquals(data, decoded);
|
||||
|
||||
InputStream in = new DecoderStream(new ByteArrayInputStream(encoded), createCompatibleDecoder());
|
||||
outBytes = new ByteArrayOutputStream();
|
||||
@@ -116,7 +112,7 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
|
||||
}
|
||||
|
||||
decoded = outBytes.toByteArray();
|
||||
assertTrue(Arrays.equals(data, decoded));
|
||||
assertArrayEquals(data, decoded);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -129,10 +125,6 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
|
||||
e.printStackTrace();
|
||||
fail(e.getMessage() + ": " + i);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
fail(e.getMessage() + ": " + i);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 100; i < 2000; i += 250) {
|
||||
@@ -143,10 +135,6 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
|
||||
e.printStackTrace();
|
||||
fail(e.getMessage() + ": " + i);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
fail(e.getMessage() + ": " + i);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 2000; i < 80000; i += 1000) {
|
||||
@@ -157,14 +145,8 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
|
||||
e.printStackTrace();
|
||||
fail(e.getMessage() + ": " + i);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
fail(e.getMessage() + ": " + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Test that the transition from byte[] to ByteBuffer didn't introduce bugs when writing to a wrapped array with offset.
|
||||
|
||||
|
||||
// TODO: Test that the transition from byte[] to ByteBuffer didn't introduce bugs when writing to a wrapped array with offset.
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>3.6</version>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>common-lang</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
@@ -13,4 +13,8 @@
|
||||
The TwelveMonkeys Common Language support
|
||||
</description>
|
||||
|
||||
<properties>
|
||||
<project.jpms.module.name>com.twelvemonkeys.common.lang</project.jpms.module.name>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -330,7 +330,7 @@ abstract class AbstractDecoratedMap<K, V> extends AbstractMap<K, V> implements M
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple Map.Entry implementaton.
|
||||
* A simple Map.Entry implementation.
|
||||
*/
|
||||
static class BasicEntry<K, V> implements Entry<K, V>, Serializable {
|
||||
K mKey;
|
||||
|
||||
@@ -34,7 +34,7 @@ import java.io.Serializable;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* A {@code Map} implementation that removes (exipres) its elements after
|
||||
* A {@code Map} implementation that removes (expires) its elements after
|
||||
* a given period. The map is by default backed by a {@link java.util.HashMap},
|
||||
* or can be instantiated with any given {@code Map} as backing.
|
||||
* <p>
|
||||
@@ -67,7 +67,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
|
||||
protected long expiryTime = 60000L; // 1 minute
|
||||
|
||||
//////////////////////
|
||||
private volatile long nextExpiryTime;
|
||||
private volatile long nextExpiryTime = Long.MAX_VALUE;
|
||||
//////////////////////
|
||||
|
||||
/**
|
||||
@@ -178,7 +178,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
|
||||
* @return {@code true} if this map contains no key-value mappings.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return (size() <= 0);
|
||||
return size() <= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -208,7 +208,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
|
||||
* @see #containsKey(java.lang.Object)
|
||||
*/
|
||||
public V get(Object pKey) {
|
||||
TimedEntry<K, V> entry = (TimedEntry<K, V>) entries.get(pKey);
|
||||
TimedEntry entry = (TimedEntry) entries.get(pKey);
|
||||
|
||||
if (entry == null) {
|
||||
return null;
|
||||
@@ -236,7 +236,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
|
||||
* {@code null} values.
|
||||
*/
|
||||
public V put(K pKey, V pValue) {
|
||||
TimedEntry<K, V> entry = (TimedEntry<K, V>) entries.get(pKey);
|
||||
TimedEntry entry = (TimedEntry) entries.get(pKey);
|
||||
V oldValue;
|
||||
|
||||
if (entry == null) {
|
||||
@@ -272,7 +272,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
|
||||
* {@code null} values.
|
||||
*/
|
||||
public V remove(Object pKey) {
|
||||
TimedEntry<K, V> entry = (TimedEntry<K, V>) entries.remove(pKey);
|
||||
TimedEntry entry = (TimedEntry) entries.remove(pKey);
|
||||
return (entry != null) ? entry.getValue() : null;
|
||||
}
|
||||
|
||||
@@ -284,13 +284,12 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
|
||||
init();
|
||||
}
|
||||
|
||||
/*protected*/ TimedEntry<K, V> createEntry(K pKey, V pValue) {
|
||||
return new TimedEntry<K, V>(pKey, pValue);
|
||||
/*protected*/ TimedEntry createEntry(K pKey, V pValue) {
|
||||
return new TimedEntry(pKey, pValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes any expired mappings.
|
||||
*
|
||||
*/
|
||||
protected void removeExpiredEntries() {
|
||||
// Remove any expired elements
|
||||
@@ -312,7 +311,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
|
||||
long next = Long.MAX_VALUE;
|
||||
nextExpiryTime = next; // Avoid multiple runs...
|
||||
for (Iterator<Entry<K, V>> iterator = new EntryIterator(); iterator.hasNext();) {
|
||||
TimedEntry<K, V> entry = (TimedEntry<K, V>) iterator.next();
|
||||
TimedEntry entry = (TimedEntry) iterator.next();
|
||||
////
|
||||
long expires = entry.expires();
|
||||
if (expires < next) {
|
||||
@@ -376,7 +375,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
|
||||
|
||||
while (mNext == null && mIterator.hasNext()) {
|
||||
Entry<K, Entry<K, V>> entry = mIterator.next();
|
||||
TimedEntry<K, V> timed = (TimedEntry<K, V>) entry.getValue();
|
||||
TimedEntry timed = (TimedEntry) entry.getValue();
|
||||
|
||||
if (timed.isExpiredBy(mNow)) {
|
||||
// Remove from map, and continue
|
||||
@@ -425,19 +424,28 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
|
||||
/**
|
||||
* Keeps track of timed objects
|
||||
*/
|
||||
private class TimedEntry<K, V> extends BasicEntry<K, V> {
|
||||
private class TimedEntry extends BasicEntry<K, V> {
|
||||
private long mTimestamp;
|
||||
|
||||
TimedEntry(K pKey, V pValue) {
|
||||
super(pKey, pValue);
|
||||
mTimestamp = System.currentTimeMillis();
|
||||
updateTimestamp();
|
||||
}
|
||||
|
||||
public V setValue(V pValue) {
|
||||
mTimestamp = System.currentTimeMillis();
|
||||
updateTimestamp();
|
||||
return super.setValue(pValue);
|
||||
}
|
||||
|
||||
private void updateTimestamp() {
|
||||
mTimestamp = System.currentTimeMillis();
|
||||
|
||||
long expires = expires();
|
||||
if (expires < nextExpiryTime) {
|
||||
nextExpiryTime = expires;
|
||||
}
|
||||
}
|
||||
|
||||
final boolean isExpired() {
|
||||
return isExpiredBy(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
package com.twelvemonkeys.lang;
|
||||
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.awt.*;
|
||||
import java.sql.Timestamp;
|
||||
@@ -41,7 +41,7 @@ import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.Locale;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* StringUtilTestCase
|
||||
@@ -76,24 +76,24 @@ public class StringUtilTest {
|
||||
assertNull(StringUtil.valueOf(null));
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Test
|
||||
public void testToUpperCase() {
|
||||
String str = StringUtil.toUpperCase(TEST_STRING);
|
||||
assertNotNull(str);
|
||||
assertEquals(TEST_STRING.toUpperCase(), str);
|
||||
|
||||
str = StringUtil.toUpperCase(null);
|
||||
assertNull(str);
|
||||
assertNull(StringUtil.toUpperCase(null));
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Test
|
||||
public void testToLowerCase() {
|
||||
String str = StringUtil.toLowerCase(TEST_STRING);
|
||||
assertNotNull(str);
|
||||
assertEquals(TEST_STRING.toLowerCase(), str);
|
||||
|
||||
str = StringUtil.toLowerCase(null);
|
||||
assertNull(str);
|
||||
assertNull(StringUtil.toLowerCase(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -113,6 +113,7 @@ public class StringUtilTest {
|
||||
assertFalse(StringUtil.isEmpty(new String[]{WHITESPACE_STRING, TEST_STRING}));
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Test
|
||||
public void testContains() {
|
||||
assertTrue(StringUtil.contains(TEST_STRING, TEST_STRING));
|
||||
@@ -145,6 +146,7 @@ public class StringUtilTest {
|
||||
assertFalse(StringUtil.containsIgnoreCase(null, null));
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Test
|
||||
public void testContainsChar() {
|
||||
for (int i = 0; i < TEST_STRING.length(); i++) {
|
||||
@@ -466,7 +468,7 @@ public class StringUtilTest {
|
||||
assertEquals(TEST_STRING, StringUtil.ltrim(TEST_STRING));
|
||||
assertEquals(TEST_STRING, StringUtil.ltrim(" " + TEST_STRING));
|
||||
assertEquals(TEST_STRING, StringUtil.ltrim(WHITESPACE_STRING + TEST_STRING));
|
||||
assertFalse(TEST_STRING.equals(StringUtil.ltrim(TEST_STRING + WHITESPACE_STRING)));
|
||||
assertNotEquals(TEST_STRING, StringUtil.ltrim(TEST_STRING + WHITESPACE_STRING));
|
||||
// TODO: Test is not complete
|
||||
}
|
||||
|
||||
@@ -475,7 +477,7 @@ public class StringUtilTest {
|
||||
assertEquals(TEST_STRING, StringUtil.rtrim(TEST_STRING));
|
||||
assertEquals(TEST_STRING, StringUtil.rtrim(TEST_STRING + " "));
|
||||
assertEquals(TEST_STRING, StringUtil.rtrim(TEST_STRING + WHITESPACE_STRING));
|
||||
assertFalse(TEST_STRING.equals(StringUtil.rtrim(WHITESPACE_STRING + TEST_STRING)));
|
||||
assertNotEquals(TEST_STRING, StringUtil.rtrim(WHITESPACE_STRING + TEST_STRING));
|
||||
// TODO: Test is not complete
|
||||
}
|
||||
|
||||
@@ -516,7 +518,7 @@ public class StringUtilTest {
|
||||
public void testCaptialize() {
|
||||
assertNull(StringUtil.capitalize(null));
|
||||
assertEquals(TEST_STRING.toUpperCase(), StringUtil.capitalize(TEST_STRING.toUpperCase()));
|
||||
assertTrue(StringUtil.capitalize("abc").charAt(0) == 'A');
|
||||
assertEquals('A', StringUtil.capitalize("abc").charAt(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -552,13 +554,13 @@ public class StringUtilTest {
|
||||
public void testToDateWithFormatString() {
|
||||
Calendar cal = new GregorianCalendar();
|
||||
cal.clear();
|
||||
cal.set(1976, 2, 16); // Month is 0-based
|
||||
cal.set(1976, Calendar.MARCH, 16); // Month is 0-based
|
||||
Date date = StringUtil.toDate("16.03.1976", "dd.MM.yyyy");
|
||||
assertNotNull(date);
|
||||
assertEquals(cal.getTime(), date);
|
||||
|
||||
cal.clear();
|
||||
cal.set(2004, 4, 13, 23, 51, 3);
|
||||
cal.set(2004, Calendar.MAY, 13, 23, 51, 3);
|
||||
date = StringUtil.toDate("2004-5-13 23:51 (03)", "yyyy-MM-dd hh:mm (ss)");
|
||||
assertNotNull(date);
|
||||
assertEquals(cal.getTime(), date);
|
||||
@@ -576,15 +578,15 @@ public class StringUtilTest {
|
||||
public void testToDateWithFormat() {
|
||||
Calendar cal = new GregorianCalendar();
|
||||
cal.clear();
|
||||
cal.set(1976, 2, 16); // Month is 0-based
|
||||
cal.set(1976, Calendar.MARCH, 16); // Month is 0-based
|
||||
Date date = StringUtil.toDate("16.03.1976", new SimpleDateFormat("dd.MM.yyyy"));
|
||||
assertNotNull(date);
|
||||
assertEquals(cal.getTime(), date);
|
||||
|
||||
cal.clear();
|
||||
cal.set(2004, 4, 13, 23, 51);
|
||||
date = StringUtil.toDate("13.5.04 23:51",
|
||||
DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, new Locale("no", "NO")));
|
||||
cal.set(2004, Calendar.MAY, 13, 23, 51);
|
||||
DateFormat format = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, new Locale("no", "NO"));
|
||||
date = StringUtil.toDate(format.format(cal.getTime()), format);
|
||||
assertNotNull(date);
|
||||
assertEquals(cal.getTime(), date);
|
||||
|
||||
@@ -601,10 +603,9 @@ public class StringUtilTest {
|
||||
public void testToTimestamp() {
|
||||
Calendar cal = new GregorianCalendar();
|
||||
cal.clear();
|
||||
cal.set(1976, 2, 16, 21, 28, 4); // Month is 0-based
|
||||
Date date = StringUtil.toTimestamp("1976-03-16 21:28:04");
|
||||
cal.set(1976, Calendar.MARCH, 16, 21, 28, 4); // Month is 0-based
|
||||
Timestamp date = StringUtil.toTimestamp("1976-03-16 21:28:04");
|
||||
assertNotNull(date);
|
||||
assertTrue(date instanceof Timestamp);
|
||||
assertEquals(cal.getTime(), date);
|
||||
}
|
||||
|
||||
@@ -821,7 +822,7 @@ public class StringUtilTest {
|
||||
assertTrue(StringUtil.isNumber("12345"));
|
||||
assertTrue(StringUtil.isNumber(TEST_INTEGER.toString()));
|
||||
assertTrue(StringUtil.isNumber("1234567890123456789012345678901234567890"));
|
||||
assertTrue(StringUtil.isNumber(String.valueOf(Long.MAX_VALUE) + String.valueOf(Long.MAX_VALUE)));
|
||||
assertTrue(StringUtil.isNumber(String.valueOf(Long.MAX_VALUE) + Long.MAX_VALUE));
|
||||
assertFalse(StringUtil.isNumber("abc"));
|
||||
assertFalse(StringUtil.isNumber(TEST_STRING));
|
||||
}
|
||||
@@ -831,7 +832,7 @@ public class StringUtilTest {
|
||||
assertTrue(StringUtil.isNumber("-12345"));
|
||||
assertTrue(StringUtil.isNumber('-' + TEST_INTEGER.toString()));
|
||||
assertTrue(StringUtil.isNumber("-1234567890123456789012345678901234567890"));
|
||||
assertTrue(StringUtil.isNumber('-' + String.valueOf(Long.MAX_VALUE) + String.valueOf(Long.MAX_VALUE)));
|
||||
assertTrue(StringUtil.isNumber('-' + String.valueOf(Long.MAX_VALUE) + Long.MAX_VALUE));
|
||||
assertFalse(StringUtil.isNumber("-abc"));
|
||||
assertFalse(StringUtil.isNumber('-' + TEST_STRING));
|
||||
}
|
||||
|
||||
@@ -557,7 +557,7 @@ public class TimeoutMapTest extends MapAbstractTest {
|
||||
// NOTE: Only wait fist time, to avoid slooow tests
|
||||
synchronized (this) {
|
||||
try {
|
||||
wait(60l);
|
||||
wait(60L);
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
}
|
||||
@@ -591,7 +591,7 @@ public class TimeoutMapTest extends MapAbstractTest {
|
||||
try {
|
||||
wait(60l);
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
catch (InterruptedException ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -651,5 +651,24 @@ public class TimeoutMapTest extends MapAbstractTest {
|
||||
assertTrue("Wrong entry removed, keySet().iterator() is broken.", !map.containsKey(removedKey));
|
||||
assertTrue("Wrong entry removed, keySet().iterator() is broken.", map.containsKey(otherKey));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testContainsKeyOnEmptyMap() {
|
||||
// See #600
|
||||
Map<String, String> timeoutMap = new TimeoutMap<>(30);
|
||||
assertFalse(timeoutMap.containsKey("xyz"));
|
||||
timeoutMap.put("xyz", "xyz");
|
||||
assertTrue(timeoutMap.containsKey("xyz"));
|
||||
|
||||
try {
|
||||
Thread.sleep(50); // Let the item expire
|
||||
}
|
||||
catch (InterruptedException ignore) {
|
||||
}
|
||||
|
||||
assertFalse(timeoutMap.containsKey("xyz"));
|
||||
assertNull(timeoutMap.get("xyz"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys</groupId>
|
||||
<artifactId>twelvemonkeys</artifactId>
|
||||
<version>3.6</version>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
@@ -47,7 +47,7 @@
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.7</version>
|
||||
<version>4.13.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys</groupId>
|
||||
<artifactId>twelvemonkeys</artifactId>
|
||||
<version>3.6</version>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>com.twelvemonkeys.contrib</groupId>
|
||||
<artifactId>contrib</artifactId>
|
||||
@@ -65,7 +65,7 @@
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.7</version>
|
||||
<version>4.13.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.twelvemonkeys.contrib.exif;
|
||||
|
||||
import com.twelvemonkeys.image.ImageUtil;
|
||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import javax.imageio.IIOImage;
|
||||
@@ -35,7 +36,7 @@ public class EXIFUtilities {
|
||||
* @throws IOException if an error occurs during reading.
|
||||
*/
|
||||
public static IIOImage readWithOrientation(final URL input) throws IOException {
|
||||
try (ImageInputStream stream = ImageIO.createImageOutputStream(input)) {
|
||||
try (ImageInputStream stream = ImageIO.createImageInputStream(input)) {
|
||||
return readWithOrientation(stream);
|
||||
}
|
||||
}
|
||||
@@ -48,7 +49,7 @@ public class EXIFUtilities {
|
||||
* @throws IOException if an error occurs during reading.
|
||||
*/
|
||||
public static IIOImage readWithOrientation(final InputStream input) throws IOException {
|
||||
try (ImageInputStream stream = ImageIO.createImageOutputStream(input)) {
|
||||
try (ImageInputStream stream = ImageIO.createImageInputStream(input)) {
|
||||
return readWithOrientation(stream);
|
||||
}
|
||||
}
|
||||
@@ -61,7 +62,7 @@ public class EXIFUtilities {
|
||||
* @throws IOException if an error occurs during reading.
|
||||
*/
|
||||
public static IIOImage readWithOrientation(final File input) throws IOException {
|
||||
try (ImageInputStream stream = ImageIO.createImageOutputStream(input)) {
|
||||
try (ImageInputStream stream = ImageIO.createImageInputStream(input)) {
|
||||
return readWithOrientation(stream);
|
||||
}
|
||||
}
|
||||
@@ -82,12 +83,11 @@ public class EXIFUtilities {
|
||||
ImageReader reader = readers.next();
|
||||
try {
|
||||
reader.setInput(input, true, false);
|
||||
IIOImage image = reader.readAll(0, reader.getDefaultReadParam());
|
||||
|
||||
BufferedImage bufferedImage = ImageUtil.toBuffered(image.getRenderedImage());
|
||||
image.setRenderedImage(applyOrientation(bufferedImage, findImageOrientation(image.getMetadata()).value()));
|
||||
IIOMetadata metadata = reader.getImageMetadata(0);
|
||||
BufferedImage bufferedImage = applyOrientation(reader.read(0), findImageOrientation(metadata).value());
|
||||
|
||||
return image;
|
||||
return new IIOImage(bufferedImage, null, metadata);
|
||||
}
|
||||
finally {
|
||||
reader.dispose();
|
||||
@@ -123,9 +123,15 @@ public class EXIFUtilities {
|
||||
for (String arg : args) {
|
||||
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);
|
||||
|
||||
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
|
||||
Orientation orientation = findImageOrientation(image.getMetadata());
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.6</version>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-batik</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: Batik Plugin</name>
|
||||
@@ -14,6 +14,12 @@
|
||||
See the <a href="http://xmlgraphics.apache.org/batik/">Batik Home page</a>
|
||||
for more information.]]>
|
||||
</description>
|
||||
|
||||
<properties>
|
||||
<project.jpms.module.name>com.twelvemonkeys.imageio.batik</project.jpms.module.name>
|
||||
<batik.version>1.14</batik.version>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
@@ -39,6 +45,7 @@
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -101,8 +108,4 @@
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<batik.version>1.12</batik.version>
|
||||
</properties>
|
||||
</project>
|
||||
|
||||
+1
-1
@@ -655,7 +655,7 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
if (allowExternalResources) {
|
||||
return super.getExternalResourceSecurity(resourceURL, docURL);
|
||||
}
|
||||
return new NoLoadExternalResourceSecurity();
|
||||
return new EmbededExternalResourceSecurity(resourceURL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+27
-20
@@ -31,6 +31,7 @@
|
||||
package com.twelvemonkeys.imageio.plugins.svg;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -44,7 +45,6 @@ import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ImagingOpException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URISyntaxException;
|
||||
@@ -67,7 +67,10 @@ import static org.mockito.Mockito.*;
|
||||
*/
|
||||
public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader> {
|
||||
|
||||
private SVGImageReaderSpi provider = new SVGImageReaderSpi();
|
||||
@Override
|
||||
protected ImageReaderSpi createProvider() {
|
||||
return new SVGImageReaderSpi();
|
||||
}
|
||||
|
||||
protected List<TestData> getTestData() {
|
||||
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() {
|
||||
return Collections.singletonList("svg");
|
||||
}
|
||||
@@ -111,8 +101,6 @@ public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader>
|
||||
public void testScaleViewBox() throws IOException {
|
||||
URL svgUrl = getClassLoaderResource("/svg/quadrants.svg");
|
||||
|
||||
File tmpDir = new File(System.getProperty("java.io.tmpdir"));
|
||||
|
||||
SVGImageReader reader = createReader();
|
||||
SVGReadParam param = new SVGReadParam();
|
||||
|
||||
@@ -160,11 +148,11 @@ public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader>
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void testReadWithSizeParam() {
|
||||
public void testReadWithSizeParam() throws IOException {
|
||||
try {
|
||||
super.testReadWithSizeParam();
|
||||
}
|
||||
catch (AssertionError failure) {
|
||||
catch (AssertionError | IOException failure) {
|
||||
Throwable cause = failure;
|
||||
|
||||
while (cause.getCause() != null) {
|
||||
@@ -299,7 +287,7 @@ public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader>
|
||||
params.setAllowExternalResources(true);
|
||||
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) {
|
||||
assertTrue(allowed.getMessage().contains("batikLogo.svg")); // The embedded resource we don't find
|
||||
@@ -309,6 +297,25 @@ public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader>
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadEmbeddedWithDisallowExternalResources() throws IOException{
|
||||
// File using "data:" URLs for embedded resources
|
||||
URL resource = getClassLoaderResource("/svg/embedded-data-resource.svg");
|
||||
SVGImageReader reader = createReader();
|
||||
|
||||
TestData data = new TestData(resource, (Dimension) null);
|
||||
try (ImageInputStream stream = data.getInputStream()) {
|
||||
reader.setInput(stream);
|
||||
|
||||
SVGReadParam param = reader.getDefaultReadParam();
|
||||
param.setAllowExternalResources(false);
|
||||
reader.read(0, param);
|
||||
}
|
||||
finally {
|
||||
reader.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = SecurityException.class)
|
||||
public void testDisallowedExternalResources() throws URISyntaxException, IOException {
|
||||
// system-property set to true in surefire-plugin-settings in the pom
|
||||
|
||||
+7
-13
@@ -31,6 +31,7 @@
|
||||
package com.twelvemonkeys.imageio.plugins.wmf;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||
|
||||
import org.junit.Ignore;
|
||||
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$
|
||||
*/
|
||||
public class WMFImageReaderTest extends ImageReaderAbstractTest<WMFImageReader> {
|
||||
private WMFImageReaderSpi provider = new WMFImageReaderSpi();
|
||||
@Override
|
||||
protected ImageReaderSpi createProvider() {
|
||||
return new WMFImageReaderSpi();
|
||||
}
|
||||
|
||||
protected List<TestData> getTestData() {
|
||||
return Collections.singletonList(
|
||||
@@ -57,27 +61,17 @@ public class WMFImageReaderTest extends ImageReaderAbstractTest<WMFImageReader>
|
||||
);
|
||||
}
|
||||
|
||||
protected ImageReaderSpi createProvider() {
|
||||
return provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WMFImageReader createReader() {
|
||||
return new WMFImageReader(createProvider());
|
||||
}
|
||||
|
||||
protected Class<WMFImageReader> getReaderClass() {
|
||||
return WMFImageReader.class;
|
||||
}
|
||||
|
||||
protected List<String> getFormatNames() {
|
||||
return Collections.singletonList("wmf");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getSuffixes() {
|
||||
return Arrays.asList("wmf", "emf");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getMIMETypes() {
|
||||
return Arrays.asList("image/x-wmf", "application/x-msmetafile");
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 92 KiB |
@@ -4,12 +4,16 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.6</version>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-bmp</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: BMP plugin</name>
|
||||
<description>ImageIO plugin for Microsoft Device Independent Bitmap (BMP/DIB) format.</description>
|
||||
|
||||
<properties>
|
||||
<project.jpms.module.name>com.twelvemonkeys.imageio.bmp</project.jpms.module.name>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
@@ -19,6 +23,7 @@
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
+79
-63
@@ -30,6 +30,28 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.*;
|
||||
import java.io.DataInput;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.event.IIOReadUpdateListener;
|
||||
import javax.imageio.event.IIOReadWarningListener;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.metadata.IIOMetadataFormatImpl;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
|
||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||
import com.twelvemonkeys.imageio.stream.SubImageInputStream;
|
||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||
@@ -39,23 +61,6 @@ import com.twelvemonkeys.io.LittleEndianDataInputStream;
|
||||
import com.twelvemonkeys.io.enc.DecoderStream;
|
||||
import com.twelvemonkeys.xml.XMLSerializer;
|
||||
|
||||
import javax.imageio.*;
|
||||
import javax.imageio.event.IIOReadUpdateListener;
|
||||
import javax.imageio.event.IIOReadWarningListener;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.metadata.IIOMetadataFormatImpl;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.*;
|
||||
import java.io.DataInput;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* ImageReader for Microsoft Windows Bitmap (BMP) format.
|
||||
*
|
||||
@@ -125,6 +130,10 @@ public final class BMPImageReader extends ImageReaderBase {
|
||||
|
||||
// Read DIB header
|
||||
header = DIBHeader.read(imageInput);
|
||||
|
||||
if (pixelOffset < header.size + DIB.BMP_FILE_HEADER_SIZE) {
|
||||
throw new IIOException("Invalid pixel offset: " + pixelOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,7 +205,7 @@ public final class BMPImageReader extends ImageReaderBase {
|
||||
checkBounds(pImageIndex);
|
||||
|
||||
// TODO: Better implementation, include INT_RGB types for 3BYTE_BGR and 4BYTE_ABGR for INT_ARGB
|
||||
return Arrays.asList(getRawImageType(pImageIndex)).iterator();
|
||||
return Collections.singletonList(getRawImageType(pImageIndex)).iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -207,50 +216,55 @@ public final class BMPImageReader extends ImageReaderBase {
|
||||
throw new IIOException("Multiple planes not supported");
|
||||
}
|
||||
|
||||
switch (header.getBitCount()) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 4:
|
||||
case 8:
|
||||
return ImageTypeSpecifiers.createFromIndexColorModel(readColorMap());
|
||||
try {
|
||||
switch (header.getBitCount()) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 4:
|
||||
case 8:
|
||||
return ImageTypeSpecifiers.createFromIndexColorModel(readColorMap());
|
||||
|
||||
case 16:
|
||||
if (header.hasMasks()) {
|
||||
return ImageTypeSpecifiers.createPacked(
|
||||
ColorSpace.getInstance(ColorSpace.CS_sRGB),
|
||||
header.masks[0], header.masks[1], header.masks[2], header.masks[3],
|
||||
DataBuffer.TYPE_USHORT, false
|
||||
);
|
||||
}
|
||||
case 16:
|
||||
if (header.hasMasks()) {
|
||||
return ImageTypeSpecifiers.createPacked(
|
||||
ColorSpace.getInstance(ColorSpace.CS_sRGB),
|
||||
header.masks[0], header.masks[1], header.masks[2], header.masks[3],
|
||||
DataBuffer.TYPE_USHORT, false
|
||||
);
|
||||
}
|
||||
|
||||
// Default if no mask is 555
|
||||
return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_USHORT_555_RGB);
|
||||
// Default if no mask is 555
|
||||
return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_USHORT_555_RGB);
|
||||
|
||||
case 24:
|
||||
if (header.getCompression() != DIB.COMPRESSION_RGB) {
|
||||
throw new IIOException("Unsupported compression for RGB: " + header.getCompression());
|
||||
}
|
||||
case 24:
|
||||
if (header.getCompression() != DIB.COMPRESSION_RGB) {
|
||||
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:
|
||||
if (header.hasMasks()) {
|
||||
return ImageTypeSpecifiers.createPacked(
|
||||
ColorSpace.getInstance(ColorSpace.CS_sRGB),
|
||||
header.masks[0], header.masks[1], header.masks[2], header.masks[3],
|
||||
DataBuffer.TYPE_INT, false
|
||||
);
|
||||
}
|
||||
case 32:
|
||||
if (header.hasMasks()) {
|
||||
return ImageTypeSpecifiers.createPacked(
|
||||
ColorSpace.getInstance(ColorSpace.CS_sRGB),
|
||||
header.masks[0], header.masks[1], header.masks[2], header.masks[3],
|
||||
DataBuffer.TYPE_INT, false
|
||||
);
|
||||
}
|
||||
|
||||
// Default if no mask
|
||||
return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB);
|
||||
// Default if no mask
|
||||
return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB);
|
||||
|
||||
case 0:
|
||||
if (header.getCompression() == DIB.COMPRESSION_JPEG || header.getCompression() == DIB.COMPRESSION_PNG) {
|
||||
return initReaderDelegate(header.getCompression()).getRawImageType(0);
|
||||
}
|
||||
default:
|
||||
throw new IIOException("Unsupported bit count: " + header.getBitCount());
|
||||
case 0:
|
||||
if (header.getCompression() == DIB.COMPRESSION_JPEG || header.getCompression() == DIB.COMPRESSION_PNG) {
|
||||
return initReaderDelegate(header.getCompression()).getRawImageType(0);
|
||||
}
|
||||
default:
|
||||
throw new IIOException("Unsupported bit count: " + header.getBitCount());
|
||||
}
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
throw new IIOException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -396,6 +410,13 @@ public final class BMPImageReader extends ImageReaderBase {
|
||||
|
||||
private ImageReader initReaderDelegate(int compression) throws IOException {
|
||||
ImageReader reader = getImageReaderDelegate(compression);
|
||||
reader.reset();
|
||||
|
||||
// Install listener
|
||||
ListenerDelegator listenerDelegator = new ListenerDelegator();
|
||||
reader.addIIOReadWarningListener(listenerDelegator);
|
||||
reader.addIIOReadProgressListener(listenerDelegator);
|
||||
reader.addIIOReadUpdateListener(listenerDelegator);
|
||||
|
||||
imageInput.seek(pixelOffset);
|
||||
reader.setInput(new SubImageInputStream(imageInput, header.getImageSize()));
|
||||
@@ -436,12 +457,6 @@ public final class BMPImageReader extends ImageReaderBase {
|
||||
|
||||
ImageReader reader = readers.next();
|
||||
|
||||
// Install listener
|
||||
ListenerDelegator listenerDelegator = new ListenerDelegator();
|
||||
reader.addIIOReadWarningListener(listenerDelegator);
|
||||
reader.addIIOReadProgressListener(listenerDelegator);
|
||||
reader.addIIOReadUpdateListener(listenerDelegator);
|
||||
|
||||
// Cache for later use
|
||||
switch (compression) {
|
||||
case DIB.COMPRESSION_JPEG:
|
||||
@@ -619,7 +634,8 @@ public final class BMPImageReader extends ImageReaderBase {
|
||||
return new BMPMetadata(header, colors);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public static void main(String[] args) {
|
||||
BMPImageReaderSpi provider = new BMPImageReaderSpi();
|
||||
BMPImageReader reader = new BMPImageReader(provider);
|
||||
|
||||
@@ -672,7 +688,7 @@ public final class BMPImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "UnusedDeclaration"})
|
||||
@SuppressWarnings({ "unchecked", "UnusedDeclaration", "SameParameterValue" })
|
||||
static <T extends Throwable> void throwAs(final Class<T> pType, final Throwable pThrowable) throws T {
|
||||
throw (T) pThrowable;
|
||||
}
|
||||
|
||||
+5
-7
@@ -29,9 +29,10 @@
|
||||
*/
|
||||
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.io.IOException;
|
||||
|
||||
/**
|
||||
* Describes a bitmap structure.
|
||||
@@ -47,14 +48,11 @@ abstract class BitmapDescriptor {
|
||||
protected BitmapMask mask;
|
||||
|
||||
public BitmapDescriptor(final DirectoryEntry pEntry, final DIBHeader pHeader) {
|
||||
Validate.notNull(pEntry, "entry");
|
||||
Validate.notNull(pHeader, "header");
|
||||
|
||||
entry = pEntry;
|
||||
header = pHeader;
|
||||
entry = notNull(pEntry, "entry");;
|
||||
header = notNull(pHeader, "header");
|
||||
}
|
||||
|
||||
abstract public BufferedImage getImage();
|
||||
abstract public BufferedImage getImage() throws IOException;
|
||||
|
||||
public final int getWidth() {
|
||||
return entry.getWidth();
|
||||
|
||||
+1
@@ -163,6 +163,7 @@ class BitmapIndexed extends BitmapDescriptor {
|
||||
return transparent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedImage getImage() {
|
||||
if (image == null) {
|
||||
image = createImageIndexed();
|
||||
|
||||
@@ -46,6 +46,7 @@ class BitmapRGB extends BitmapDescriptor {
|
||||
super(pEntry, pHeader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedImage getImage() {
|
||||
// Test is mask != null rather than hasMask(), as 32 bit (w/alpha)
|
||||
// might still have bitmask, but we don't read or use it.
|
||||
|
||||
+8
-4
@@ -31,6 +31,9 @@
|
||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
|
||||
/**
|
||||
* Represents bitmap structures we can't read.
|
||||
@@ -42,13 +45,14 @@ import java.awt.image.BufferedImage;
|
||||
class BitmapUnsupported extends BitmapDescriptor {
|
||||
private String message;
|
||||
|
||||
public BitmapUnsupported(final DirectoryEntry pEntry, final String pMessage) {
|
||||
super(pEntry, null);
|
||||
public BitmapUnsupported(final DirectoryEntry pEntry, DIBHeader header, final String pMessage) {
|
||||
super(pEntry, header);
|
||||
|
||||
message = pMessage;
|
||||
}
|
||||
|
||||
public BufferedImage getImage() {
|
||||
throw new IllegalStateException(message);
|
||||
@Override
|
||||
public BufferedImage getImage() throws IOException {
|
||||
throw new IIOException(message);
|
||||
}
|
||||
}
|
||||
|
||||
+9
-3
@@ -30,11 +30,12 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
|
||||
/**
|
||||
* 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)!
|
||||
width = pStream.readUnsignedShort();
|
||||
height = pStream.readUnsignedShort();
|
||||
height = pStream.readShort();
|
||||
|
||||
if (height < 0) {
|
||||
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>
|
||||
*/
|
||||
static final class BitmapCoreHeaderV2 extends DIBHeader {
|
||||
@SuppressWarnings("unused")
|
||||
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) {
|
||||
throw new IIOException(String.format("Size: %s !=: %s", pSize, DIB.OS2_V2_HEADER_SIZE));
|
||||
@@ -362,7 +364,11 @@ abstract class DIBHeader {
|
||||
|
||||
public String getBMPVersion() {
|
||||
// This is to be compatible with the native metadata of the original com.sun....BMPMetadata
|
||||
return compression == DIB.COMPRESSION_BITFIELDS ? "BMP v. 3.x NT" : "BMP v. 3.x";
|
||||
return size > DIB.BITMAP_INFO_HEADER_SIZE
|
||||
? "BMP V2/V3 INFO"
|
||||
: compression == DIB.COMPRESSION_BITFIELDS || compression == DIB.COMPRESSION_ALPHA_BITFIELDS
|
||||
? "BMP v. 3.x NT"
|
||||
: "BMP v. 3.x";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+24
-17
@@ -30,16 +30,6 @@
|
||||
|
||||
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.color.ColorSpace;
|
||||
import java.awt.event.WindowAdapter;
|
||||
@@ -48,8 +38,26 @@ import java.awt.image.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
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.
|
||||
@@ -287,7 +295,7 @@ abstract class DIBImageReader extends ImageReaderBase {
|
||||
|
||||
// TODO: Support this, it's already in the BMP reader, spec allows RLE4 and RLE8
|
||||
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 {
|
||||
int bitCount = header.getBitCount();
|
||||
@@ -315,7 +323,7 @@ abstract class DIBImageReader extends ImageReaderBase {
|
||||
break;
|
||||
|
||||
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 {
|
||||
int width = adjustToPadding(pBitmap.getWidth() >> 3);
|
||||
int width = adjustToPadding((pBitmap.getWidth() + 7) >> 3);
|
||||
byte[] row = new byte[width];
|
||||
|
||||
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 {
|
||||
int width = adjustToPadding(pBitmap.getWidth() >> 1);
|
||||
int width = adjustToPadding((pBitmap.getWidth() + 1) >> 1);
|
||||
byte[] row = new byte[width];
|
||||
|
||||
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 {
|
||||
// TODO: No idea if this actually works..
|
||||
short[] pixels = new short[pBitmap.getWidth() * pBitmap.getHeight()];
|
||||
|
||||
// TODO: Support TYPE_USHORT_565 and the RGB 444/ARGB 4444 layouts
|
||||
// Will create TYPE_USHORT_555
|
||||
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(
|
||||
buffer, pBitmap.getWidth(), pBitmap.getHeight(), pBitmap.getWidth(), cm.getMasks(), null
|
||||
);
|
||||
|
||||
+4
-4
@@ -41,8 +41,8 @@ import java.util.Arrays;
|
||||
* @version $Id: RLE4Decoder.java#1 $
|
||||
*/
|
||||
final class RLE4Decoder extends AbstractRLEDecoder {
|
||||
final static int BIT_MASKS[] = {0xf0, 0x0f};
|
||||
final static int BIT_SHIFTS[] = {4, 0};
|
||||
final static int[] BIT_MASKS = {0xf0, 0x0f};
|
||||
final static int[] BIT_SHIFTS = {4, 0};
|
||||
|
||||
public RLE4Decoder(final int width) {
|
||||
super(width, 4);
|
||||
@@ -94,7 +94,7 @@ final class RLE4Decoder extends AbstractRLEDecoder {
|
||||
boolean paddingByte = (((byte2 + 1) / 2) % 2) != 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) {
|
||||
packed = checkEOF(stream.read());
|
||||
}
|
||||
@@ -111,7 +111,7 @@ final class RLE4Decoder extends AbstractRLEDecoder {
|
||||
else {
|
||||
// Encoded mode
|
||||
// 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]);
|
||||
srcX++;
|
||||
}
|
||||
|
||||
+2
-2
@@ -94,7 +94,7 @@ final class RLE8Decoder extends AbstractRLEDecoder {
|
||||
// an additional padding byte is in the stream and must be skipped
|
||||
boolean paddingByte = (byte2 % 2) != 0;
|
||||
|
||||
while (byte2-- > 0) {
|
||||
while (byte2-- > 0 && srcX < row.length) {
|
||||
row[srcX++] = (byte) checkEOF(stream.read());
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ final class RLE8Decoder extends AbstractRLEDecoder {
|
||||
// Encoded mode
|
||||
// Replicate byte2 as many times as byte1 says
|
||||
byte value = (byte) byte2;
|
||||
while (byte1-- > 0) {
|
||||
while (byte1-- > 0 && srcX < row.length) {
|
||||
row[srcX++] = value;
|
||||
}
|
||||
}
|
||||
|
||||
+49
-41
@@ -30,35 +30,43 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||
import com.twelvemonkeys.xml.XMLSerializer;
|
||||
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.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.Test;
|
||||
import org.mockito.InOrder;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import javax.imageio.*;
|
||||
import javax.imageio.event.IIOReadProgressListener;
|
||||
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.*;
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||
import com.twelvemonkeys.xml.XMLSerializer;
|
||||
|
||||
/**
|
||||
* 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$
|
||||
*/
|
||||
public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader> {
|
||||
@Override
|
||||
protected ImageReaderSpi createProvider() {
|
||||
return new BMPImageReaderSpi();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<TestData> getTestData() {
|
||||
return Arrays.asList(
|
||||
// BMP Suite "Good"
|
||||
@@ -144,27 +158,17 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
|
||||
);
|
||||
}
|
||||
|
||||
protected ImageReaderSpi createProvider() {
|
||||
return new BMPImageReaderSpi();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BMPImageReader createReader() {
|
||||
return new BMPImageReader(createProvider());
|
||||
}
|
||||
|
||||
protected Class<BMPImageReader> getReaderClass() {
|
||||
return BMPImageReader.class;
|
||||
}
|
||||
|
||||
protected List<String> getFormatNames() {
|
||||
return Collections.singletonList("bmp");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getSuffixes() {
|
||||
return Arrays.asList("bmp", "rle");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getMIMETypes() {
|
||||
return Collections.singletonList("image/bmp");
|
||||
}
|
||||
@@ -178,7 +182,7 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
|
||||
|
||||
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")) {
|
||||
continue;
|
||||
}
|
||||
@@ -273,7 +277,7 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddIIOReadProgressListenerCallbacksJPEG() {
|
||||
public void testAddIIOReadProgressListenerCallbacksJPEG() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
TestData data = new TestData(getClassLoaderResource("/bmpsuite/q/rgb24jpeg.bmp"), new Dimension(127, 64));
|
||||
reader.setInput(data.getInputStream());
|
||||
@@ -296,7 +300,7 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddIIOReadProgressListenerCallbacksPNG() {
|
||||
public void testAddIIOReadProgressListenerCallbacksPNG() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
TestData data = new TestData(getClassLoaderResource("/bmpsuite/q/rgb24png.bmp"), new Dimension(127, 64));
|
||||
reader.setInput(data.getInputStream());
|
||||
@@ -319,7 +323,7 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMetadataEqualsJRE() throws IOException, URISyntaxException {
|
||||
public void testMetadataEqualsJRE() throws IOException {
|
||||
ImageReader jreReader;
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -338,6 +342,7 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
|
||||
|
||||
for (TestData data : getTestData()) {
|
||||
if (data.getInput().toString().contains("pal8offs")) {
|
||||
// Skip: Contains extra bogus PaletteEntry nodes
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -354,9 +359,10 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
|
||||
System.err.println("WARNING: Reading " + data + " caused exception: " + e.getMessage());
|
||||
continue;
|
||||
}
|
||||
|
||||
IIOMetadata jreMetadata = jreReader.getImageMetadata(0);
|
||||
|
||||
assertEquals(true, metadata.isStandardMetadataFormatSupported());
|
||||
assertTrue(metadata.isStandardMetadataFormatSupported());
|
||||
assertEquals(jreMetadata.getNativeMetadataFormatName(), metadata.getNativeMetadataFormatName());
|
||||
assertArrayEquals(jreMetadata.getExtraMetadataFormatNames(), metadata.getExtraMetadataFormatNames());
|
||||
|
||||
@@ -366,6 +372,7 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
|
||||
String absolutePath = data.toString();
|
||||
String localPath = absolutePath.substring(absolutePath.lastIndexOf("test-classes") + 12);
|
||||
|
||||
// TODO: blauesglas_16_bitmask444 fails BMP Version for 11+
|
||||
Node expectedTree = jreMetadata.getAsTree(format);
|
||||
Node actualTree = metadata.getAsTree(format);
|
||||
|
||||
@@ -379,7 +386,7 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
|
||||
new XMLSerializer(expected, "UTF-8").serialize(expectedTree, 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;
|
||||
}
|
||||
@@ -424,6 +431,7 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("RedundantIfStatement")
|
||||
private boolean excludeEqualValueTest(final Node expected) {
|
||||
if (expected.getLocalName().equals("ImageSize")) {
|
||||
// JRE metadata returns 0, even if known in reader...
|
||||
|
||||
+6
-9
@@ -2,10 +2,10 @@ package com.twelvemonkeys.imageio.plugins.bmp;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageWriterAbstractTest;
|
||||
|
||||
import javax.imageio.ImageWriter;
|
||||
import javax.imageio.spi.ImageWriterSpi;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.RenderedImage;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -15,18 +15,15 @@ import java.util.List;
|
||||
* @author last modified by : harald.kuhr$
|
||||
* @version : BMPImageWriterTest.java,v 1.0 25/06/2020 harald.kuhr Exp$
|
||||
*/
|
||||
public class BMPImageWriterTest extends ImageWriterAbstractTest {
|
||||
|
||||
private final BMPImageWriterSpi provider = new BMPImageWriterSpi();
|
||||
|
||||
public class BMPImageWriterTest extends ImageWriterAbstractTest<BMPImageWriter> {
|
||||
@Override
|
||||
protected ImageWriter createImageWriter() {
|
||||
return provider.createWriterInstance(null);
|
||||
protected ImageWriterSpi createProvider() {
|
||||
return new BMPImageWriterSpi();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<? extends RenderedImage> getTestData() {
|
||||
return Arrays.asList(
|
||||
return Collections.singletonList(
|
||||
new BufferedImage(10, 10, BufferedImage.TYPE_4BYTE_ABGR)
|
||||
);
|
||||
}
|
||||
|
||||
+10
-13
@@ -31,6 +31,7 @@
|
||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||
|
||||
import org.junit.Ignore;
|
||||
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$
|
||||
*/
|
||||
public class CURImageReaderTest extends ImageReaderAbstractTest<CURImageReader> {
|
||||
@Override
|
||||
protected ImageReaderSpi createProvider() {
|
||||
return new CURImageReaderSpi();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<TestData> getTestData() {
|
||||
return Arrays.asList(
|
||||
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
|
||||
protected CURImageReader createReader() {
|
||||
return new CURImageReader();
|
||||
}
|
||||
|
||||
protected Class<CURImageReader> getReaderClass() {
|
||||
return CURImageReader.class;
|
||||
}
|
||||
|
||||
protected List<String> getFormatNames() {
|
||||
return Collections.singletonList("cur");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getSuffixes() {
|
||||
return Collections.singletonList("cur");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getMIMETypes() {
|
||||
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);
|
||||
|
||||
// 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);
|
||||
assertEquals(pExpected, hotspot);
|
||||
|
||||
+9
-12
@@ -31,6 +31,7 @@
|
||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||
|
||||
import org.junit.Ignore;
|
||||
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$
|
||||
*/
|
||||
public class ICOImageReaderTest extends ImageReaderAbstractTest<ICOImageReader> {
|
||||
@Override
|
||||
protected ImageReaderSpi createProvider() {
|
||||
return new ICOImageReaderSpi();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<TestData> getTestData() {
|
||||
return Arrays.asList(
|
||||
new TestData(
|
||||
@@ -75,27 +82,17 @@ public class ICOImageReaderTest extends ImageReaderAbstractTest<ICOImageReader>
|
||||
);
|
||||
}
|
||||
|
||||
protected ImageReaderSpi createProvider() {
|
||||
return new ICOImageReaderSpi();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ICOImageReader createReader() {
|
||||
return new ICOImageReader();
|
||||
}
|
||||
|
||||
protected Class<ICOImageReader> getReaderClass() {
|
||||
return ICOImageReader.class;
|
||||
}
|
||||
|
||||
protected List<String> getFormatNames() {
|
||||
return Collections.singletonList("ico");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getSuffixes() {
|
||||
return Collections.singletonList("ico");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getMIMETypes() {
|
||||
return Arrays.asList("image/vnd.microsoft.icon", "image/ico", "image/x-icon");
|
||||
}
|
||||
|
||||
+4
-6
@@ -2,7 +2,7 @@ package com.twelvemonkeys.imageio.plugins.bmp;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageWriterAbstractTest;
|
||||
|
||||
import javax.imageio.ImageWriter;
|
||||
import javax.imageio.spi.ImageWriterSpi;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.RenderedImage;
|
||||
import java.util.Arrays;
|
||||
@@ -15,12 +15,10 @@ import java.util.List;
|
||||
* @author last modified by : harald.kuhr$
|
||||
* @version : ICOImageWriterTest.java,v 1.0 25/06/2020 harald.kuhr Exp$
|
||||
*/
|
||||
public class ICOImageWriterTest extends ImageWriterAbstractTest {
|
||||
private final ICOImageWriterSpi provider = new ICOImageWriterSpi();
|
||||
|
||||
public class ICOImageWriterTest extends ImageWriterAbstractTest<ICOImageWriter> {
|
||||
@Override
|
||||
protected ImageWriter createImageWriter() {
|
||||
return provider.createWriterInstance(null);
|
||||
protected ImageWriterSpi createProvider() {
|
||||
return new ICOImageWriterSpi();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.6</version>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-clippath</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: Photoshop Path Support</name>
|
||||
@@ -12,6 +12,10 @@
|
||||
Photoshop Clipping Path Support.
|
||||
</description>
|
||||
|
||||
<properties>
|
||||
<project.jpms.module.name>com.twelvemonkeys.imageio.clippath</project.jpms.module.name>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
@@ -21,6 +25,7 @@
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
|
||||
@@ -56,6 +56,8 @@ import java.awt.image.BufferedImage;
|
||||
import java.awt.image.RenderedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.SequenceInputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@@ -131,7 +133,13 @@ public final class Paths {
|
||||
List<JPEGSegment> photoshop = JPEGSegmentUtil.readSegments(stream, segmentIdentifiers);
|
||||
|
||||
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
|
||||
@@ -350,10 +358,10 @@ public final class Paths {
|
||||
IIOMetadataNode unknown = new IIOMetadataNode("unknown");
|
||||
unknown.setAttribute("MarkerTag", Integer.toString(JPEG.APP13 & 0xFF));
|
||||
|
||||
byte[] identfier = "Photoshop 3.0".getBytes(StandardCharsets.US_ASCII);
|
||||
byte[] data = new byte[identfier.length + 1 + pathResource.length];
|
||||
System.arraycopy(identfier, 0, data, 0, identfier.length);
|
||||
System.arraycopy(pathResource, 0, data, identfier.length + 1, pathResource.length);
|
||||
byte[] identifier = "Photoshop 3.0".getBytes(StandardCharsets.US_ASCII);
|
||||
byte[] data = new byte[identifier.length + 1 + pathResource.length];
|
||||
System.arraycopy(identifier, 0, data, 0, identifier.length);
|
||||
System.arraycopy(pathResource, 0, data, identifier.length + 1, pathResource.length);
|
||||
|
||||
unknown.setUserObject(data);
|
||||
|
||||
|
||||
@@ -4,11 +4,15 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.6</version>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: Core</name>
|
||||
|
||||
<properties>
|
||||
<project.jpms.module.name>com.twelvemonkeys.imageio.core</project.jpms.module.name>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
|
||||
@@ -265,8 +265,9 @@ public abstract class ImageReaderBase extends ImageReader {
|
||||
// - transferType is ok
|
||||
// - bands are ok
|
||||
// TODO: Test if color model is ok?
|
||||
if (specifier.getSampleModel().getTransferType() == dest.getSampleModel().getTransferType() &&
|
||||
specifier.getNumBands() <= dest.getSampleModel().getNumBands()) {
|
||||
if (specifier.getSampleModel().getTransferType() == dest.getSampleModel().getTransferType()
|
||||
&& Arrays.equals(specifier.getSampleModel().getSampleSize(), dest.getSampleModel().getSampleSize())
|
||||
&& specifier.getNumBands() <= dest.getSampleModel().getNumBands()) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
@@ -314,12 +315,12 @@ public abstract class ImageReaderBase extends ImageReader {
|
||||
|
||||
long dimension = (long) destWidth * destHeight;
|
||||
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();
|
||||
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
|
||||
@@ -440,6 +441,7 @@ public abstract class ImageReaderBase extends ImageReader {
|
||||
static final String ZOOM_IN = "zoom-in";
|
||||
static final String ZOOM_OUT = "zoom-out";
|
||||
static final String ZOOM_ACTUAL = "zoom-actual";
|
||||
static final String ZOOM_FIT = "zoom-fit";
|
||||
|
||||
private BufferedImage image;
|
||||
|
||||
@@ -515,9 +517,20 @@ public abstract class ImageReaderBase extends ImageReader {
|
||||
|
||||
private void setupActions() {
|
||||
// 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 out", .5), ZOOM_OUT, KeyStroke.getKeyStroke('-'), KeyStroke.getKeyStroke(KeyEvent.VK_SUBTRACT, 0));
|
||||
bindAction(new ZoomAction("Zoom actual"), ZOOM_ACTUAL, KeyStroke.getKeyStroke('0'), KeyStroke.getKeyStroke(KeyEvent.VK_0, 0));
|
||||
bindAction(new ZoomAction("Zoom in", 2), ZOOM_IN,
|
||||
KeyStroke.getKeyStroke('+'),
|
||||
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.getPasteAction(), (String) TransferHandler.getPasteAction().getValue(Action.NAME), KeyStroke.getKeyStroke(KeyEvent.VK_V, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
|
||||
@@ -534,6 +547,7 @@ public abstract class ImageReaderBase extends ImageReader {
|
||||
private JPopupMenu createPopupMenu() {
|
||||
JPopupMenu popup = new JPopupMenu();
|
||||
|
||||
popup.add(getActionMap().get(ZOOM_FIT));
|
||||
popup.add(getActionMap().get(ZOOM_ACTUAL));
|
||||
popup.add(getActionMap().get(ZOOM_IN));
|
||||
popup.add(getActionMap().get(ZOOM_OUT));
|
||||
@@ -554,7 +568,7 @@ public abstract class ImageReaderBase extends ImageReader {
|
||||
addCheckBoxItem(new ChangeBackgroundAction("Dark", Color.DARK_GRAY), background, group);
|
||||
addCheckBoxItem(new ChangeBackgroundAction("Black", Color.BLACK), background, group);
|
||||
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);
|
||||
addCheckBoxItem(chooseBackgroundAction, background, group);
|
||||
|
||||
@@ -668,14 +682,41 @@ public abstract class ImageReaderBase extends ImageReader {
|
||||
}
|
||||
else {
|
||||
Icon current = getIcon();
|
||||
int w = (int) Math.max(Math.min(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 w = Math.max(Math.min((int) (current.getIconWidth() * zoomFactor), image.getWidth() * 16), image.getWidth() / 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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 final BufferedImage image;
|
||||
|
||||
@@ -694,7 +735,7 @@ public abstract class ImageReaderBase extends ImageReader {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getTransferData(final DataFlavor flavor) throws UnsupportedFlavorException, IOException {
|
||||
public Object getTransferData(final DataFlavor flavor) throws UnsupportedFlavorException {
|
||||
if (isDataFlavorSupported(flavor)) {
|
||||
return image;
|
||||
}
|
||||
|
||||
+1
-1
@@ -61,7 +61,7 @@ public final class UInt32ColorModel extends ComponentColorModel {
|
||||
// This class only supports DataBuffer.TYPE_INT, cast is safe
|
||||
int[] ipixel = (int[]) pixel;
|
||||
for (int c = 0, nc = normOffset; c < numComponents; c++, nc++) {
|
||||
normComponents[nc] = ((float) (ipixel[c] & 0xffffffffl)) / ((float) ((1l << getComponentSize(c)) - 1));
|
||||
normComponents[nc] = ((float) (ipixel[c] & 0xFFFFFFFFL)) / ((float) ((1L << getComponentSize(c)) - 1));
|
||||
}
|
||||
|
||||
int numColorComponents = getNumColorComponents();
|
||||
|
||||
+4
-4
@@ -49,10 +49,10 @@ public abstract class ReaderWriterProviderInfo extends ProviderInfo {
|
||||
private final String[] mimeTypes;
|
||||
private final String readerClassName;
|
||||
private final String[] readerSpiClassNames;
|
||||
private final Class[] inputTypes = new Class[] {ImageInputStream.class};
|
||||
private final Class<?>[] inputTypes = new Class<?>[] {ImageInputStream.class};
|
||||
private final String writerClassName;
|
||||
private final String[] writerSpiClassNames;
|
||||
private final Class[] outputTypes = new Class[] {ImageOutputStream.class};
|
||||
private final Class<?>[] outputTypes = new Class<?>[] {ImageOutputStream.class};
|
||||
private final boolean supportsStandardStreamMetadata;
|
||||
private final String nativeStreamMetadataFormatName;
|
||||
private final String nativeStreamMetadataFormatClassName;
|
||||
@@ -80,7 +80,7 @@ public abstract class ReaderWriterProviderInfo extends ProviderInfo {
|
||||
final String writerClassName,
|
||||
final String[] writerSpiClassNames,
|
||||
final boolean supportsStandardStreamMetadata,
|
||||
final String nativeStreameMetadataFormatName,
|
||||
final String nativeStreamMetadataFormatName,
|
||||
final String nativeStreamMetadataFormatClassName,
|
||||
final String[] extraStreamMetadataFormatNames,
|
||||
final String[] extraStreamMetadataFormatClassNames,
|
||||
@@ -99,7 +99,7 @@ public abstract class ReaderWriterProviderInfo extends ProviderInfo {
|
||||
this.writerClassName = writerClassName;
|
||||
this.writerSpiClassNames = writerSpiClassNames;
|
||||
this.supportsStandardStreamMetadata = supportsStandardStreamMetadata;
|
||||
this.nativeStreamMetadataFormatName = nativeStreameMetadataFormatName;
|
||||
this.nativeStreamMetadataFormatName = nativeStreamMetadataFormatName;
|
||||
this.nativeStreamMetadataFormatClassName = nativeStreamMetadataFormatClassName;
|
||||
this.extraStreamMetadataFormatNames = extraStreamMetadataFormatNames;
|
||||
this.extraStreamMetadataFormatClassNames = extraStreamMetadataFormatClassNames;
|
||||
|
||||
+259
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.stream;
|
||||
|
||||
import javax.imageio.stream.ImageInputStreamImpl;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import static com.twelvemonkeys.lang.Validate.notNull;
|
||||
import static java.lang.Math.max;
|
||||
|
||||
/**
|
||||
* A buffered replacement for {@link javax.imageio.stream.FileImageInputStream}
|
||||
* that provides greatly improved performance for shorter reads, like single
|
||||
* byte or bit reads.
|
||||
* As with {@code javax.imageio.stream.FileImageInputStream}, either
|
||||
* {@link File} or {@link RandomAccessFile} can be used as input.
|
||||
*
|
||||
* @see javax.imageio.stream.FileImageInputStream
|
||||
*/
|
||||
// TODO: Create a memory-mapped version?
|
||||
// Or not... From java.nio.channels.FileChannel.map:
|
||||
// For most operating systems, mapping a file into memory is more
|
||||
// expensive than reading or writing a few tens of kilobytes of data via
|
||||
// the usual {@link #read read} and {@link #write write} methods. From the
|
||||
// standpoint of performance it is generally only worth mapping relatively
|
||||
// large files into memory.
|
||||
public final class BufferedFileImageInputStream extends ImageInputStreamImpl {
|
||||
static final int DEFAULT_BUFFER_SIZE = 8192;
|
||||
|
||||
private byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
|
||||
private int bufferPos;
|
||||
private int bufferLimit;
|
||||
|
||||
private final ByteBuffer integralCache = ByteBuffer.allocate(8);
|
||||
private final byte[] integralCacheArray = integralCache.array();
|
||||
|
||||
private RandomAccessFile raf;
|
||||
|
||||
/**
|
||||
* Constructs a <code>BufferedFileImageInputStream</code> that will read from a given <code>File</code>.
|
||||
*
|
||||
* @param file a <code>File</code> to read from.
|
||||
* @throws IllegalArgumentException if <code>file</code> is <code>null</code>.
|
||||
* @throws FileNotFoundException if <code>file</code> is a directory or cannot be opened for reading
|
||||
* for any reason.
|
||||
*/
|
||||
public BufferedFileImageInputStream(final File file) throws FileNotFoundException {
|
||||
this(new RandomAccessFile(notNull(file, "file"), "r"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a <code>BufferedFileImageInputStream</code> that will read from a given <code>RandomAccessFile</code>.
|
||||
*
|
||||
* @param raf a <code>RandomAccessFile</code> to read from.
|
||||
* @throws IllegalArgumentException if <code>raf</code> is <code>null</code>.
|
||||
*/
|
||||
public BufferedFileImageInputStream(final RandomAccessFile raf) {
|
||||
this.raf = notNull(raf, "raf");
|
||||
}
|
||||
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
private boolean fillBuffer() throws IOException {
|
||||
int length = raf.read(buffer, 0, buffer.length);
|
||||
bufferPos = 0;
|
||||
bufferLimit = max(length, 0);
|
||||
|
||||
return bufferLimit > 0;
|
||||
}
|
||||
|
||||
private boolean bufferEmpty() {
|
||||
return bufferPos >= bufferLimit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setByteOrder(ByteOrder byteOrder) {
|
||||
super.setByteOrder(byteOrder);
|
||||
integralCache.order(byteOrder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
checkClosed();
|
||||
|
||||
if (bufferEmpty() && !fillBuffer()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
bitOffset = 0;
|
||||
streamPos++;
|
||||
|
||||
return buffer[bufferPos++] & 0xff;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(final byte[] bytes, final int offset, final int length) throws IOException {
|
||||
checkClosed();
|
||||
bitOffset = 0;
|
||||
|
||||
if (bufferEmpty()) {
|
||||
// Bypass buffer if buffer is empty for reads longer than buffer
|
||||
if (length >= buffer.length) {
|
||||
return readDirect(bytes, offset, length);
|
||||
}
|
||||
else if (!fillBuffer()) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int fromBuffer = readBuffered(bytes, offset, length);
|
||||
|
||||
if (length > fromBuffer) {
|
||||
// Due to known bugs in certain JDK-bundled ImageIO plugins expecting read to behave as readFully,
|
||||
// we'll read as much as possible from the buffer, and the rest directly after
|
||||
return fromBuffer + max(0, readDirect(bytes, offset + fromBuffer, length - fromBuffer));
|
||||
}
|
||||
|
||||
return fromBuffer;
|
||||
}
|
||||
|
||||
private int readDirect(final byte[] bytes, final int offset, final int length) throws IOException {
|
||||
// Invalidate the buffer, as its contents is no longer in sync with the stream's position.
|
||||
bufferLimit = 0;
|
||||
int read = raf.read(bytes, offset, length);
|
||||
|
||||
if (read > 0) {
|
||||
streamPos += read;
|
||||
}
|
||||
|
||||
return read;
|
||||
}
|
||||
|
||||
private int readBuffered(final byte[] bytes, final int offset, final int length) {
|
||||
// Read as much as possible from buffer
|
||||
int available = Math.min(bufferLimit - bufferPos, length);
|
||||
|
||||
if (available > 0) {
|
||||
System.arraycopy(buffer, bufferPos, bytes, offset, available);
|
||||
bufferPos += available;
|
||||
streamPos += available;
|
||||
}
|
||||
|
||||
return available;
|
||||
}
|
||||
|
||||
public long length() {
|
||||
// WTF?! This method is allowed to throw IOException in the interface...
|
||||
try {
|
||||
checkClosed();
|
||||
return raf.length();
|
||||
}
|
||||
catch (IOException ignore) {
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
super.close();
|
||||
|
||||
raf.close();
|
||||
|
||||
raf = null;
|
||||
buffer = null;
|
||||
}
|
||||
|
||||
// Need to override the readShort(), readInt() and readLong() methods,
|
||||
// because the implementations in ImageInputStreamImpl expects the
|
||||
// read(byte[], int, int) to always read the expected number of bytes,
|
||||
// causing uninitialized values, alignment issues and EOFExceptions at
|
||||
// random places...
|
||||
// Notes:
|
||||
// * readUnsignedXx() is covered by their signed counterparts
|
||||
// * readChar() is covered by readShort()
|
||||
// * readFloat() and readDouble() is covered by readInt() and readLong()
|
||||
// respectively.
|
||||
// * readLong() may be covered by two readInt()s, we'll override to be safe
|
||||
|
||||
@Override
|
||||
public short readShort() throws IOException {
|
||||
readFully(integralCacheArray, 0, 2);
|
||||
|
||||
return integralCache.getShort(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readInt() throws IOException {
|
||||
readFully(integralCacheArray, 0, 4);
|
||||
|
||||
return integralCache.getInt(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long readLong() throws IOException {
|
||||
readFully(integralCacheArray, 0, 8);
|
||||
|
||||
return integralCache.getLong(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seek(long position) throws IOException {
|
||||
checkClosed();
|
||||
|
||||
if (position < flushedPos) {
|
||||
throw new IndexOutOfBoundsException("position < flushedPos!");
|
||||
}
|
||||
|
||||
bitOffset = 0;
|
||||
|
||||
if (streamPos == position) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Optimized to not invalidate buffer if new position is within current buffer
|
||||
long newBufferPos = bufferPos + position - streamPos;
|
||||
if (newBufferPos >= 0 && newBufferPos < bufferLimit) {
|
||||
bufferPos = (int) newBufferPos;
|
||||
}
|
||||
else {
|
||||
// Will invalidate buffer
|
||||
bufferLimit = 0;
|
||||
raf.seek(position);
|
||||
}
|
||||
|
||||
streamPos = position;
|
||||
}
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.stream;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ProviderInfo;
|
||||
|
||||
import javax.imageio.spi.ImageInputStreamSpi;
|
||||
import javax.imageio.spi.ServiceRegistry;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* BufferedFileImageInputStreamSpi
|
||||
* Experimental
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: BufferedFileImageInputStreamSpi.java,v 1.0 May 15, 2008 2:14:59 PM haraldk Exp$
|
||||
*/
|
||||
public class BufferedFileImageInputStreamSpi extends ImageInputStreamSpi {
|
||||
public BufferedFileImageInputStreamSpi() {
|
||||
this(new StreamProviderInfo());
|
||||
}
|
||||
|
||||
private BufferedFileImageInputStreamSpi(ProviderInfo providerInfo) {
|
||||
super(providerInfo.getVendorName(), providerInfo.getVersion(), File.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRegistration(final ServiceRegistry registry, final Class<?> category) {
|
||||
Iterator<ImageInputStreamSpi> providers = registry.getServiceProviders(ImageInputStreamSpi.class, new FileInputFilter(), true);
|
||||
|
||||
while (providers.hasNext()) {
|
||||
ImageInputStreamSpi provider = providers.next();
|
||||
if (provider != this) {
|
||||
registry.setOrdering(ImageInputStreamSpi.class, this, provider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ImageInputStream createInputStreamInstance(final Object input, final boolean pUseCache, final File pCacheDir) {
|
||||
if (input instanceof File) {
|
||||
try {
|
||||
return new BufferedFileImageInputStream((File) input);
|
||||
}
|
||||
catch (FileNotFoundException e) {
|
||||
// For consistency with the JRE bundled SPIs, we'll return null here,
|
||||
// even though the spec does not say that's allowed.
|
||||
// The problem is that the SPIs can only declare that they support an input type like a File,
|
||||
// instead they should be allowed to inspect the instance, to see that the file does exist...
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Expected input of type File: " + input);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUseCacheFile() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getDescription(final Locale pLocale) {
|
||||
return "Service provider that instantiates an ImageInputStream from a File";
|
||||
}
|
||||
|
||||
private static class FileInputFilter implements ServiceRegistry.Filter {
|
||||
@Override
|
||||
public boolean filter(final Object provider) {
|
||||
return ((ImageInputStreamSpi) provider).getInputClass() == File.class;
|
||||
}
|
||||
}
|
||||
}
|
||||
+27
-20
@@ -43,22 +43,28 @@ import static com.twelvemonkeys.lang.Validate.notNull;
|
||||
* A buffered {@code ImageInputStream}.
|
||||
* Experimental - seems to be effective for {@link javax.imageio.stream.FileImageInputStream}
|
||||
* and {@link javax.imageio.stream.FileCacheImageInputStream} when doing a lot of single-byte reads
|
||||
* (or short byte-array reads) on OS X at least.
|
||||
* (or short byte-array reads).
|
||||
* Code that uses the {@code readFully} methods are not affected by the issue.
|
||||
* <p>
|
||||
* NOTE: Invoking {@code close()} will <em>NOT</em> close the wrapped stream.
|
||||
* </p>
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: BufferedFileImageInputStream.java,v 1.0 May 15, 2008 4:36:49 PM haraldk Exp$
|
||||
*
|
||||
* @deprecated Use {@link BufferedFileImageInputStream} instead.
|
||||
*/
|
||||
// TODO: Create a provider for this (wrapping the FileIIS and FileCacheIIS classes), and disable the Sun built-in spis?
|
||||
// TODO: Test on other platforms, might be just an OS X issue
|
||||
@Deprecated
|
||||
public final class BufferedImageInputStream extends ImageInputStreamImpl implements ImageInputStream {
|
||||
static final int DEFAULT_BUFFER_SIZE = 8192;
|
||||
|
||||
private ImageInputStream stream;
|
||||
|
||||
private ByteBuffer buffer;
|
||||
private ByteBuffer integralCache = ByteBuffer.allocate(8);
|
||||
private ByteBuffer buffer;
|
||||
|
||||
private final ByteBuffer integralCache = ByteBuffer.allocate(8);
|
||||
private final byte[] integralCacheArray = integralCache.array();
|
||||
|
||||
public BufferedImageInputStream(final ImageInputStream pStream) throws IOException {
|
||||
this(pStream, DEFAULT_BUFFER_SIZE);
|
||||
@@ -97,10 +103,10 @@ public final class BufferedImageInputStream extends ImageInputStreamImpl impleme
|
||||
|
||||
if (!buffer.hasRemaining()) {
|
||||
fillBuffer();
|
||||
}
|
||||
|
||||
if (!buffer.hasRemaining()) {
|
||||
return -1;
|
||||
if (!buffer.hasRemaining()) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
bitOffset = 0;
|
||||
@@ -172,21 +178,21 @@ public final class BufferedImageInputStream extends ImageInputStreamImpl impleme
|
||||
|
||||
@Override
|
||||
public short readShort() throws IOException {
|
||||
readFully(integralCache.array(), 0, 2);
|
||||
readFully(integralCacheArray, 0, 2);
|
||||
|
||||
return integralCache.getShort(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readInt() throws IOException {
|
||||
readFully(integralCache.array(), 0, 4);
|
||||
readFully(integralCacheArray, 0, 4);
|
||||
|
||||
return integralCache.getInt(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long readLong() throws IOException {
|
||||
readFully(integralCache.array(), 0, 8);
|
||||
readFully(integralCacheArray, 0, 8);
|
||||
|
||||
return integralCache.getLong(0);
|
||||
}
|
||||
@@ -253,6 +259,7 @@ public final class BufferedImageInputStream extends ImageInputStreamImpl impleme
|
||||
}
|
||||
|
||||
int val = buffer.get() & 0xff;
|
||||
streamPos++;
|
||||
|
||||
accum <<= 8;
|
||||
accum |= val;
|
||||
@@ -262,9 +269,7 @@ public final class BufferedImageInputStream extends ImageInputStreamImpl impleme
|
||||
// Move byte position back if in the middle of a byte
|
||||
if (newBitOffset != 0) {
|
||||
buffer.position(buffer.position() - 1);
|
||||
}
|
||||
else {
|
||||
streamPos++;
|
||||
streamPos--;
|
||||
}
|
||||
|
||||
this.bitOffset = newBitOffset;
|
||||
@@ -279,26 +284,26 @@ public final class BufferedImageInputStream extends ImageInputStreamImpl impleme
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seek(long pPosition) throws IOException {
|
||||
public void seek(long position) throws IOException {
|
||||
checkClosed();
|
||||
bitOffset = 0;
|
||||
|
||||
if (streamPos == pPosition) {
|
||||
if (streamPos == position) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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()) {
|
||||
buffer.position((int) newBufferPos);
|
||||
}
|
||||
else {
|
||||
// Will invalidate buffer
|
||||
buffer.limit(0);
|
||||
stream.seek(pPosition);
|
||||
stream.seek(position);
|
||||
}
|
||||
|
||||
streamPos = pPosition;
|
||||
streamPos = position;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -330,7 +335,9 @@ public final class BufferedImageInputStream extends ImageInputStreamImpl impleme
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
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;
|
||||
buffer = null;
|
||||
}
|
||||
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.stream;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ProviderInfo;
|
||||
|
||||
import javax.imageio.spi.ImageInputStreamSpi;
|
||||
import javax.imageio.spi.ServiceRegistry;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.File;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* BufferedRAFImageInputStreamSpi
|
||||
* Experimental
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: BufferedRAFImageInputStreamSpi.java,v 1.0 May 15, 2008 2:14:59 PM haraldk Exp$
|
||||
*/
|
||||
public class BufferedRAFImageInputStreamSpi extends ImageInputStreamSpi {
|
||||
public BufferedRAFImageInputStreamSpi() {
|
||||
this(new StreamProviderInfo());
|
||||
}
|
||||
|
||||
private BufferedRAFImageInputStreamSpi(ProviderInfo providerInfo) {
|
||||
super(providerInfo.getVendorName(), providerInfo.getVersion(), RandomAccessFile.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRegistration(final ServiceRegistry registry, final Class<?> category) {
|
||||
Iterator<ImageInputStreamSpi> providers = registry.getServiceProviders(ImageInputStreamSpi.class, new RAFInputFilter(), true);
|
||||
|
||||
while (providers.hasNext()) {
|
||||
ImageInputStreamSpi provider = providers.next();
|
||||
if (provider != this) {
|
||||
registry.setOrdering(ImageInputStreamSpi.class, this, provider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ImageInputStream createInputStreamInstance(final Object input, final boolean pUseCache, final File pCacheDir) {
|
||||
if (input instanceof RandomAccessFile) {
|
||||
return new BufferedFileImageInputStream((RandomAccessFile) input);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Expected input of type RandomAccessFile: " + input);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUseCacheFile() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getDescription(final Locale pLocale) {
|
||||
return "Service provider that instantiates an ImageInputStream from a RandomAccessFile";
|
||||
}
|
||||
|
||||
private static class RAFInputFilter implements ServiceRegistry.Filter {
|
||||
@Override
|
||||
public boolean filter(final Object provider) {
|
||||
return ((ImageInputStreamSpi) provider).getInputClass() == RandomAccessFile.class;
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
-3
@@ -30,10 +30,11 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.stream;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ProviderInfo;
|
||||
|
||||
import javax.imageio.spi.ImageInputStreamSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
@@ -47,10 +48,14 @@ import java.util.Locale;
|
||||
public class ByteArrayImageInputStreamSpi extends ImageInputStreamSpi {
|
||||
|
||||
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[]) {
|
||||
return new ByteArrayImageInputStream((byte[]) pInput);
|
||||
}
|
||||
|
||||
+9
@@ -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());
|
||||
}
|
||||
}
|
||||
+20
-10
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* Copyright (c) 2021, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -30,9 +30,10 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.stream;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ProviderInfo;
|
||||
|
||||
import javax.imageio.spi.ImageInputStreamSpi;
|
||||
import javax.imageio.stream.FileCacheImageInputStream;
|
||||
import javax.imageio.stream.FileImageInputStream;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import javax.imageio.stream.MemoryCacheImageInputStream;
|
||||
import java.io.File;
|
||||
@@ -53,7 +54,11 @@ import java.util.Locale;
|
||||
// TODO: URI instead of URL?
|
||||
public class URLImageInputStreamSpi extends ImageInputStreamSpi {
|
||||
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
|
||||
@@ -66,7 +71,7 @@ public class URLImageInputStreamSpi extends ImageInputStreamSpi {
|
||||
// Special case for file protocol, a lot faster than FileCacheImageInputStream
|
||||
if ("file".equals(url.getProtocol())) {
|
||||
try {
|
||||
return new BufferedImageInputStream(new FileImageInputStream(new File(url.toURI())));
|
||||
return new BufferedFileImageInputStream(new File(url.toURI()));
|
||||
}
|
||||
catch (URISyntaxException ignore) {
|
||||
// This should never happen, but if it does, we'll fall back to using the stream
|
||||
@@ -75,29 +80,29 @@ public class URLImageInputStreamSpi extends ImageInputStreamSpi {
|
||||
}
|
||||
|
||||
// Otherwise revert to cached
|
||||
final InputStream stream = url.openStream();
|
||||
final InputStream urlStream = url.openStream();
|
||||
if (pUseCache) {
|
||||
return new BufferedImageInputStream(new FileCacheImageInputStream(stream, pCacheDir) {
|
||||
return new FileCacheImageInputStream(urlStream, pCacheDir) {
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
try {
|
||||
super.close();
|
||||
}
|
||||
finally {
|
||||
stream.close(); // NOTE: If this line throws IOE, it will shadow the original..
|
||||
urlStream.close(); // NOTE: If this line throws IOE, it will shadow the original..
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
else {
|
||||
return new MemoryCacheImageInputStream(stream) {
|
||||
return new MemoryCacheImageInputStream(urlStream) {
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
try {
|
||||
super.close();
|
||||
}
|
||||
finally {
|
||||
stream.close(); // NOTE: If this line throws IOE, it will shadow the original..
|
||||
urlStream.close(); // NOTE: If this line throws IOE, it will shadow the original..
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -108,6 +113,11 @@ public class URLImageInputStreamSpi extends ImageInputStreamSpi {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUseCacheFile() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public String getDescription(final Locale pLocale) {
|
||||
return "Service provider that instantiates an ImageInputStream from a URL";
|
||||
}
|
||||
|
||||
@@ -223,7 +223,12 @@ public final class IIOUtil {
|
||||
public static void subsampleRow(byte[] srcRow, int srcPos, int srcWidth,
|
||||
byte[] destRow, int destPos,
|
||||
int samplesPerPixel, int bitsPerSample, int samplePeriod) {
|
||||
Validate.isTrue(samplePeriod > 1, "samplePeriod must be > 1"); // Period == 1 could be a no-op...
|
||||
// Period == 1 is a no-op...
|
||||
if (samplePeriod == 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
Validate.isTrue(samplePeriod > 1, "samplePeriod must be > 1");
|
||||
Validate.isTrue(bitsPerSample > 0 && bitsPerSample <= 8 && (bitsPerSample == 1 || bitsPerSample % 2 == 0),
|
||||
"bitsPerSample must be > 0 and <= 8 and a power of 2");
|
||||
Validate.isTrue(samplesPerPixel > 0, "samplesPerPixel must be > 0");
|
||||
@@ -261,7 +266,12 @@ public final class IIOUtil {
|
||||
public static void subsampleRow(short[] srcRow, int srcPos, int srcWidth,
|
||||
short[] destRow, int destPos,
|
||||
int samplesPerPixel, int bitsPerSample, int samplePeriod) {
|
||||
Validate.isTrue(samplePeriod > 1, "samplePeriod must be > 1"); // Period == 1 could be a no-op...
|
||||
// Period == 1 is a no-op...
|
||||
if (samplePeriod == 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
Validate.isTrue(samplePeriod > 1, "samplePeriod must be > 1");
|
||||
Validate.isTrue(bitsPerSample > 0 && bitsPerSample <= 16 && (bitsPerSample == 1 || bitsPerSample % 2 == 0),
|
||||
"bitsPerSample must be > 0 and <= 16 and a power of 2");
|
||||
Validate.isTrue(samplesPerPixel > 0, "samplesPerPixel must be > 0");
|
||||
@@ -278,6 +288,28 @@ public final class IIOUtil {
|
||||
public static void subsampleRow(int[] srcRow, int srcPos, int srcWidth,
|
||||
int[] destRow, int destPos,
|
||||
int samplesPerPixel, int bitsPerSample, int samplePeriod) {
|
||||
// Period == 1 is a no-op...
|
||||
if (samplePeriod == 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
Validate.isTrue(samplePeriod > 1, "samplePeriod must be > 1");
|
||||
Validate.isTrue(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);
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
+40
-18
@@ -30,15 +30,22 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.util;
|
||||
|
||||
import com.twelvemonkeys.imageio.color.DiscreteAlphaIndexColorModel;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.*;
|
||||
|
||||
import static com.twelvemonkeys.lang.Validate.isTrue;
|
||||
import static com.twelvemonkeys.lang.Validate.notNull;
|
||||
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ColorModel;
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.awt.image.DirectColorModel;
|
||||
import java.awt.image.IndexColorModel;
|
||||
import java.awt.image.MultiPixelPackedSampleModel;
|
||||
import java.awt.image.SampleModel;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
|
||||
import com.twelvemonkeys.imageio.color.DiscreteAlphaIndexColorModel;
|
||||
|
||||
/**
|
||||
* Factory class for creating {@code ImageTypeSpecifier}s.
|
||||
* Fixes some subtle bugs in {@code ImageTypeSpecifier}'s factory methods, but
|
||||
@@ -169,21 +176,36 @@ public final class ImageTypeSpecifiers {
|
||||
|
||||
int numEntries = 1 << bits;
|
||||
|
||||
byte[] r = new byte[numEntries];
|
||||
byte[] g = new byte[numEntries];
|
||||
byte[] b = new byte[numEntries];
|
||||
ColorModel colorModel;
|
||||
|
||||
// Scale array values according to color profile..
|
||||
for (int i = 0; i < numEntries; i++) {
|
||||
float[] gray = new float[]{i / (float) (numEntries - 1)};
|
||||
float[] rgb = colorSpace.toRGB(gray);
|
||||
if (ColorSpace.getInstance(ColorSpace.CS_GRAY).equals(colorSpace)) {
|
||||
// For default gray, use linear response
|
||||
byte[] gray = new byte[numEntries];
|
||||
|
||||
r[i] = (byte) (rgb[0] * 255);
|
||||
g[i] = (byte) (rgb[1] * 255);
|
||||
b[i] = (byte) (rgb[2] * 255);
|
||||
for (int i = 0; i < numEntries; i++) {
|
||||
gray[i] = (byte) ((i * 255) / (numEntries - 1));
|
||||
}
|
||||
|
||||
colorModel = new IndexColorModel(bits, numEntries, gray, gray, gray);
|
||||
}
|
||||
else {
|
||||
byte[] r = new byte[numEntries];
|
||||
byte[] g = new byte[numEntries];
|
||||
byte[] b = new byte[numEntries];
|
||||
|
||||
// Scale array values according to color profile..
|
||||
for (int i = 0; i < numEntries; i++) {
|
||||
float[] gray = new float[] { i / (float) (numEntries - 1) };
|
||||
float[] rgb = colorSpace.toRGB(gray);
|
||||
|
||||
r[i] = (byte) Math.round(rgb[0] * 255);
|
||||
g[i] = (byte) Math.round(rgb[1] * 255);
|
||||
b[i] = (byte) Math.round(rgb[2] * 255);
|
||||
}
|
||||
|
||||
colorModel = new IndexColorModel(bits, numEntries, r, g, b);
|
||||
}
|
||||
|
||||
ColorModel colorModel = new IndexColorModel(bits, numEntries, r, g, b);
|
||||
SampleModel sampleModel = new MultiPixelPackedSampleModel(dataType, 1, 1, bits);
|
||||
|
||||
return new ImageTypeSpecifier(colorModel, sampleModel);
|
||||
@@ -201,7 +223,7 @@ public final class ImageTypeSpecifiers {
|
||||
}
|
||||
|
||||
public static ImageTypeSpecifier createFromIndexColorModel(final IndexColorModel pColorModel) {
|
||||
return IndexedImageTypeSpecifier.createFromIndexColorModel(pColorModel);
|
||||
return new IndexedImageTypeSpecifier(pColorModel);
|
||||
}
|
||||
|
||||
public static ImageTypeSpecifier createDiscreteAlphaIndexedFromIndexColorModel(final IndexColorModel pColorModel) {
|
||||
|
||||
+24
-27
@@ -30,14 +30,14 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.util;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.IndexColorModel;
|
||||
import java.awt.image.WritableRaster;
|
||||
import java.util.Hashtable;
|
||||
|
||||
import static com.twelvemonkeys.lang.Validate.notNull;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ColorModel;
|
||||
import java.awt.image.WritableRaster;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
|
||||
/**
|
||||
* IndexedImageTypeSpecifier
|
||||
*
|
||||
@@ -45,27 +45,24 @@ import static com.twelvemonkeys.lang.Validate.notNull;
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: IndexedImageTypeSpecifier.java,v 1.0 May 19, 2008 11:04:28 AM haraldk Exp$
|
||||
*/
|
||||
final class IndexedImageTypeSpecifier {
|
||||
private IndexedImageTypeSpecifier() {}
|
||||
final class IndexedImageTypeSpecifier extends ImageTypeSpecifier {
|
||||
IndexedImageTypeSpecifier(final ColorModel colorModel) {
|
||||
// For some reason, we need a sample model, even though we won't use it
|
||||
super(notNull(colorModel, "colorModel"), colorModel.createCompatibleSampleModel(1, 1));
|
||||
}
|
||||
|
||||
static ImageTypeSpecifier createFromIndexColorModel(final IndexColorModel pColorModel) {
|
||||
// For some reason, we need a sample model
|
||||
return new ImageTypeSpecifier(notNull(pColorModel, "colorModel"), pColorModel.createCompatibleSampleModel(1, 1)) {
|
||||
|
||||
@Override
|
||||
public final BufferedImage createBufferedImage(final int pWidth, final int pHeight) {
|
||||
try {
|
||||
// This is a fix for the super-method, that first creates a sample model, and then
|
||||
// creates a raster from it, using Raster.createWritableRaster. The problem with
|
||||
// that approach, is that it always creates a TYPE_CUSTOM BufferedImage for indexed images.
|
||||
WritableRaster raster = colorModel.createCompatibleWritableRaster(pWidth, pHeight);
|
||||
return new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), new Hashtable());
|
||||
}
|
||||
catch (NegativeArraySizeException e) {
|
||||
// Exception most likely thrown from a DataBuffer constructor
|
||||
throw new IllegalArgumentException("Array size > Integer.MAX_VALUE!");
|
||||
}
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public final BufferedImage createBufferedImage(final int pWidth, final int pHeight) {
|
||||
try {
|
||||
// This is a fix for the super-method, that first creates a sample model, and then
|
||||
// creates a raster from it, using Raster.createWritableRaster. The problem with
|
||||
// that approach, is that it always creates a TYPE_CUSTOM BufferedImage for indexed images.
|
||||
WritableRaster raster = colorModel.createCompatibleWritableRaster(pWidth, pHeight);
|
||||
return new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);
|
||||
}
|
||||
catch (NegativeArraySizeException e) {
|
||||
// Exception most likely thrown from a DataBuffer constructor
|
||||
throw new IllegalArgumentException("Array size > Integer.MAX_VALUE!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.util;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.*;
|
||||
|
||||
import static com.twelvemonkeys.lang.Validate.notNull;
|
||||
|
||||
/**
|
||||
* A class containing various raster utility methods.
|
||||
*/
|
||||
public final class RasterUtils {
|
||||
|
||||
private RasterUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a raster with {@code DataBuffer.TYPE_BYTE} transfer type.
|
||||
* Works for any raster from a {@code BufferedImage.TYPE_INT_*} image
|
||||
*
|
||||
* @param raster a {@code Raster} with either transfer type {@code DataBuffer.TYPE_BYTE}
|
||||
* or {@code DataBuffer.TYPE_INT} with `SinglePixelPackedSampleModel`, not {@code null}.
|
||||
* @return a raster with {@code DataBuffer.TYPE_BYTE} transfer type.
|
||||
* @throws IllegalArgumentException if {@code raster} does not have transfer type {@code DataBuffer.TYPE_BYTE}
|
||||
* or {@code DataBuffer.TYPE_INT} with `SinglePixelPackedSampleModel`
|
||||
* @throws NullPointerException if {@code raster} is {@code null}.
|
||||
*/
|
||||
public static Raster asByteRaster(final Raster raster) {
|
||||
return asByteRaster0(raster);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a writable raster with {@code DataBuffer.TYPE_BYTE} transfer type.
|
||||
* Works for any raster from a {@code BufferedImage.TYPE_INT_*} image.
|
||||
*
|
||||
* @param raster a {@code WritableRaster} with either transfer type {@code DataBuffer.TYPE_BYTE}
|
||||
* or {@code DataBuffer.TYPE_INT} with `SinglePixelPackedSampleModel`, not {@code null}.
|
||||
* @return a writable raster with {@code DataBuffer.TYPE_BYTE} transfer type.
|
||||
* @throws IllegalArgumentException if {@code raster} does not have transfer type {@code DataBuffer.TYPE_BYTE}
|
||||
* or {@code DataBuffer.TYPE_INT} with `SinglePixelPackedSampleModel`
|
||||
* @throws NullPointerException if {@code raster} is {@code null}.
|
||||
*/
|
||||
public static WritableRaster asByteRaster(final WritableRaster raster) {
|
||||
return (WritableRaster) asByteRaster0(raster);
|
||||
}
|
||||
|
||||
private static Raster asByteRaster0(final Raster raster) {
|
||||
switch (raster.getTransferType()) {
|
||||
case DataBuffer.TYPE_BYTE:
|
||||
return raster;
|
||||
case DataBuffer.TYPE_INT:
|
||||
SampleModel sampleModel = raster.getSampleModel();
|
||||
|
||||
if (!(sampleModel instanceof SinglePixelPackedSampleModel)) {
|
||||
throw new IllegalArgumentException(String.format("Requires SinglePixelPackedSampleModel, %s not supported", sampleModel.getClass().getSimpleName()));
|
||||
}
|
||||
|
||||
final int bands = 4;
|
||||
final DataBufferInt buffer = (DataBufferInt) raster.getDataBuffer();
|
||||
|
||||
int w = raster.getWidth();
|
||||
int h = raster.getHeight();
|
||||
int size = buffer.getSize();
|
||||
|
||||
return new WritableRaster(
|
||||
new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE, w, h, bands, w * bands, createBandOffsets((SinglePixelPackedSampleModel) sampleModel)),
|
||||
new DataBuffer(DataBuffer.TYPE_BYTE, size * bands) {
|
||||
final int[] MASKS = {
|
||||
0xffffff00,
|
||||
0xffff00ff,
|
||||
0xff00ffff,
|
||||
0x00ffffff,
|
||||
};
|
||||
|
||||
@Override
|
||||
public int getElem(int bank, int i) {
|
||||
int index = i / bands;
|
||||
int shift = (i % bands) * 8;
|
||||
|
||||
return (buffer.getElem(index) >>> shift) & 0xff;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setElem(int bank, int i, int val) {
|
||||
int index = i / bands;
|
||||
int element = i % bands;
|
||||
int shift = element * 8;
|
||||
|
||||
int value = (buffer.getElem(index) & MASKS[element]) | ((val & 0xff) << shift);
|
||||
buffer.setElem(index, value);
|
||||
}
|
||||
}, new Point()) {
|
||||
};
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException(String.format("Raster type %d not supported", raster.getTransferType()));
|
||||
}
|
||||
}
|
||||
|
||||
private static int[] createBandOffsets(final SinglePixelPackedSampleModel sampleModel) {
|
||||
notNull(sampleModel, "sampleModel");
|
||||
|
||||
int[] masks = sampleModel.getBitMasks();
|
||||
int[] offs = new int[masks.length];
|
||||
|
||||
for (int i = 0; i < masks.length; i++) {
|
||||
int mask = masks[i];
|
||||
int off = 0;
|
||||
|
||||
// TODO: FixMe! This only works for standard 8 bit masks (0xFF)
|
||||
if (mask != 0) {
|
||||
while ((mask & 0xFF) == 0) {
|
||||
mask >>>= 8;
|
||||
off++;
|
||||
}
|
||||
}
|
||||
|
||||
offs[i] = off;
|
||||
}
|
||||
|
||||
return offs;
|
||||
}
|
||||
}
|
||||
+18
-9
@@ -30,15 +30,16 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.util;
|
||||
|
||||
import com.twelvemonkeys.imageio.color.UInt32ColorModel;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.BandedSampleModel;
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.awt.image.PixelInterleavedSampleModel;
|
||||
import java.awt.image.SampleModel;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
|
||||
import com.twelvemonkeys.imageio.color.UInt32ColorModel;
|
||||
|
||||
/**
|
||||
* ImageTypeSpecifier for interleaved 32 bit unsigned integral samples.
|
||||
*
|
||||
@@ -47,11 +48,13 @@ import java.awt.image.SampleModel;
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: UInt32ImageTypeSpecifier.java,v 1.0 24.01.11 17.51 haraldk Exp$
|
||||
*/
|
||||
final class UInt32ImageTypeSpecifier {
|
||||
private UInt32ImageTypeSpecifier() {}
|
||||
final class UInt32ImageTypeSpecifier extends ImageTypeSpecifier {
|
||||
private UInt32ImageTypeSpecifier(final ColorSpace cs, final boolean hasAlpha, final boolean isAlphaPremultiplied, final SampleModel sampleModel) {
|
||||
super(new UInt32ColorModel(cs, hasAlpha, isAlphaPremultiplied), sampleModel);
|
||||
}
|
||||
|
||||
static ImageTypeSpecifier createInterleaved(final ColorSpace cs, final int[] bandOffsets, final boolean hasAlpha, final boolean isAlphaPremultiplied) {
|
||||
return create(
|
||||
return new UInt32ImageTypeSpecifier(
|
||||
cs, hasAlpha, isAlphaPremultiplied,
|
||||
new PixelInterleavedSampleModel(
|
||||
DataBuffer.TYPE_INT, 1, 1,
|
||||
@@ -63,7 +66,7 @@ final class UInt32ImageTypeSpecifier {
|
||||
}
|
||||
|
||||
static ImageTypeSpecifier createBanded(final ColorSpace cs, final int[] bandIndices, final int[] bandOffsets, final boolean hasAlpha, final boolean isAlphaPremultiplied) {
|
||||
return create(
|
||||
return new UInt32ImageTypeSpecifier(
|
||||
cs, hasAlpha, isAlphaPremultiplied,
|
||||
new BandedSampleModel(
|
||||
DataBuffer.TYPE_INT, 1, 1, 1,
|
||||
@@ -72,7 +75,13 @@ final class UInt32ImageTypeSpecifier {
|
||||
);
|
||||
}
|
||||
|
||||
private static ImageTypeSpecifier create(final ColorSpace cs, final boolean hasAlpha, final boolean isAlphaPremultiplied, final SampleModel sampleModel) {
|
||||
return new ImageTypeSpecifier(new UInt32ColorModel(cs, hasAlpha, isAlphaPremultiplied), sampleModel);
|
||||
@Override
|
||||
public boolean equals(final Object other) {
|
||||
if (!(other instanceof UInt32ImageTypeSpecifier)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UInt32ImageTypeSpecifier that = (UInt32ImageTypeSpecifier) other;
|
||||
return colorModel.equals(that.colorModel) && sampleModel.equals(that.sampleModel);
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
com.twelvemonkeys.imageio.stream.BufferedFileImageInputStreamSpi
|
||||
com.twelvemonkeys.imageio.stream.BufferedRAFImageInputStreamSpi
|
||||
+12
-9
@@ -30,11 +30,11 @@
|
||||
|
||||
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.color.ColorSpace;
|
||||
import java.awt.image.BufferedImage;
|
||||
@@ -44,8 +44,11 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.util.Collections.singleton;
|
||||
import static org.junit.Assert.*;
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* ImageReaderBaseTest
|
||||
@@ -212,19 +215,19 @@ public class ImageReaderBaseTest {
|
||||
assertEquals(TYPES.get(0).getBufferedImageType(), destination.getType());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
@Test(expected = IIOException.class)
|
||||
public void testGetDestinationParamDestinationExceedsIntegerMax() throws IIOException {
|
||||
ImageReadParam param = new ImageReadParam();
|
||||
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
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
@Test(expected = IIOException.class)
|
||||
public void testGetDestinationDimensionExceedsIntegerMax() throws IIOException {
|
||||
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 {
|
||||
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
|
||||
|
||||
+4
-2
@@ -35,6 +35,8 @@ import org.junit.Test;
|
||||
|
||||
import java.awt.image.*;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class DiscreteAlphaIndexColorModelTest {
|
||||
@@ -162,7 +164,7 @@ public class DiscreteAlphaIndexColorModelTest {
|
||||
assertEquals(2, sampleModel.getHeight());
|
||||
|
||||
assertTrue(colorModel.isCompatibleSampleModel(sampleModel));
|
||||
assertThat(sampleModel, CoreMatchers.is(PixelInterleavedSampleModel.class));
|
||||
assertThat(sampleModel, instanceOf(PixelInterleavedSampleModel.class));
|
||||
assertThat(sampleModel.getDataType(), CoreMatchers.equalTo(DataBuffer.TYPE_BYTE));
|
||||
}
|
||||
|
||||
@@ -180,7 +182,7 @@ public class DiscreteAlphaIndexColorModelTest {
|
||||
assertEquals(2, sampleModel.getHeight());
|
||||
|
||||
assertTrue(colorModel.isCompatibleSampleModel(sampleModel));
|
||||
assertThat(sampleModel, CoreMatchers.is(PixelInterleavedSampleModel.class));
|
||||
assertThat(sampleModel, instanceOf(PixelInterleavedSampleModel.class));
|
||||
assertThat(sampleModel.getDataType(), CoreMatchers.equalTo(DataBuffer.TYPE_USHORT));
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ import static org.junit.Assert.*;
|
||||
*/
|
||||
public class ProviderInfoTest {
|
||||
@Test
|
||||
public void testCreateNorma() {
|
||||
public void testCreateNormal() {
|
||||
new ProviderInfo(Package.getPackage("java.util"));
|
||||
}
|
||||
|
||||
|
||||
+15
-14
@@ -31,8 +31,8 @@
|
||||
package com.twelvemonkeys.imageio.spi;
|
||||
|
||||
import org.hamcrest.Description;
|
||||
import org.hamcrest.TypeSafeMatcher;
|
||||
import org.junit.Test;
|
||||
import org.junit.internal.matchers.TypeSafeMatcher;
|
||||
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.ImageWriter;
|
||||
@@ -42,6 +42,7 @@ import javax.imageio.spi.ImageWriterSpi;
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
@@ -62,52 +63,52 @@ public abstract class ReaderWriterProviderInfoTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readerClassName() throws Exception {
|
||||
public void readerClassName() {
|
||||
assertClassExists(providerInfo.readerClassName(), ImageReader.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readerSpiClassNames() throws Exception {
|
||||
public void readerSpiClassNames() {
|
||||
assertClassesExist(providerInfo.readerSpiClassNames(), ImageReaderSpi.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void inputTypes() throws Exception {
|
||||
public void inputTypes() {
|
||||
assertNotNull(providerInfo.inputTypes());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writerClassName() throws Exception {
|
||||
public void writerClassName() {
|
||||
assertClassExists(providerInfo.writerClassName(), ImageWriter.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writerSpiClassNames() throws Exception {
|
||||
public void writerSpiClassNames() {
|
||||
assertClassesExist(providerInfo.writerSpiClassNames(), ImageWriterSpi.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outputTypes() throws Exception {
|
||||
public void outputTypes() {
|
||||
assertNotNull(providerInfo.outputTypes());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nativeStreamMetadataFormatClassName() throws Exception {
|
||||
public void nativeStreamMetadataFormatClassName() {
|
||||
assertClassExists(providerInfo.nativeStreamMetadataFormatClassName(), IIOMetadataFormat.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extraStreamMetadataFormatClassNames() throws Exception {
|
||||
public void extraStreamMetadataFormatClassNames() {
|
||||
assertClassesExist(providerInfo.extraStreamMetadataFormatClassNames(), IIOMetadataFormat.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nativeImageMetadataFormatClassName() throws Exception {
|
||||
public void nativeImageMetadataFormatClassName() {
|
||||
assertClassExists(providerInfo.nativeImageMetadataFormatClassName(), IIOMetadataFormat.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extraImageMetadataFormatClassNames() throws Exception {
|
||||
public void extraImageMetadataFormatClassNames() {
|
||||
assertClassesExist(providerInfo.extraImageMetadataFormatClassNames(), IIOMetadataFormat.class);
|
||||
}
|
||||
|
||||
@@ -115,7 +116,7 @@ public abstract class ReaderWriterProviderInfoTest {
|
||||
public void formatNames() {
|
||||
String[] names = providerInfo.formatNames();
|
||||
assertNotNull(names);
|
||||
assertFalse(names.length == 0);
|
||||
assertNotEquals(0, names.length);
|
||||
|
||||
List<String> list = asList(names);
|
||||
|
||||
@@ -132,7 +133,7 @@ public abstract class ReaderWriterProviderInfoTest {
|
||||
public void suffixes() {
|
||||
String[] suffixes = providerInfo.suffixes();
|
||||
assertNotNull(suffixes);
|
||||
assertFalse(suffixes.length == 0);
|
||||
assertNotEquals(0, suffixes.length);
|
||||
|
||||
for (String suffix : suffixes) {
|
||||
assertNotNull(suffix);
|
||||
@@ -144,7 +145,7 @@ public abstract class ReaderWriterProviderInfoTest {
|
||||
public void mimeTypes() {
|
||||
String[] mimeTypes = providerInfo.mimeTypes();
|
||||
assertNotNull(mimeTypes);
|
||||
assertFalse(mimeTypes.length == 0);
|
||||
assertNotEquals(0, mimeTypes.length);
|
||||
|
||||
for (String mimeType : mimeTypes) {
|
||||
assertNotNull(mimeType);
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.twelvemonkeys.imageio.stream;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.spi.ImageInputStreamSpi;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assume.assumeFalse;
|
||||
|
||||
public class BufferedFileImageInputStreamSpiTest extends ImageInputStreamSpiTest<File> {
|
||||
@Override
|
||||
protected ImageInputStreamSpi createProvider() {
|
||||
return new BufferedFileImageInputStreamSpi();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected File createInput() throws IOException {
|
||||
return File.createTempFile("test-", ".tst");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReturnNullWhenFileDoesNotExist() throws IOException {
|
||||
// This is really stupid behavior, but it is consistent with the JRE bundled SPIs.
|
||||
File input = new File("a-file-that-should-not-exist-ever.fnf");
|
||||
assumeFalse("File should not exist: " + input.getPath(), input.exists());
|
||||
assertNull(provider.createInputStreamInstance(input));
|
||||
}
|
||||
}
|
||||
+429
@@ -0,0 +1,429 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.stream;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.function.ThrowingRunnable;
|
||||
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Random;
|
||||
|
||||
import static com.twelvemonkeys.imageio.stream.BufferedImageInputStreamTest.rangeEquals;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* BufferedFileImageInputStreamTestCase
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: BufferedFileImageInputStreamTestCase.java,v 1.0 Apr 21, 2009 10:58:48 AM haraldk Exp$
|
||||
*/
|
||||
public class BufferedFileImageInputStreamTest {
|
||||
private final Random random = new Random(170984354357234566L);
|
||||
|
||||
private File randomDataToFile(byte[] data) throws IOException {
|
||||
random.nextBytes(data);
|
||||
|
||||
File file = File.createTempFile("read", ".tmp");
|
||||
Files.write(file.toPath(), data);
|
||||
return file;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate() throws IOException {
|
||||
try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(File.createTempFile("empty", ".tmp"))) {
|
||||
assertEquals("Data length should be same as stream length", 0, stream.length());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateNullFile() throws IOException {
|
||||
try {
|
||||
new BufferedFileImageInputStream((File) null);
|
||||
fail("Expected IllegalArgumentException");
|
||||
}
|
||||
catch (IllegalArgumentException expected) {
|
||||
assertNotNull("Null exception message", expected.getMessage());
|
||||
String message = expected.getMessage().toLowerCase();
|
||||
assertTrue("Exception message does not contain parameter name", message.contains("file"));
|
||||
assertTrue("Exception message does not contain null", message.contains("null"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateNullRAF() {
|
||||
try {
|
||||
new BufferedFileImageInputStream((RandomAccessFile) null);
|
||||
fail("Expected IllegalArgumentException");
|
||||
}
|
||||
catch (IllegalArgumentException expected) {
|
||||
assertNotNull("Null exception message", expected.getMessage());
|
||||
String message = expected.getMessage().toLowerCase();
|
||||
assertTrue("Exception message does not contain parameter name", message.contains("raf"));
|
||||
assertTrue("Exception message does not contain null", message.contains("null"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRead() throws IOException {
|
||||
byte[] data = new byte[1024 * 1024];
|
||||
File file = randomDataToFile(data);
|
||||
|
||||
try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file)) {
|
||||
assertEquals("File length should be same as stream length", file.length(), stream.length());
|
||||
|
||||
for (byte value : data) {
|
||||
assertEquals("Wrong data read", value & 0xff, stream.read());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadArray() throws IOException {
|
||||
byte[] data = new byte[1024 * 1024];
|
||||
File file = randomDataToFile(data);
|
||||
|
||||
try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file)) {
|
||||
assertEquals("File length should be same as stream length", file.length(), stream.length());
|
||||
|
||||
byte[] result = new byte[1024];
|
||||
|
||||
for (int i = 0; i < data.length / result.length; i++) {
|
||||
stream.readFully(result);
|
||||
assertTrue("Wrong data read: " + i, rangeEquals(data, i * result.length, result, 0, result.length));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadSkip() throws IOException {
|
||||
byte[] data = new byte[1024 * 14];
|
||||
File file = randomDataToFile(data);
|
||||
|
||||
try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file)) {
|
||||
assertEquals("File length should be same as stream length", file.length(), stream.length());
|
||||
|
||||
byte[] result = new byte[7];
|
||||
|
||||
for (int i = 0; i < data.length / result.length; i += 2) {
|
||||
stream.readFully(result);
|
||||
stream.skipBytes(result.length);
|
||||
assertTrue("Wrong data read: " + i, rangeEquals(data, i * result.length, result, 0, result.length));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadSeek() throws IOException {
|
||||
byte[] data = new byte[1024 * 18];
|
||||
File file = randomDataToFile(data);
|
||||
|
||||
try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file)) {
|
||||
assertEquals("File length should be same as stream length", file.length(), stream.length());
|
||||
|
||||
byte[] result = new byte[9];
|
||||
|
||||
for (int i = 0; i < data.length / result.length; i++) {
|
||||
// Read backwards
|
||||
long newPos = stream.length() - result.length - i * result.length;
|
||||
stream.seek(newPos);
|
||||
assertEquals("Wrong stream position", newPos, stream.getStreamPosition());
|
||||
stream.readFully(result);
|
||||
assertTrue("Wrong data read: " + i, rangeEquals(data, (int) newPos, result, 0, result.length));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadOutsideDataSeek0Read() throws IOException {
|
||||
byte[] data = new byte[256];
|
||||
File file = randomDataToFile(data);
|
||||
|
||||
try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file)) {
|
||||
assertEquals("File length should be same as stream length", file.length(), stream.length());
|
||||
|
||||
byte[] buffer = new byte[data.length * 2];
|
||||
stream.read(buffer);
|
||||
stream.seek(0);
|
||||
assertNotEquals(-1, stream.read());
|
||||
assertNotEquals(-1, stream.read(buffer));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadBitRandom() throws IOException {
|
||||
byte[] bytes = new byte[8];
|
||||
File file = randomDataToFile(bytes);
|
||||
long value = ByteBuffer.wrap(bytes).getLong();
|
||||
|
||||
// Create stream
|
||||
try (ImageInputStream stream = new BufferedFileImageInputStream(file)) {
|
||||
for (int i = 1; i <= 64; i++) {
|
||||
assertEquals(String.format("bit %d differ", i), (value << (i - 1L)) >>> 63L, stream.readBit());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadBitsRandom() throws IOException {
|
||||
byte[] bytes = new byte[8];
|
||||
File file = randomDataToFile(bytes);
|
||||
long value = ByteBuffer.wrap(bytes).getLong();
|
||||
|
||||
// Create stream
|
||||
try (ImageInputStream stream = new BufferedFileImageInputStream(file)) {
|
||||
for (int i = 1; i <= 64; i++) {
|
||||
stream.seek(0);
|
||||
assertEquals(String.format("bit %d differ", i), value >>> (64L - i), stream.readBits(i));
|
||||
assertEquals(i % 8, stream.getBitOffset());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadBitsRandomOffset() throws IOException {
|
||||
byte[] bytes = new byte[8];
|
||||
File file = randomDataToFile(bytes);
|
||||
long value = ByteBuffer.wrap(bytes).getLong();
|
||||
|
||||
// Create stream
|
||||
try (ImageInputStream stream = new BufferedFileImageInputStream(file)) {
|
||||
for (int i = 1; i <= 60; i++) {
|
||||
stream.seek(0);
|
||||
stream.setBitOffset(i % 8);
|
||||
assertEquals(String.format("bit %d differ", i), (value << (i % 8)) >>> (64L - i), stream.readBits(i));
|
||||
assertEquals(i * 2 % 8, stream.getBitOffset());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadShort() throws IOException {
|
||||
byte[] bytes = new byte[8743]; // Slightly more than one buffer size
|
||||
File file = randomDataToFile(bytes);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
try (final ImageInputStream stream = new BufferedFileImageInputStream(file)) {
|
||||
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 2; i++) {
|
||||
assertEquals(buffer.getShort(), stream.readShort());
|
||||
}
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readShort();
|
||||
}
|
||||
});
|
||||
|
||||
stream.seek(0);
|
||||
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
buffer.position(0);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 2; i++) {
|
||||
assertEquals(buffer.getShort(), stream.readShort());
|
||||
}
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readShort();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadInt() throws IOException {
|
||||
byte[] bytes = new byte[8743]; // Slightly more than one buffer size
|
||||
File file = randomDataToFile(bytes);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
try (final ImageInputStream stream = new BufferedFileImageInputStream(file)) {
|
||||
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 4; i++) {
|
||||
assertEquals(buffer.getInt(), stream.readInt());
|
||||
}
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readInt();
|
||||
}
|
||||
});
|
||||
|
||||
stream.seek(0);
|
||||
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
buffer.position(0);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 4; i++) {
|
||||
assertEquals(buffer.getInt(), stream.readInt());
|
||||
}
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readInt();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadLong() throws IOException {
|
||||
byte[] bytes = new byte[8743]; // Slightly more than one buffer size
|
||||
File file = randomDataToFile(bytes);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
try (final ImageInputStream stream = new BufferedFileImageInputStream(file)) {
|
||||
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 8; i++) {
|
||||
assertEquals(buffer.getLong(), stream.readLong());
|
||||
}
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readLong();
|
||||
}
|
||||
});
|
||||
|
||||
stream.seek(0);
|
||||
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
buffer.position(0);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 8; i++) {
|
||||
assertEquals(buffer.getLong(), stream.readLong());
|
||||
}
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readLong();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSeekPastEOF() throws IOException {
|
||||
byte[] bytes = new byte[9];
|
||||
File file = randomDataToFile(bytes);
|
||||
|
||||
try (final ImageInputStream stream = new BufferedFileImageInputStream(file)) {
|
||||
stream.seek(1000);
|
||||
|
||||
assertEquals(-1, stream.read());
|
||||
assertEquals(-1, stream.read(new byte[1], 0, 1));
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readFully(new byte[1]);
|
||||
}
|
||||
});
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readByte();
|
||||
}
|
||||
});
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readShort();
|
||||
}
|
||||
});
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readInt();
|
||||
}
|
||||
});
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readLong();
|
||||
}
|
||||
});
|
||||
|
||||
stream.seek(0);
|
||||
for (byte value : bytes) {
|
||||
assertEquals(value, stream.readByte());
|
||||
}
|
||||
|
||||
assertEquals(-1, stream.read());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClose() throws IOException {
|
||||
// Create wrapper stream
|
||||
RandomAccessFile mock = mock(RandomAccessFile.class);
|
||||
ImageInputStream stream = new BufferedFileImageInputStream(mock);
|
||||
|
||||
stream.close();
|
||||
verify(mock, only()).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWorkaroundForWBMPImageReaderExpectsReadToBehaveAsReadFully() throws IOException {
|
||||
// See #606 for details.
|
||||
// Bug in JDK WBMPImageReader, uses read(byte[], int, int) instead of readFully(byte[], int, int).
|
||||
// Ie: Relies on read to return all bytes at once, without blocking
|
||||
int size = BufferedFileImageInputStream.DEFAULT_BUFFER_SIZE * 7;
|
||||
byte[] bytes = new byte[size];
|
||||
File file = randomDataToFile(bytes);
|
||||
|
||||
try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file)) {
|
||||
byte[] result = new byte[size];
|
||||
int head = stream.read(result, 0, 12); // Provoke a buffered read
|
||||
int len = stream.read(result, 12, size - 12); // Rest of buffer + direct read
|
||||
|
||||
assertEquals(size, len + head);
|
||||
assertArrayEquals(bytes, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
+254
@@ -32,16 +32,19 @@ package com.twelvemonkeys.imageio.stream;
|
||||
|
||||
import com.twelvemonkeys.io.ole2.CompoundDocument;
|
||||
import com.twelvemonkeys.io.ole2.Entry;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import javax.imageio.stream.MemoryCacheImageInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Random;
|
||||
|
||||
import static java.util.Arrays.fill;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* 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: Create test that exposes read += -1 (eof) bug
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.twelvemonkeys.imageio.stream;
|
||||
|
||||
import javax.imageio.spi.ImageInputStreamSpi;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
|
||||
public class BufferedRAFImageInputStreamSpiTest extends ImageInputStreamSpiTest<RandomAccessFile> {
|
||||
@Override
|
||||
protected ImageInputStreamSpi createProvider() {
|
||||
return new BufferedRAFImageInputStreamSpi();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RandomAccessFile createInput() throws IOException {
|
||||
return new RandomAccessFile(File.createTempFile("test-", ".tst"), "r");
|
||||
}
|
||||
}
|
||||
+16
@@ -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];
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -39,11 +39,11 @@ import static com.twelvemonkeys.imageio.stream.BufferedImageInputStreamTest.rang
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* ByteArrayImageInputStreamTestCase
|
||||
* ByteArrayImageInputStreamTest
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: ByteArrayImageInputStreamTestCase.java,v 1.0 Apr 21, 2009 10:58:48 AM haraldk Exp$
|
||||
* @version $Id: ByteArrayImageInputStreamTest.java,v 1.0 Apr 21, 2009 10:58:48 AM haraldk Exp$
|
||||
*/
|
||||
public class ByteArrayImageInputStreamTest {
|
||||
private final Random random = new Random(1709843507234566L);
|
||||
|
||||
+84
@@ -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> {
|
||||
protected final ImageInputStreamSpi provider = createProvider();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected final Class<T> inputClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
|
||||
|
||||
protected abstract ImageInputStreamSpi createProvider();
|
||||
|
||||
protected abstract T createInput() throws IOException;
|
||||
|
||||
@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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
+23
@@ -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());
|
||||
}
|
||||
}
|
||||
+16
@@ -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");
|
||||
}
|
||||
}
|
||||
+234
-188
@@ -31,6 +31,7 @@
|
||||
package com.twelvemonkeys.imageio.util;
|
||||
|
||||
import com.twelvemonkeys.imageio.stream.URLImageInputStreamSpi;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.mockito.InOrder;
|
||||
@@ -49,6 +50,7 @@ import java.awt.image.RenderedImage;
|
||||
import java.awt.image.SampleModel;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -69,41 +71,34 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
// TODO: Should we really test if the provider is installed?
|
||||
// - Pro: Tests the META-INF/services config
|
||||
// - Con: Not all providers should be installed at runtime...
|
||||
// TODO: Create own subclass for testing the Spis?
|
||||
|
||||
static {
|
||||
IIORegistry.getDefaultInstance().registerServiceProvider(new URLImageInputStreamSpi());
|
||||
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 Class<T> getReaderClass();
|
||||
|
||||
protected T createReader() {
|
||||
try {
|
||||
return getReaderClass().newInstance();
|
||||
}
|
||||
catch (InstantiationException | IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
protected final T createReader() throws IOException {
|
||||
return readerClass.cast(provider.createReaderInstance(null));
|
||||
}
|
||||
|
||||
protected abstract List<TestData> getTestData();
|
||||
|
||||
protected abstract List<String> getFormatNames();
|
||||
|
||||
protected abstract List<String> getSuffixes();
|
||||
|
||||
protected abstract List<String> getMIMETypes();
|
||||
|
||||
protected boolean allowsNullRawImageType() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static void failBecause(String message, Throwable exception) {
|
||||
AssertionError error = new AssertionError(message);
|
||||
error.initCause(exception);
|
||||
throw error;
|
||||
throw new AssertionError(message, exception);
|
||||
}
|
||||
|
||||
protected void assertProviderInstalledForName(final String pFormat, final Class<? extends ImageReader> pReaderClass) {
|
||||
@@ -123,17 +118,20 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
boolean found = false;
|
||||
while (pReaders.hasNext()) {
|
||||
ImageReader reader = pReaders.next();
|
||||
if (reader.getClass() == pReaderClass) {
|
||||
if (reader.getClass() == pReaderClass && isOurProvider(reader.getOriginatingProvider())) {
|
||||
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
|
||||
public void testProviderInstalledForNames() {
|
||||
Class<? extends ImageReader> readerClass = getReaderClass();
|
||||
for (String name : getFormatNames()) {
|
||||
assertProviderInstalledForName(name, readerClass);
|
||||
}
|
||||
@@ -141,7 +139,6 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
|
||||
@Test
|
||||
public void testProviderInstalledForSuffixes() {
|
||||
Class<? extends ImageReader> readerClass = getReaderClass();
|
||||
for (String suffix : getSuffixes()) {
|
||||
assertProviderInstalledForSuffix(suffix, readerClass);
|
||||
}
|
||||
@@ -149,7 +146,6 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
|
||||
@Test
|
||||
public void testProviderInstalledForMIMETypes() {
|
||||
Class<? extends ImageReader> readerClass = getReaderClass();
|
||||
for (String type : getMIMETypes()) {
|
||||
assertProviderInstalledForMIMEType(type, readerClass);
|
||||
}
|
||||
@@ -159,7 +155,6 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
public void testProviderCanRead() throws IOException {
|
||||
List<TestData> testData = getTestData();
|
||||
|
||||
ImageReaderSpi provider = createProvider();
|
||||
for (TestData data : testData) {
|
||||
ImageInputStream stream = data.getInputStream();
|
||||
assertNotNull(stream);
|
||||
@@ -172,7 +167,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
boolean canRead = false;
|
||||
|
||||
try {
|
||||
canRead = createProvider().canDecodeInput(null);
|
||||
canRead = provider.canDecodeInput(null);
|
||||
}
|
||||
catch (IllegalArgumentException ignore) {
|
||||
}
|
||||
@@ -187,7 +182,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetInput() {
|
||||
public void testSetInput() throws IOException {
|
||||
// Should just pass with no exceptions
|
||||
ImageReader reader = createReader();
|
||||
assertNotNull(reader);
|
||||
@@ -200,7 +195,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetInputNull() {
|
||||
public void testSetInputNull() throws IOException {
|
||||
// Should just pass with no exceptions
|
||||
ImageReader reader = createReader();
|
||||
assertNotNull(reader);
|
||||
@@ -209,7 +204,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRead() {
|
||||
public void testRead() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
|
||||
for (TestData data : getTestData()) {
|
||||
@@ -222,6 +217,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
image = reader.read(i);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
failBecause(String.format("Image %s index %s could not be read: %s", data.getInput(), i, e), e);
|
||||
}
|
||||
|
||||
@@ -243,7 +239,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadIndexNegative() {
|
||||
public void testReadIndexNegative() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
TestData data = getTestData().get(0);
|
||||
reader.setInput(data.getInputStream());
|
||||
@@ -264,7 +260,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadIndexOutOfBounds() {
|
||||
public void testReadIndexOutOfBounds() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
TestData data = getTestData().get(0);
|
||||
reader.setInput(data.getInputStream());
|
||||
@@ -284,26 +280,6 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
reader.dispose();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadNoInput() {
|
||||
ImageReader reader = createReader();
|
||||
// Do not set input
|
||||
|
||||
BufferedImage image = null;
|
||||
try {
|
||||
image = reader.read(0);
|
||||
fail("Read image with no input");
|
||||
}
|
||||
catch (IllegalStateException ignore) {
|
||||
}
|
||||
catch (IOException e) {
|
||||
failBecause("Image could not be read", e);
|
||||
}
|
||||
assertNull(image);
|
||||
|
||||
reader.dispose();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReRead() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
@@ -324,73 +300,75 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
reader.dispose();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadIndexNegativeWithParam() {
|
||||
ImageReader reader = createReader();
|
||||
TestData data = getTestData().get(0);
|
||||
reader.setInput(data.getInputStream());
|
||||
|
||||
BufferedImage image = null;
|
||||
try {
|
||||
image = reader.read(-1, reader.getDefaultReadParam());
|
||||
fail("Read image with illegal index");
|
||||
}
|
||||
catch (IndexOutOfBoundsException ignore) {
|
||||
}
|
||||
catch (IOException e) {
|
||||
failBecause("Image could not be read", e);
|
||||
}
|
||||
|
||||
assertNull(image);
|
||||
|
||||
reader.dispose();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadIndexOutOfBoundsWithParam() {
|
||||
ImageReader reader = createReader();
|
||||
TestData data = getTestData().get(0);
|
||||
reader.setInput(data.getInputStream());
|
||||
|
||||
BufferedImage image = null;
|
||||
try {
|
||||
image = reader.read(Short.MAX_VALUE, reader.getDefaultReadParam());
|
||||
fail("Read image with index out of bounds");
|
||||
}
|
||||
catch (IndexOutOfBoundsException ignore) {
|
||||
}
|
||||
catch (IOException e) {
|
||||
failBecause("Image could not be read", e);
|
||||
}
|
||||
|
||||
assertNull(image);
|
||||
|
||||
reader.dispose();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadNoInputWithParam() {
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void testReadNoInput() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
// Do not set input
|
||||
|
||||
BufferedImage image = null;
|
||||
try {
|
||||
image = reader.read(0, reader.getDefaultReadParam());
|
||||
reader.read(0);
|
||||
fail("Read image with no input");
|
||||
}
|
||||
catch (IllegalStateException ignore) {
|
||||
}
|
||||
catch (IOException e) {
|
||||
failBecause("Image could not be read", e);
|
||||
}
|
||||
}
|
||||
|
||||
assertNull(image);
|
||||
@Test(expected = IndexOutOfBoundsException.class)
|
||||
public void testReadIndexNegativeWithParam() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
TestData data = getTestData().get(0);
|
||||
reader.setInput(data.getInputStream());
|
||||
|
||||
reader.dispose();
|
||||
try {
|
||||
reader.read(-1, reader.getDefaultReadParam());
|
||||
fail("Read image with illegal index");
|
||||
}
|
||||
catch (IOException e) {
|
||||
failBecause("Image could not be read", e);
|
||||
}
|
||||
finally {
|
||||
reader.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = IndexOutOfBoundsException.class)
|
||||
public void testReadIndexOutOfBoundsWithParam() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
TestData data = getTestData().get(0);
|
||||
reader.setInput(data.getInputStream());
|
||||
|
||||
try {
|
||||
reader.read(Short.MAX_VALUE, reader.getDefaultReadParam());
|
||||
fail("Read image with index out of bounds");
|
||||
}
|
||||
catch (IOException e) {
|
||||
failBecause("Image could not be read", e);
|
||||
}
|
||||
finally {
|
||||
reader.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void testReadNoInputWithParam() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
// Do not set input
|
||||
|
||||
try {
|
||||
reader.read(0, reader.getDefaultReadParam());
|
||||
fail("Read image with no input");
|
||||
}
|
||||
catch (IOException e) {
|
||||
failBecause("Image could not be read", e);
|
||||
}
|
||||
finally {
|
||||
reader.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadWithNewParam() {
|
||||
public void testReadWithNewParam() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
TestData data = getTestData().get(0);
|
||||
reader.setInput(data.getInputStream());
|
||||
@@ -411,7 +389,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadWithDefaultParam() {
|
||||
public void testReadWithDefaultParam() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
TestData data = getTestData().get(0);
|
||||
reader.setInput(data.getInputStream());
|
||||
@@ -432,7 +410,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadWithNullParam() {
|
||||
public void testReadWithNullParam() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
TestData data = getTestData().get(0);
|
||||
reader.setInput(data.getInputStream());
|
||||
@@ -453,7 +431,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadWithSizeParam() {
|
||||
public void testReadWithSizeParam() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
TestData data = getTestData().get(0);
|
||||
reader.setInput(data.getInputStream());
|
||||
@@ -479,7 +457,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadWithSubsampleParamDimensions() {
|
||||
public void testReadWithSubsampleParamDimensions() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
TestData data = getTestData().get(0);
|
||||
reader.setInput(data.getInputStream());
|
||||
@@ -530,6 +508,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
// TODO: Subsample all test data
|
||||
// TODO: Subsample with varying ratios and offsets
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
protected final void assertSubsampledImageDataEquals(String message, BufferedImage expected, BufferedImage actual, ImageReadParam param) throws IOException {
|
||||
assertNotNull("Expected image was null", expected);
|
||||
assertNotNull("Actual image was null!", actual);
|
||||
@@ -553,10 +532,10 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
int actualRGB = actual.getRGB(x, y);
|
||||
|
||||
try {
|
||||
assertEquals(String.format("%s alpha at (%d, %d)", message, x, y), (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(String.format("%s green at (%d, %d)", message, x, y), (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 >>> 24) & 0xff, (actualRGB >>> 24) & 0xff, 5);
|
||||
assertEquals((expectedRGB >> 16) & 0xff, (actualRGB >> 16) & 0xff, 5);
|
||||
assertEquals((expectedRGB >> 8) & 0xff, (actualRGB >> 8) & 0xff, 5);
|
||||
assertEquals(expectedRGB & 0xff, actualRGB & 0xff, 5);
|
||||
}
|
||||
catch (AssertionError e) {
|
||||
File tempExpected = File.createTempFile("junit-expected-", ".png");
|
||||
@@ -566,7 +545,6 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
System.err.println("tempActual.getAbsolutePath(): " + tempActual.getAbsolutePath());
|
||||
ImageIO.write(actual, "PNG", tempActual);
|
||||
|
||||
|
||||
assertEquals(String.format("%s ARGB at (%d, %d)", message, x, y), String.format("#%08x", expectedRGB), String.format("#%08x", actualRGB));
|
||||
}
|
||||
}
|
||||
@@ -595,7 +573,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadWithSourceRegionParam() {
|
||||
public void testReadWithSourceRegionParam() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
TestData data = getTestData().get(0);
|
||||
reader.setInput(data.getInputStream());
|
||||
@@ -626,40 +604,53 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
|
||||
protected void assertReadWithSourceRegionParamEqualImage(final Rectangle r, final TestData data, final int imageIndex) throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
reader.setInput(data.getInputStream());
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
try (ImageInputStream inputStream = data.getInputStream()) {
|
||||
reader.setInput(inputStream);
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
|
||||
// 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);
|
||||
// Read full image and get sub image for comparison
|
||||
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 {
|
||||
// SwingUtilities.invokeAndWait(new Runnable() {
|
||||
// public void run() {
|
||||
// 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);
|
||||
assertEquals("Read image has wrong width: " + image.getWidth(), r.width, image.getWidth());
|
||||
assertEquals("Read image has wrong height: " + image.getHeight(), r.height, image.getHeight());
|
||||
|
||||
assertNotNull("Image was null!", image);
|
||||
assertEquals("Read image has wrong width: " + image.getWidth(), r.width, image.getWidth());
|
||||
assertEquals("Read image has wrong height: " + image.getHeight(), r.height, image.getHeight());
|
||||
assertImageDataEquals("Images differ", roi, image);
|
||||
try {
|
||||
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
|
||||
public void testReadWithSizeAndSourceRegionParam() {
|
||||
public void testReadWithSizeAndSourceRegionParam() throws IOException {
|
||||
// TODO: Is this test correct???
|
||||
ImageReader reader = createReader();
|
||||
TestData data = getTestData().get(0);
|
||||
@@ -690,7 +681,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadWithSubsampleAndSourceRegionParam() {
|
||||
public void testReadWithSubsampleAndSourceRegionParam() throws IOException {
|
||||
// NOTE: The "standard" (com.sun.imageio.plugin.*) ImageReaders pass
|
||||
// this test, so the test should be correct...
|
||||
ImageReader reader = createReader();
|
||||
@@ -716,7 +707,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadAsRenderedImageIndexNegative() {
|
||||
public void testReadAsRenderedImageIndexNegative() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
TestData data = getTestData().get(0);
|
||||
reader.setInput(data.getInputStream());
|
||||
@@ -739,7 +730,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadAsRenderedImageIndexOutOfBounds() throws IIOException {
|
||||
public void testReadAsRenderedImageIndexOutOfBounds() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
TestData data = getTestData().get(0);
|
||||
reader.setInput(data.getInputStream());
|
||||
@@ -766,7 +757,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadAsRenderedImageNoInput() {
|
||||
public void testReadAsRenderedImageNoInput() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
// Do not set input
|
||||
|
||||
@@ -788,7 +779,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadAsRenderedImage() {
|
||||
public void testReadAsRenderedImage() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
TestData data = getTestData().get(0);
|
||||
reader.setInput(data.getInputStream());
|
||||
@@ -811,7 +802,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadAsRenderedImageWithDefaultParam() {
|
||||
public void testReadAsRenderedImageWithDefaultParam() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
TestData data = getTestData().get(0);
|
||||
reader.setInput(data.getInputStream());
|
||||
@@ -834,7 +825,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDefaultReadParam() {
|
||||
public void testGetDefaultReadParam() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
assertNotNull(param);
|
||||
@@ -842,7 +833,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetFormatName() {
|
||||
public void testGetFormatName() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
TestData data = getTestData().get(0);
|
||||
reader.setInput(data.getInputStream());
|
||||
@@ -858,7 +849,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMinIndex() {
|
||||
public void testGetMinIndex() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
TestData data = getTestData().get(0);
|
||||
reader.setInput(data.getInputStream());
|
||||
@@ -874,7 +865,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMinIndexNoInput() {
|
||||
public void testGetMinIndexNoInput() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
int num = 0;
|
||||
|
||||
@@ -888,7 +879,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetNumImages() {
|
||||
public void testGetNumImages() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
TestData data = getTestData().get(0);
|
||||
reader.setInput(data.getInputStream());
|
||||
@@ -917,7 +908,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetNumImagesNoInput() {
|
||||
public void testGetNumImagesNoInput() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
int num = -1;
|
||||
|
||||
@@ -945,7 +936,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetWidth() {
|
||||
public void testGetWidth() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
TestData data = getTestData().get(0);
|
||||
reader.setInput(data.getInputStream());
|
||||
@@ -962,7 +953,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetWidthIndexOutOfBounds() {
|
||||
public void testGetWidthIndexOutOfBounds() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
TestData data = getTestData().get(0);
|
||||
reader.setInput(data.getInputStream());
|
||||
@@ -983,13 +974,13 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetWidthNoInput() {
|
||||
public void testGetWidthNoInput() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
|
||||
int width = 0;
|
||||
try {
|
||||
width = reader.getWidth(0);
|
||||
fail("Width read without imput");
|
||||
fail("Width read without input");
|
||||
}
|
||||
catch (IllegalStateException ignore) {
|
||||
}
|
||||
@@ -1001,7 +992,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetHeight() {
|
||||
public void testGetHeight() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
TestData data = getTestData().get(0);
|
||||
reader.setInput(data.getInputStream());
|
||||
@@ -1018,13 +1009,13 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetHeightNoInput() {
|
||||
public void testGetHeightNoInput() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
|
||||
int height = 0;
|
||||
try {
|
||||
height = reader.getHeight(0);
|
||||
fail("height read without imput");
|
||||
fail("height read without input");
|
||||
}
|
||||
catch (IllegalStateException ignore) {
|
||||
}
|
||||
@@ -1036,7 +1027,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetHeightIndexOutOfBounds() {
|
||||
public void testGetHeightIndexOutOfBounds() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
TestData data = getTestData().get(0);
|
||||
reader.setInput(data.getInputStream());
|
||||
@@ -1057,7 +1048,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAspectRatio() {
|
||||
public void testGetAspectRatio() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
TestData data = getTestData().get(0);
|
||||
reader.setInput(data.getInputStream());
|
||||
@@ -1075,7 +1066,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAspectRatioNoInput() {
|
||||
public void testGetAspectRatioNoInput() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
|
||||
float aspectRatio = 0f;
|
||||
@@ -1093,7 +1084,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAspectRatioIndexOutOfBounds() {
|
||||
public void testGetAspectRatioIndexOutOfBounds() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
TestData data = getTestData().get(0);
|
||||
reader.setInput(data.getInputStream());
|
||||
@@ -1114,13 +1105,13 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDisposeBeforeRead() {
|
||||
public void testDisposeBeforeRead() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
reader.dispose(); // Just pass with no exceptions
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDisposeAfterRead() {
|
||||
public void testDisposeAfterRead() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
TestData data = getTestData().get(0);
|
||||
reader.setInput(data.getInputStream());
|
||||
@@ -1128,21 +1119,21 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddIIOReadProgressListener() {
|
||||
public void testAddIIOReadProgressListener() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
reader.addIIOReadProgressListener(mock(IIOReadProgressListener.class));
|
||||
reader.dispose();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddIIOReadProgressListenerNull() {
|
||||
public void testAddIIOReadProgressListenerNull() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
reader.addIIOReadProgressListener(null);
|
||||
reader.dispose();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddIIOReadProgressListenerCallbacks() {
|
||||
public void testAddIIOReadProgressListenerCallbacks() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
TestData data = getTestData().get(0);
|
||||
reader.setInput(data.getInputStream());
|
||||
@@ -1166,7 +1157,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleAddIIOReadProgressListenerCallbacks() {
|
||||
public void testMultipleAddIIOReadProgressListenerCallbacks() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
TestData data = getTestData().get(0);
|
||||
reader.setInput(data.getInputStream());
|
||||
@@ -1204,21 +1195,21 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveIIOReadProgressListenerNull() {
|
||||
public void testRemoveIIOReadProgressListenerNull() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
reader.removeIIOReadProgressListener(null);
|
||||
reader.dispose();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveIIOReadProgressListenerNone() {
|
||||
public void testRemoveIIOReadProgressListenerNone() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
reader.removeIIOReadProgressListener(mock(IIOReadProgressListener.class));
|
||||
reader.dispose();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveIIOReadProgressListener() {
|
||||
public void testRemoveIIOReadProgressListener() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
TestData data = getTestData().get(0);
|
||||
reader.setInput(data.getInputStream());
|
||||
@@ -1240,7 +1231,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveIIOReadProgressListenerMultiple() {
|
||||
public void testRemoveIIOReadProgressListenerMultiple() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
TestData data = getTestData().get(0);
|
||||
reader.setInput(data.getInputStream());
|
||||
@@ -1272,7 +1263,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveAllIIOReadProgressListeners() {
|
||||
public void testRemoveAllIIOReadProgressListeners() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
TestData data = getTestData().get(0);
|
||||
reader.setInput(data.getInputStream());
|
||||
@@ -1295,7 +1286,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveAllIIOReadProgressListenersMultiple() {
|
||||
public void testRemoveAllIIOReadProgressListenersMultiple() throws IOException {
|
||||
ImageReader reader = createReader();
|
||||
TestData data = getTestData().get(0);
|
||||
reader.setInput(data.getInputStream());
|
||||
@@ -1322,7 +1313,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAbort() {
|
||||
public void testAbort() throws IOException {
|
||||
final ImageReader reader = createReader();
|
||||
TestData data = getTestData().get(0);
|
||||
reader.setInput(data.getInputStream());
|
||||
@@ -1336,7 +1327,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
// Create a listener that just makes the reader abort immediately...
|
||||
IIOReadProgressListener abortingListener = mock(IIOReadProgressListener.class, "Aborter");
|
||||
Answer<Void> abort = new Answer<Void>() {
|
||||
public Void answer(InvocationOnMock invocation) throws Throwable {
|
||||
public Void answer(InvocationOnMock invocation) {
|
||||
reader.abort();
|
||||
return null;
|
||||
}
|
||||
@@ -1365,9 +1356,6 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
reader.setInput(data.getInputStream());
|
||||
|
||||
ImageTypeSpecifier rawType = reader.getRawImageType(0);
|
||||
if (rawType == null && allowsNullRawImageType()) {
|
||||
continue;
|
||||
}
|
||||
assertNotNull(rawType);
|
||||
|
||||
Iterator<ImageTypeSpecifier> types = reader.getImageTypes(0);
|
||||
@@ -1387,8 +1375,9 @@ 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();
|
||||
}
|
||||
|
||||
@@ -1569,7 +1558,6 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
reader.dispose();
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Test
|
||||
public void testSetDestinationOffsetNull() throws IOException {
|
||||
final ImageReader reader = createReader();
|
||||
@@ -1620,7 +1608,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
assertEquals(expectedModel.getDataType(), resultModel.getDataType());
|
||||
assertEquals(expectedModel.getNumBands(), resultModel.getNumBands());
|
||||
assertEquals(expectedModel.getNumDataElements(), resultModel.getNumDataElements());
|
||||
assertTrue(Arrays.equals(expectedModel.getSampleSize(), resultModel.getSampleSize()));
|
||||
assertArrayEquals(expectedModel.getSampleSize(), resultModel.getSampleSize());
|
||||
assertEquals(expectedModel.getTransferType(), resultModel.getTransferType());
|
||||
for (int i = 0; i < expectedModel.getNumBands(); i++) {
|
||||
assertEquals(expectedModel.getSampleSize(i), resultModel.getSampleSize(i));
|
||||
@@ -1638,12 +1626,72 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
BufferedImage one = reader.read(0);
|
||||
BufferedImage two = reader.read(0);
|
||||
|
||||
// Test for same BufferedImage instance
|
||||
assertNotSame("Multiple reads return same (mutable) image", one, two);
|
||||
|
||||
one.setRGB(0, 0, Color.BLUE.getRGB());
|
||||
two.setRGB(0, 0, Color.RED.getRGB());
|
||||
|
||||
// Test for same backing storage (array)
|
||||
one.setRGB(0, 0, Color.BLACK.getRGB());
|
||||
two.setRGB(0, 0, Color.WHITE.getRGB());
|
||||
assertTrue(one.getRGB(0, 0) != two.getRGB(0, 0));
|
||||
|
||||
reader.dispose();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadThumbnails() throws IOException {
|
||||
T reader = createReader();
|
||||
|
||||
if (reader.readerSupportsThumbnails()) {
|
||||
for (TestData testData : getTestData()) {
|
||||
try (ImageInputStream inputStream = testData.getInputStream()) {
|
||||
reader.setInput(inputStream);
|
||||
|
||||
int numImages = reader.getNumImages(true);
|
||||
|
||||
for (int i = 0; i < numImages; i++) {
|
||||
int numThumbnails = reader.getNumThumbnails(0);
|
||||
|
||||
for (int t = 0; t < numThumbnails; t++) {
|
||||
BufferedImage thumbnail = reader.readThumbnail(0, t);
|
||||
|
||||
assertNotNull(thumbnail);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reader.dispose();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThumbnailProgress() throws IOException {
|
||||
T reader = createReader();
|
||||
|
||||
IIOReadProgressListener listener = mock(IIOReadProgressListener.class);
|
||||
reader.addIIOReadProgressListener(listener);
|
||||
|
||||
if (reader.readerSupportsThumbnails()) {
|
||||
for (TestData testData : getTestData()) {
|
||||
try (ImageInputStream inputStream = testData.getInputStream()) {
|
||||
|
||||
reader.setInput(inputStream);
|
||||
|
||||
int numThumbnails = reader.getNumThumbnails(0);
|
||||
for (int i = 0; i < numThumbnails; i++) {
|
||||
reset(listener);
|
||||
|
||||
reader.readThumbnail(0, i);
|
||||
|
||||
InOrder order = inOrder(listener);
|
||||
order.verify(listener).thumbnailStarted(reader, 0, i);
|
||||
order.verify(listener, atLeastOnce()).thumbnailProgress(reader, 100f);
|
||||
order.verify(listener).thumbnailComplete(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reader.dispose();
|
||||
}
|
||||
|
||||
@@ -1692,20 +1740,18 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
|
||||
@Ignore("TODO: Implement")
|
||||
@Test
|
||||
public void testSetDestinationBands() throws IOException {
|
||||
public void testSetDestinationBands() {
|
||||
throw new UnsupportedOperationException("Method testSetDestinationBands not implemented"); // TODO: Implement
|
||||
}
|
||||
|
||||
@Ignore("TODO: Implement")
|
||||
@Test
|
||||
public void testSetSourceBands() throws IOException {
|
||||
public void testSetSourceBands() {
|
||||
throw new UnsupportedOperationException("Method testSetDestinationBands not implemented"); // TODO: Implement
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProviderAndMetadataFormatNamesMatch() throws IOException {
|
||||
ImageReaderSpi provider = createProvider();
|
||||
|
||||
ImageReader reader = createReader();
|
||||
reader.setInput(getTestData().get(0).getInputStream());
|
||||
|
||||
@@ -1826,7 +1872,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + ": " + String.valueOf(input);
|
||||
return String.format("%s: %s", getClass().getSimpleName(), input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+20
-15
@@ -30,14 +30,20 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.util;
|
||||
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ColorModel;
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.awt.image.DirectColorModel;
|
||||
import java.awt.image.IndexColorModel;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.*;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
|
||||
public class ImageTypeSpecifiersTest {
|
||||
|
||||
@@ -541,8 +547,7 @@ public class ImageTypeSpecifiersTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatePackedGrayscale1() {
|
||||
// TODO: Fails on Java 11, because IndexColorModel now has an overloaded equals that actually tests the color entries
|
||||
public void testCreatePackedGrayscale1BPP() {
|
||||
assertEquals(
|
||||
ImageTypeSpecifier.createGrayscale(1, DataBuffer.TYPE_BYTE, false),
|
||||
ImageTypeSpecifiers.createPackedGrayscale(GRAY, 1, DataBuffer.TYPE_BYTE)
|
||||
@@ -550,8 +555,8 @@ public class ImageTypeSpecifiersTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatePackedGrayscale2() {
|
||||
// TODO: Fails on Java 11, because IndexColorModel now has an overloaded equals that actually tests the color entries
|
||||
public void testCreatePackedGrayscale2BPP() {
|
||||
// TODO: Fails on Java 11+, because IndexColorModel now has an overloaded equals that actually tests the color entries
|
||||
assertEquals(
|
||||
ImageTypeSpecifier.createGrayscale(2, DataBuffer.TYPE_BYTE, false),
|
||||
ImageTypeSpecifiers.createPackedGrayscale(GRAY, 2, DataBuffer.TYPE_BYTE)
|
||||
@@ -559,8 +564,8 @@ public class ImageTypeSpecifiersTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatePackedGrayscale4() throws Exception {
|
||||
// TODO: Fails on Java 11, because IndexColorModel now has an overloaded equals that actually tests the color entries
|
||||
public void testCreatePackedGrayscale4BPP() {
|
||||
// TODO: Fails on Java 11+, because IndexColorModel now has an overloaded equals that actually tests the color entries
|
||||
assertEquals(
|
||||
ImageTypeSpecifier.createGrayscale(4, DataBuffer.TYPE_BYTE, false),
|
||||
ImageTypeSpecifiers.createPackedGrayscale(GRAY, 4, DataBuffer.TYPE_BYTE)
|
||||
@@ -653,7 +658,7 @@ public class ImageTypeSpecifiersTest {
|
||||
for (int bits = 1; bits <= 8; bits <<= 1) {
|
||||
int[] colors = createIntLut(1 << bits);
|
||||
assertEquals(
|
||||
IndexedImageTypeSpecifier.createFromIndexColorModel(new IndexColorModel(bits, colors.length, colors, 0, false, -1, DataBuffer.TYPE_BYTE)),
|
||||
new IndexedImageTypeSpecifier(new IndexColorModel(bits, colors.length, colors, 0, false, -1, DataBuffer.TYPE_BYTE)),
|
||||
ImageTypeSpecifiers.createIndexed(colors, false, -1, bits, DataBuffer.TYPE_BYTE)
|
||||
);
|
||||
}
|
||||
@@ -663,7 +668,7 @@ public class ImageTypeSpecifiersTest {
|
||||
public void testCreateIndexedIntArray16() {
|
||||
int[] colors = createIntLut(1 << 16);
|
||||
assertEquals(
|
||||
IndexedImageTypeSpecifier.createFromIndexColorModel(new IndexColorModel(16, colors.length, colors, 0, false, -1, DataBuffer.TYPE_USHORT)),
|
||||
new IndexedImageTypeSpecifier(new IndexColorModel(16, colors.length, colors, 0, false, -1, DataBuffer.TYPE_USHORT)),
|
||||
ImageTypeSpecifiers.createIndexed(colors, false, -1, 16, DataBuffer.TYPE_USHORT)
|
||||
);
|
||||
|
||||
@@ -675,7 +680,7 @@ public class ImageTypeSpecifiersTest {
|
||||
int[] colors = createIntLut(1 << bits);
|
||||
IndexColorModel colorModel = new IndexColorModel(bits, colors.length, colors, 0, false, -1, DataBuffer.TYPE_BYTE);
|
||||
assertEquals(
|
||||
IndexedImageTypeSpecifier.createFromIndexColorModel(colorModel),
|
||||
new IndexedImageTypeSpecifier(colorModel),
|
||||
ImageTypeSpecifiers.createFromIndexColorModel(colorModel)
|
||||
);
|
||||
}
|
||||
@@ -686,7 +691,7 @@ public class ImageTypeSpecifiersTest {
|
||||
int[] colors = createIntLut(1 << 16);
|
||||
IndexColorModel colorModel = new IndexColorModel(16, colors.length, colors, 0, false, -1, DataBuffer.TYPE_USHORT);
|
||||
assertEquals(
|
||||
IndexedImageTypeSpecifier.createFromIndexColorModel(colorModel),
|
||||
new IndexedImageTypeSpecifier(colorModel),
|
||||
ImageTypeSpecifiers.createFromIndexColorModel(colorModel)
|
||||
);
|
||||
}
|
||||
|
||||
+37
-26
@@ -31,6 +31,7 @@
|
||||
package com.twelvemonkeys.imageio.util;
|
||||
|
||||
import com.twelvemonkeys.imageio.stream.URLImageInputStreamSpi;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.mockito.InOrder;
|
||||
|
||||
@@ -39,12 +40,14 @@ import javax.imageio.ImageWriteParam;
|
||||
import javax.imageio.ImageWriter;
|
||||
import javax.imageio.event.IIOWriteProgressListener;
|
||||
import javax.imageio.spi.IIORegistry;
|
||||
import javax.imageio.spi.ImageWriterSpi;
|
||||
import javax.imageio.stream.ImageOutputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.RenderedImage;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
|
||||
@@ -59,7 +62,7 @@ import static org.mockito.Mockito.*;
|
||||
* @author last modified by $Author: haku $
|
||||
* @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.
|
||||
|
||||
@@ -68,7 +71,16 @@ public abstract class ImageWriterAbstractTest {
|
||||
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();
|
||||
|
||||
@@ -104,22 +116,22 @@ public abstract class ImageWriterAbstractTest {
|
||||
@Test
|
||||
public void testSetOutput() throws IOException {
|
||||
// Should just pass with no exceptions
|
||||
ImageWriter writer = createImageWriter();
|
||||
ImageWriter writer = createWriter();
|
||||
assertNotNull(writer);
|
||||
writer.setOutput(ImageIO.createImageOutputStream(new ByteArrayOutputStream()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetOutputNull() {
|
||||
public void testSetOutputNull() throws IOException {
|
||||
// Should just pass with no exceptions
|
||||
ImageWriter writer = createImageWriter();
|
||||
ImageWriter writer = createWriter();
|
||||
assertNotNull(writer);
|
||||
writer.setOutput(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrite() throws IOException {
|
||||
ImageWriter writer = createImageWriter();
|
||||
ImageWriter writer = createWriter();
|
||||
|
||||
for (RenderedImage testData : getTestData()) {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
@@ -136,10 +148,9 @@ public abstract class ImageWriterAbstractTest {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Test
|
||||
public void testWriteNull() throws IOException {
|
||||
ImageWriter writer = createImageWriter();
|
||||
ImageWriter writer = createWriter();
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
writer.setOutput(ImageIO.createImageOutputStream(buffer));
|
||||
|
||||
@@ -156,8 +167,8 @@ public abstract class ImageWriterAbstractTest {
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void testWriteNoOutput() {
|
||||
ImageWriter writer = createImageWriter();
|
||||
public void testWriteNoOutput() throws IOException {
|
||||
ImageWriter writer = createWriter();
|
||||
|
||||
try {
|
||||
writer.write(getTestData(0));
|
||||
@@ -168,8 +179,8 @@ public abstract class ImageWriterAbstractTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDefaultWriteParam() {
|
||||
ImageWriter writer = createImageWriter();
|
||||
public void testGetDefaultWriteParam() throws IOException {
|
||||
ImageWriter writer = createWriter();
|
||||
ImageWriteParam param = writer.getDefaultWriteParam();
|
||||
assertNotNull("Default ImageWriteParam is null", param);
|
||||
}
|
||||
@@ -178,20 +189,20 @@ public abstract class ImageWriterAbstractTest {
|
||||
// TODO: Source region and subsampling at least
|
||||
|
||||
@Test
|
||||
public void testAddIIOWriteProgressListener() {
|
||||
ImageWriter writer = createImageWriter();
|
||||
public void testAddIIOWriteProgressListener() throws IOException {
|
||||
ImageWriter writer = createWriter();
|
||||
writer.addIIOWriteProgressListener(mock(IIOWriteProgressListener.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddIIOWriteProgressListenerNull() {
|
||||
ImageWriter writer = createImageWriter();
|
||||
public void testAddIIOWriteProgressListenerNull() throws IOException {
|
||||
ImageWriter writer = createWriter();
|
||||
writer.addIIOWriteProgressListener(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddIIOWriteProgressListenerCallbacks() throws IOException {
|
||||
ImageWriter writer = createImageWriter();
|
||||
ImageWriter writer = createWriter();
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
writer.setOutput(ImageIO.createImageOutputStream(buffer));
|
||||
|
||||
@@ -214,7 +225,7 @@ public abstract class ImageWriterAbstractTest {
|
||||
|
||||
@Test
|
||||
public void testMultipleAddIIOWriteProgressListenerCallbacks() throws IOException {
|
||||
ImageWriter writer = createImageWriter();
|
||||
ImageWriter writer = createWriter();
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
writer.setOutput(ImageIO.createImageOutputStream(buffer));
|
||||
|
||||
@@ -250,20 +261,20 @@ public abstract class ImageWriterAbstractTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveIIOWriteProgressListenerNull() {
|
||||
ImageWriter writer = createImageWriter();
|
||||
public void testRemoveIIOWriteProgressListenerNull() throws IOException {
|
||||
ImageWriter writer = createWriter();
|
||||
writer.removeIIOWriteProgressListener(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveIIOWriteProgressListenerNone() {
|
||||
ImageWriter writer = createImageWriter();
|
||||
public void testRemoveIIOWriteProgressListenerNone() throws IOException {
|
||||
ImageWriter writer = createWriter();
|
||||
writer.removeIIOWriteProgressListener(mock(IIOWriteProgressListener.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveIIOWriteProgressListener() throws IOException {
|
||||
ImageWriter writer = createImageWriter();
|
||||
ImageWriter writer = createWriter();
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
writer.setOutput(ImageIO.createImageOutputStream(buffer));
|
||||
|
||||
@@ -284,7 +295,7 @@ public abstract class ImageWriterAbstractTest {
|
||||
|
||||
@Test
|
||||
public void testRemoveIIOWriteProgressListenerMultiple() throws IOException {
|
||||
ImageWriter writer = createImageWriter();
|
||||
ImageWriter writer = createWriter();
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
writer.setOutput(ImageIO.createImageOutputStream(buffer));
|
||||
|
||||
@@ -316,7 +327,7 @@ public abstract class ImageWriterAbstractTest {
|
||||
|
||||
@Test
|
||||
public void testRemoveAllIIOWriteProgressListeners() throws IOException {
|
||||
ImageWriter writer = createImageWriter();
|
||||
ImageWriter writer = createWriter();
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
writer.setOutput(ImageIO.createImageOutputStream(buffer));
|
||||
|
||||
@@ -339,7 +350,7 @@ public abstract class ImageWriterAbstractTest {
|
||||
|
||||
@Test
|
||||
public void testRemoveAllIIOWriteProgressListenersMultiple() throws IOException {
|
||||
ImageWriter writer = createImageWriter();
|
||||
ImageWriter writer = createWriter();
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
writer.setOutput(ImageIO.createImageOutputStream(buffer));
|
||||
|
||||
|
||||
+17
-17
@@ -30,14 +30,17 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.util;
|
||||
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.awt.image.IndexColorModel;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* IndexedImageTypeSpecifierTestCase
|
||||
@@ -51,46 +54,43 @@ public class IndexedImageTypeSpecifierTest {
|
||||
public void testEquals() {
|
||||
IndexColorModel cm = new IndexColorModel(1, 2, new int[]{0xffffff, 0x00}, 0, false, -1, DataBuffer.TYPE_BYTE);
|
||||
|
||||
ImageTypeSpecifier spec = IndexedImageTypeSpecifier.createFromIndexColorModel(cm);
|
||||
ImageTypeSpecifier other = IndexedImageTypeSpecifier.createFromIndexColorModel(cm);
|
||||
ImageTypeSpecifier different = IndexedImageTypeSpecifier.createFromIndexColorModel(new IndexColorModel(2, 2, new int[]{0xff00ff, 0x00, 0xff00ff, 0x00}, 0, false, -1, DataBuffer.TYPE_BYTE));
|
||||
ImageTypeSpecifier spec = new IndexedImageTypeSpecifier(cm);
|
||||
ImageTypeSpecifier other = new IndexedImageTypeSpecifier(cm);
|
||||
ImageTypeSpecifier different = new IndexedImageTypeSpecifier(new IndexColorModel(2, 2, new int[]{0xff00ff, 0x00, 0xff00ff, 0x00}, 0, false, -1, DataBuffer.TYPE_BYTE));
|
||||
|
||||
assertEquals(spec, other);
|
||||
assertEquals(other, spec);
|
||||
|
||||
assertEquals(spec.hashCode(), other.hashCode());
|
||||
|
||||
assertTrue(spec.equals(other));
|
||||
assertTrue(other.equals(spec));
|
||||
|
||||
// TODO: There is still a problem that IndexColorModel does not override equals,
|
||||
// so any model with the same number of bits, transparency, and transfer type will be treated as equal
|
||||
assertFalse(other.equals(different));
|
||||
assertNotEquals(other, different);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHashCode() {
|
||||
IndexColorModel cm = new IndexColorModel(1, 2, new int[]{0xffffff, 0x00}, 0, false, -1, DataBuffer.TYPE_BYTE);
|
||||
|
||||
ImageTypeSpecifier spec = IndexedImageTypeSpecifier.createFromIndexColorModel(cm);
|
||||
ImageTypeSpecifier other = IndexedImageTypeSpecifier.createFromIndexColorModel(cm);
|
||||
ImageTypeSpecifier different = IndexedImageTypeSpecifier.createFromIndexColorModel(new IndexColorModel(2, 2, new int[]{0xff00ff, 0x00, 0xff00ff, 0x00}, 0, false, -1, DataBuffer.TYPE_BYTE));
|
||||
ImageTypeSpecifier spec = new IndexedImageTypeSpecifier(cm);
|
||||
ImageTypeSpecifier other = new IndexedImageTypeSpecifier(cm);
|
||||
ImageTypeSpecifier different = new IndexedImageTypeSpecifier(new IndexColorModel(2, 2, new int[]{0xff00ff, 0x00, 0xff00ff, 0x00}, 0, false, -1, DataBuffer.TYPE_BYTE));
|
||||
|
||||
// TODO: There is still a problem that IndexColorModel does not override hashCode,
|
||||
// so any model with the same number of bits, transparency, and transfer type will have same hash
|
||||
assertEquals(spec.hashCode(), other.hashCode());
|
||||
assertFalse(spec.hashCode() == different.hashCode());
|
||||
assertNotEquals(spec.hashCode(), different.hashCode());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testCreateNull() {
|
||||
IndexedImageTypeSpecifier.createFromIndexColorModel(null);
|
||||
new IndexedImageTypeSpecifier(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateBufferedImageBinary() {
|
||||
IndexColorModel cm = new IndexColorModel(1, 2, new int[]{0xffffff, 0x00}, 0, false, -1, DataBuffer.TYPE_BYTE);
|
||||
ImageTypeSpecifier spec = IndexedImageTypeSpecifier.createFromIndexColorModel(cm);
|
||||
ImageTypeSpecifier spec = new IndexedImageTypeSpecifier(cm);
|
||||
|
||||
BufferedImage image = spec.createBufferedImage(2, 2);
|
||||
|
||||
@@ -102,7 +102,7 @@ public class IndexedImageTypeSpecifierTest {
|
||||
@Test
|
||||
public void testCreateBufferedImageIndexed() {
|
||||
IndexColorModel cm = new IndexColorModel(8, 256, new int[256], 0, false, -1, DataBuffer.TYPE_BYTE);
|
||||
ImageTypeSpecifier spec = IndexedImageTypeSpecifier.createFromIndexColorModel(cm);
|
||||
ImageTypeSpecifier spec = new IndexedImageTypeSpecifier(cm);
|
||||
|
||||
BufferedImage image = spec.createBufferedImage(2, 2);
|
||||
|
||||
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
package com.twelvemonkeys.imageio.util;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import java.awt.*;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.*;
|
||||
import java.util.Random;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertSame;
|
||||
|
||||
/**
|
||||
* RasterUtilsTest.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: RasterUtilsTest.java,v 1.0 05/05/2021 haraldk Exp$
|
||||
*/
|
||||
public class RasterUtilsTest {
|
||||
@Test(expected = NullPointerException.class)
|
||||
public void testAsByteRasterFromNull() {
|
||||
RasterUtils.asByteRaster((Raster) null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("RedundantCast")
|
||||
@Test(expected = NullPointerException.class)
|
||||
public void testAsByteRasterWritableFromNull() {
|
||||
RasterUtils.asByteRaster((WritableRaster) null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsByteRasterPassThrough() {
|
||||
WritableRaster[] rasters = new WritableRaster[] {
|
||||
new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR).getRaster(),
|
||||
new BufferedImage(1, 1, BufferedImage.TYPE_4BYTE_ABGR).getRaster(),
|
||||
new BufferedImage(1, 1, BufferedImage.TYPE_4BYTE_ABGR_PRE).getRaster(),
|
||||
new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY).getRaster(),
|
||||
Raster.createBandedRaster(DataBuffer.TYPE_BYTE, 1, 1, 7, null),
|
||||
Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, 1, 1, 2, null),
|
||||
new WritableRaster(new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE, 1, 1, 1, 1, new int[1]), new Point(0, 0)) {}
|
||||
};
|
||||
|
||||
for (Raster raster : rasters) {
|
||||
assertSame(raster, RasterUtils.asByteRaster(raster));
|
||||
}
|
||||
|
||||
for (WritableRaster raster : rasters) {
|
||||
assertSame(raster, RasterUtils.asByteRaster(raster));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsByteRasterWritableFromTYPE_INT_RGB() {
|
||||
BufferedImage image = new BufferedImage(9, 11, BufferedImage.TYPE_INT_RGB);
|
||||
|
||||
WritableRaster raster = RasterUtils.asByteRaster(image.getRaster());
|
||||
|
||||
assertEquals(DataBuffer.TYPE_BYTE, raster.getTransferType());
|
||||
assertEquals(PixelInterleavedSampleModel.class, raster.getSampleModel().getClass());
|
||||
assertEquals(image.getWidth(), raster.getWidth());
|
||||
assertEquals(image.getHeight(), raster.getHeight());
|
||||
|
||||
assertEquals(3, raster.getNumBands());
|
||||
assertEquals(3, raster.getNumDataElements());
|
||||
|
||||
assertImageRasterEquals(image, raster);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsByteRasterWritableFromTYPE_INT_ARGB() {
|
||||
BufferedImage image = new BufferedImage(9, 11, BufferedImage.TYPE_INT_ARGB);
|
||||
|
||||
WritableRaster raster = RasterUtils.asByteRaster(image.getRaster());
|
||||
|
||||
assertEquals(DataBuffer.TYPE_BYTE, raster.getTransferType());
|
||||
assertEquals(PixelInterleavedSampleModel.class, raster.getSampleModel().getClass());
|
||||
assertEquals(image.getWidth(), raster.getWidth());
|
||||
assertEquals(image.getHeight(), raster.getHeight());
|
||||
|
||||
assertEquals(4, raster.getNumBands());
|
||||
assertEquals(4, raster.getNumDataElements());
|
||||
|
||||
assertImageRasterEquals(image, raster);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsByteRasterWritableFromTYPE_INT_ARGB_PRE() {
|
||||
BufferedImage image = new BufferedImage(9, 11, BufferedImage.TYPE_INT_ARGB_PRE);
|
||||
|
||||
WritableRaster raster = RasterUtils.asByteRaster(image.getRaster());
|
||||
|
||||
assertEquals(DataBuffer.TYPE_BYTE, raster.getTransferType());
|
||||
assertEquals(PixelInterleavedSampleModel.class, raster.getSampleModel().getClass());
|
||||
assertEquals(image.getWidth(), raster.getWidth());
|
||||
assertEquals(image.getHeight(), raster.getHeight());
|
||||
|
||||
assertEquals(4, raster.getNumBands());
|
||||
assertEquals(4, raster.getNumDataElements());
|
||||
|
||||
// We don't assert on values here, as the premultiplied values makes it hard...
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsByteRasterWritableFromTYPE_INT_BGR() {
|
||||
BufferedImage image = new BufferedImage(9, 11, BufferedImage.TYPE_INT_BGR);
|
||||
|
||||
WritableRaster raster = RasterUtils.asByteRaster(image.getRaster());
|
||||
|
||||
assertEquals(DataBuffer.TYPE_BYTE, raster.getTransferType());
|
||||
assertEquals(PixelInterleavedSampleModel.class, raster.getSampleModel().getClass());
|
||||
assertEquals(image.getWidth(), raster.getWidth());
|
||||
assertEquals(image.getHeight(), raster.getHeight());
|
||||
|
||||
assertEquals(3, raster.getNumBands());
|
||||
assertEquals(3, raster.getNumDataElements());
|
||||
|
||||
assertImageRasterEquals(image, raster);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsByteRasterWritableFromTYPE_CUSTOM_GRAB() {
|
||||
BufferedImage image = ImageTypeSpecifier.createPacked(ColorSpace.getInstance(ColorSpace.CS_sRGB),
|
||||
0x00FF0000,
|
||||
0xFF000000,
|
||||
0x000000FF,
|
||||
0x0000FF00,
|
||||
DataBuffer.TYPE_INT, false).createBufferedImage(7, 13);
|
||||
|
||||
WritableRaster raster = RasterUtils.asByteRaster(image.getRaster());
|
||||
|
||||
assertEquals(DataBuffer.TYPE_BYTE, raster.getTransferType());
|
||||
assertEquals(PixelInterleavedSampleModel.class, raster.getSampleModel().getClass());
|
||||
assertEquals(image.getWidth(), raster.getWidth());
|
||||
assertEquals(image.getHeight(), raster.getHeight());
|
||||
|
||||
assertEquals(4, raster.getNumBands());
|
||||
assertEquals(4, raster.getNumDataElements());
|
||||
|
||||
assertImageRasterEquals(image, raster);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsByteRasterWritableFromTYPE_CUSTOM_BxRG() {
|
||||
BufferedImage image = ImageTypeSpecifier.createPacked(ColorSpace.getInstance(ColorSpace.CS_sRGB),
|
||||
0x0000FF00,
|
||||
0x000000FF,
|
||||
0xFF000000,
|
||||
0,
|
||||
DataBuffer.TYPE_INT, false).createBufferedImage(7, 13);
|
||||
|
||||
WritableRaster raster = RasterUtils.asByteRaster(image.getRaster());
|
||||
|
||||
assertEquals(DataBuffer.TYPE_BYTE, raster.getTransferType());
|
||||
assertEquals(PixelInterleavedSampleModel.class, raster.getSampleModel().getClass());
|
||||
assertEquals(image.getWidth(), raster.getWidth());
|
||||
assertEquals(image.getHeight(), raster.getHeight());
|
||||
|
||||
assertEquals(3, raster.getNumBands());
|
||||
assertEquals(3, raster.getNumDataElements());
|
||||
|
||||
assertImageRasterEquals(image, raster);
|
||||
}
|
||||
|
||||
private static void assertImageRasterEquals(BufferedImage image, WritableRaster raster) {
|
||||
// NOTE: This is NOT necessarily how the values are stored in the data buffer
|
||||
int[] argbOffs = new int[] {16, 8, 0, 24};
|
||||
|
||||
Raster imageRaster = image.getRaster();
|
||||
|
||||
Random rng = new Random(27365481723L);
|
||||
|
||||
for (int y = 0; y < raster.getHeight(); y++) {
|
||||
for (int x = 0; x < raster.getWidth(); x++) {
|
||||
int argb = 0;
|
||||
|
||||
for (int b = 0; b < raster.getNumBands(); b++) {
|
||||
int s = rng.nextInt(0xFF);
|
||||
raster.setSample(x, y, b, s);
|
||||
|
||||
assertEquals(s, raster.getSample(x, y, b));
|
||||
assertEquals(s, imageRaster.getSample(x, y, b));
|
||||
|
||||
argb |= (s << argbOffs[b]);
|
||||
}
|
||||
|
||||
if (raster.getNumBands() < 4) {
|
||||
argb |= 0xFF000000;
|
||||
}
|
||||
|
||||
int expectedArgb = image.getRGB(x, y);
|
||||
if (argb != expectedArgb) {
|
||||
assertEquals(x + ", " + y + ": ", String.format("#%08x", expectedArgb), String.format("#%08x", argb));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+17
-15
@@ -31,6 +31,7 @@
|
||||
package com.twelvemonkeys.imageio.util;
|
||||
|
||||
import com.twelvemonkeys.imageio.color.ColorSpaces;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
@@ -39,7 +40,8 @@ import java.awt.image.ComponentColorModel;
|
||||
import java.awt.image.DataBuffer;
|
||||
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.*;
|
||||
|
||||
public class UInt32ImageTypeSpecifierTest {
|
||||
@@ -55,13 +57,13 @@ public class UInt32ImageTypeSpecifierTest {
|
||||
assertEquals(1, spec.getNumComponents());
|
||||
assertEquals(32, spec.getBitsPerBand(0));
|
||||
|
||||
assertThat(spec.getColorModel(), is(ComponentColorModel.class));
|
||||
assertThat(spec.getColorModel(), instanceOf(ComponentColorModel.class));
|
||||
assertFalse(spec.getColorModel().hasAlpha());
|
||||
assertFalse(spec.getColorModel().isAlphaPremultiplied());
|
||||
assertEquals(1, spec.getColorModel().getNumComponents());
|
||||
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().getNumDataElements());
|
||||
}
|
||||
@@ -74,13 +76,13 @@ public class UInt32ImageTypeSpecifierTest {
|
||||
assertEquals(32, spec.getBitsPerBand(0));
|
||||
assertEquals(32, spec.getBitsPerBand(1));
|
||||
|
||||
assertThat(spec.getColorModel(), is(ComponentColorModel.class));
|
||||
assertThat(spec.getColorModel(), instanceOf(ComponentColorModel.class));
|
||||
assertTrue(spec.getColorModel().hasAlpha());
|
||||
assertFalse(spec.getColorModel().isAlphaPremultiplied());
|
||||
assertEquals(2, spec.getColorModel().getNumComponents());
|
||||
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().getNumDataElements());
|
||||
}
|
||||
@@ -95,13 +97,13 @@ public class UInt32ImageTypeSpecifierTest {
|
||||
assertEquals(32, spec.getBitsPerBand(1));
|
||||
assertEquals(32, spec.getBitsPerBand(2));
|
||||
|
||||
assertThat(spec.getColorModel(), is(ComponentColorModel.class));
|
||||
assertThat(spec.getColorModel(), instanceOf(ComponentColorModel.class));
|
||||
assertFalse(spec.getColorModel().hasAlpha());
|
||||
assertFalse(spec.getColorModel().isAlphaPremultiplied());
|
||||
assertEquals(3, spec.getColorModel().getNumComponents());
|
||||
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().getNumDataElements());
|
||||
}
|
||||
@@ -116,13 +118,13 @@ public class UInt32ImageTypeSpecifierTest {
|
||||
assertEquals(32, spec.getBitsPerBand(2));
|
||||
assertEquals(32, spec.getBitsPerBand(3));
|
||||
|
||||
assertThat(spec.getColorModel(), is(ComponentColorModel.class));
|
||||
assertThat(spec.getColorModel(), instanceOf(ComponentColorModel.class));
|
||||
assertTrue(spec.getColorModel().hasAlpha());
|
||||
assertFalse(spec.getColorModel().isAlphaPremultiplied());
|
||||
assertEquals(4, spec.getColorModel().getNumComponents());
|
||||
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().getNumDataElements());
|
||||
}
|
||||
@@ -137,13 +139,13 @@ public class UInt32ImageTypeSpecifierTest {
|
||||
assertEquals(32, spec.getBitsPerBand(2));
|
||||
assertEquals(32, spec.getBitsPerBand(3));
|
||||
|
||||
assertThat(spec.getColorModel(), is(ComponentColorModel.class));
|
||||
assertThat(spec.getColorModel(), instanceOf(ComponentColorModel.class));
|
||||
assertTrue(spec.getColorModel().hasAlpha());
|
||||
assertTrue(spec.getColorModel().isAlphaPremultiplied());
|
||||
assertEquals(4, spec.getColorModel().getNumComponents());
|
||||
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().getNumDataElements());
|
||||
}
|
||||
@@ -159,13 +161,13 @@ public class UInt32ImageTypeSpecifierTest {
|
||||
assertEquals(32, spec.getBitsPerBand(2));
|
||||
assertEquals(32, spec.getBitsPerBand(3));
|
||||
|
||||
assertThat(spec.getColorModel(), is(ComponentColorModel.class));
|
||||
assertThat(spec.getColorModel(), instanceOf(ComponentColorModel.class));
|
||||
assertFalse(spec.getColorModel().hasAlpha());
|
||||
assertFalse(spec.getColorModel().isAlphaPremultiplied());
|
||||
assertEquals(4, spec.getColorModel().getNumComponents());
|
||||
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().getNumDataElements());
|
||||
}
|
||||
@@ -181,13 +183,13 @@ public class UInt32ImageTypeSpecifierTest {
|
||||
assertEquals(32, spec.getBitsPerBand(3));
|
||||
assertEquals(32, spec.getBitsPerBand(4));
|
||||
|
||||
assertThat(spec.getColorModel(), is(ComponentColorModel.class));
|
||||
assertThat(spec.getColorModel(), instanceOf(ComponentColorModel.class));
|
||||
assertTrue(spec.getColorModel().hasAlpha());
|
||||
assertFalse(spec.getColorModel().isAlphaPremultiplied());
|
||||
assertEquals(5, spec.getColorModel().getNumComponents());
|
||||
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().getNumDataElements());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2014, Harald Kuhr
|
||||
~ All rights reserved.
|
||||
~
|
||||
~ Redistribution and use in source and binary forms, with or without
|
||||
~ modification, are permitted provided that the following conditions are met:
|
||||
~ * Redistributions of source code must retain the above copyright
|
||||
~ notice, this list of conditions and the following disclaimer.
|
||||
~ * Redistributions in binary form must reproduce the above copyright
|
||||
~ notice, this list of conditions and the following disclaimer in the
|
||||
~ documentation and/or other materials provided with the distribution.
|
||||
~ * Neither the name "TwelveMonkeys" nor the
|
||||
~ names of its contributors may be used to endorse or promote products
|
||||
~ derived from this software without specific prior written permission.
|
||||
~
|
||||
~ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
~ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
~ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
~ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
~ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
~ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
~ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
~ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
~ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
~ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
~ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>imageio</artifactId>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>imageio-cr2</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: CR2 plugin</name>
|
||||
<description>ImageIO plugin for Canon RAW (CR2) format.</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
<classifier>tests</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-metadata</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<version>${project.version}</version>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-jpeg</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
+492
@@ -0,0 +1,492 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name "TwelveMonkeys" nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.cr2;
|
||||
|
||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||
import com.twelvemonkeys.imageio.metadata.CompoundDirectory;
|
||||
import com.twelvemonkeys.imageio.metadata.Directory;
|
||||
import com.twelvemonkeys.imageio.metadata.Entry;
|
||||
import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegment;
|
||||
import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegmentUtil;
|
||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
|
||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFFReader;
|
||||
import com.twelvemonkeys.imageio.plugins.jpeg.Slice;
|
||||
import com.twelvemonkeys.imageio.plugins.jpeg.SliceContext;
|
||||
import com.twelvemonkeys.imageio.stream.SubImageInputStream;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
//import com.twelvemonkeys.imageio.plugins.jpeg.LosslessJPEGDecoder;
|
||||
|
||||
/**
|
||||
* Canon CR2 RAW ImageReader.
|
||||
* <p/>
|
||||
* Acknowledgement:
|
||||
* This ImageReader is based on the excellent work of Laurent Clevy, and would probably not exist without it.
|
||||
*
|
||||
* @see <a href="http://lclevy.free.fr/dng/">Understanding What is stored in a Canon RAW .CR2 file, How and Why</a>
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: CR2ImageReader.java,v 1.0 07.04.14 21:31 haraldk Exp$
|
||||
*/
|
||||
public final class CR2ImageReader extends ImageReaderBase {
|
||||
// See http://lclevy.free.fr/dng/
|
||||
// TODO: Avoid duped code from TIFFImageReader
|
||||
// TODO: Probably a good idea to move some of the getAsShort/Int/Long/Array to TIFF/EXIF metadata module
|
||||
// TODO: Automatic EXIF rotation, if we find a good way to do that for JPEG/EXIF/TIFF and keeping the metadata sane...
|
||||
|
||||
final static boolean DEBUG = true; //"true".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.imageio.plugins.dng.debug"));
|
||||
|
||||
// Thumbnail is in IFD1 (2nd entry)
|
||||
private final static int THUMBNAIL_IFD = 1;
|
||||
|
||||
private CompoundDirectory IFDs;
|
||||
private Directory currentIFD;
|
||||
|
||||
CR2ImageReader(final ImageReaderSpi provider) {
|
||||
super(provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resetMembers() {
|
||||
IFDs = null;
|
||||
currentIFD = null;
|
||||
}
|
||||
|
||||
private void readMetadata() throws IOException {
|
||||
if (imageInput == null) {
|
||||
throw new IllegalStateException("input not set");
|
||||
}
|
||||
|
||||
if (IFDs == null) {
|
||||
// We'll validate the TIFF structure later, for now just see if we have 'CR'0x0200 at the right place
|
||||
|
||||
imageInput.skipBytes(8); // TIFF byte order mark + magic + IFD0 offset
|
||||
|
||||
if (imageInput.readByte() != 'C' || imageInput.readByte() != 'R') {
|
||||
throw new IIOException("Not a valid CR2 structure: Missing CR magic ('CR')");
|
||||
}
|
||||
|
||||
int version = imageInput.readUnsignedByte();
|
||||
int revision = imageInput.readUnsignedByte();
|
||||
|
||||
// TODO: Choke on anything but 2.0? All sample data from 400D until 5D mk III has 2.0 version...
|
||||
|
||||
imageInput.seek(0);
|
||||
|
||||
IFDs = (CompoundDirectory) new TIFFReader().read(imageInput); // NOTE: Sets byte order as a side effect
|
||||
|
||||
if (DEBUG) {
|
||||
System.err.println("Byte order: " + imageInput.getByteOrder());
|
||||
System.err.println("Version: " + version + "." + revision);
|
||||
System.err.println("Number of IFDs: " + IFDs.directoryCount());
|
||||
|
||||
for (int i = 0; i < IFDs.directoryCount(); i++) {
|
||||
System.err.printf("IFD %d: %s\n", i, IFDs.getDirectory(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void readIFD(final int ifdIndex) throws IOException {
|
||||
readMetadata();
|
||||
|
||||
if (ifdIndex < 0) {
|
||||
throw new IndexOutOfBoundsException("index < minIndex");
|
||||
}
|
||||
else if (ifdIndex >= IFDs.directoryCount()) {
|
||||
throw new IndexOutOfBoundsException("index >= numImages (" + ifdIndex + " >= " + IFDs.directoryCount() + ")");
|
||||
}
|
||||
|
||||
currentIFD = IFDs.getDirectory(ifdIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumImages(final boolean allowSearch) throws IOException {
|
||||
readMetadata();
|
||||
|
||||
// This validation is maybe a little too restrictive, but ok for now
|
||||
if (IFDs.directoryCount() != 4) {
|
||||
throw new IIOException("Unexpected number of IFDs in CR2: " + IFDs.directoryCount());
|
||||
}
|
||||
|
||||
return IFDs.directoryCount() - 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumThumbnails(int imageIndex) throws IOException {
|
||||
readMetadata();
|
||||
checkBounds(imageIndex);
|
||||
|
||||
return imageIndex == 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean readerSupportsThumbnails() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getThumbnailWidth(int imageIndex, int thumbnailIndex) throws IOException {
|
||||
readMetadata();
|
||||
checkBounds(imageIndex);
|
||||
|
||||
// TODO: Need to get from JPEGImageReader (no ImageWidth tag), this is an ok (but lame) implementation for now
|
||||
return super.getThumbnailWidth(imageIndex, thumbnailIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getThumbnailHeight(int imageIndex, int thumbnailIndex) throws IOException {
|
||||
readMetadata();
|
||||
checkBounds(imageIndex);
|
||||
|
||||
// TODO: Need to get from JPEGImageReader (no ImageHeight tag), this is an ok (but lame) implementation for now
|
||||
return super.getThumbnailHeight(imageIndex, thumbnailIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedImage readThumbnail(int imageIndex, int thumbnailIndex) throws IOException {
|
||||
readIFD(THUMBNAIL_IFD);
|
||||
|
||||
if (imageIndex != 0) {
|
||||
throw new IndexOutOfBoundsException("No thumbnail for imageIndex: " + imageIndex);
|
||||
}
|
||||
if (thumbnailIndex != 0) {
|
||||
throw new IndexOutOfBoundsException("thumbnailIndex out of bounds: " + thumbnailIndex);
|
||||
}
|
||||
|
||||
// This IFD (IFD1) lacks Compression 6 (old JPEG), but has the relevant tags for a JPEG/EXIF thumbnail
|
||||
int jpegOffset = getValueAsInt(TIFF.TAG_JPEG_INTERCHANGE_FORMAT, "JPEGInterchangeFormat");
|
||||
int jpegLength = getValueAsInt(TIFF.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, "JPEGInterchangeFormatLength");
|
||||
|
||||
imageInput.seek(jpegOffset);
|
||||
|
||||
// TODO: Consider using cached JPEGImageReader directly
|
||||
return ImageIO.read(new SubImageInputStream(imageInput, jpegLength));
|
||||
}
|
||||
|
||||
private long[] getValueAsLongArray(final int tag, final String tagName, boolean required) throws IIOException {
|
||||
Entry entry = currentIFD.getEntryById(tag);
|
||||
if (entry == null) {
|
||||
if (required) {
|
||||
throw new IIOException("Missing TIFF tag " + tagName);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
long[] value;
|
||||
|
||||
if (entry.valueCount() == 1) {
|
||||
// For single entries, this will be a boxed type
|
||||
value = new long[] {((Number) entry.getValue()).longValue()};
|
||||
}
|
||||
else if (entry.getValue() instanceof short[]) {
|
||||
short[] shorts = (short[]) entry.getValue();
|
||||
value = new long[shorts.length];
|
||||
|
||||
for (int i = 0, length = value.length; i < length; i++) {
|
||||
value[i] = shorts[i];
|
||||
}
|
||||
}
|
||||
else if (entry.getValue() instanceof int[]) {
|
||||
int[] ints = (int[]) entry.getValue();
|
||||
value = new long[ints.length];
|
||||
|
||||
for (int i = 0, length = value.length; i < length; i++) {
|
||||
value[i] = ints[i];
|
||||
}
|
||||
}
|
||||
else if (entry.getValue() instanceof long[]) {
|
||||
value = (long[]) entry.getValue();
|
||||
}
|
||||
else {
|
||||
throw new IIOException(String.format("Unsupported %s type: %s (%s)", tagName, entry.getTypeName(), entry.getValue().getClass()));
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private Number getValueAsNumberWithDefault(final int tag, final String tagName, final Number defaultValue) throws IIOException {
|
||||
Entry entry = currentIFD.getEntryById(tag);
|
||||
|
||||
if (entry == null) {
|
||||
if (defaultValue != null) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
throw new IIOException("Missing TIFF tag: " + (tagName != null ? tagName : tag));
|
||||
}
|
||||
|
||||
return (Number) entry.getValue();
|
||||
}
|
||||
|
||||
private long getValueAsLongWithDefault(final int tag, final String tagName, final Long defaultValue) throws IIOException {
|
||||
return getValueAsNumberWithDefault(tag, tagName, defaultValue).longValue();
|
||||
}
|
||||
|
||||
private long getValueAsLongWithDefault(final int tag, final Long defaultValue) throws IIOException {
|
||||
return getValueAsLongWithDefault(tag, null, defaultValue);
|
||||
}
|
||||
|
||||
private int getValueAsIntWithDefault(final int tag, final String tagName, final Integer defaultValue) throws IIOException {
|
||||
return getValueAsNumberWithDefault(tag, tagName, defaultValue).intValue();
|
||||
}
|
||||
|
||||
private int getValueAsIntWithDefault(final int tag, Integer defaultValue) throws IIOException {
|
||||
return getValueAsIntWithDefault(tag, null, defaultValue);
|
||||
}
|
||||
|
||||
private int getValueAsInt(final int tag, String tagName) throws IIOException {
|
||||
return getValueAsIntWithDefault(tag, tagName, null);
|
||||
}
|
||||
|
||||
private int imageIndexToIFDNumber(int imageIndex) {
|
||||
return imageIndex >= THUMBNAIL_IFD ? imageIndex + 1 : imageIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth(int imageIndex) throws IOException {
|
||||
readIFD(imageIndexToIFDNumber(imageIndex));
|
||||
|
||||
return getValueAsInt(TIFF.TAG_IMAGE_WIDTH, "ImageWidth");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight(int imageIndex) throws IOException {
|
||||
readIFD(imageIndexToIFDNumber(imageIndex));
|
||||
|
||||
return getValueAsInt(TIFF.TAG_IMAGE_HEIGHT, "ImageHeight");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IOException {
|
||||
readIFD(imageIndexToIFDNumber(imageIndex));
|
||||
|
||||
// TODO: For IFD0, get from JPEGImageReader delagate
|
||||
|
||||
// return Arrays.asList(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR)).iterator();
|
||||
Entry bitsPerSample = currentIFD.getEntryById(TIFF.TAG_BITS_PER_SAMPLE);
|
||||
if (bitsPerSample == null) {
|
||||
// TODO: FixME!
|
||||
return Arrays.asList(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR)).iterator();
|
||||
}
|
||||
|
||||
// For IFD1, create linear RGB, but we don't really know...
|
||||
int bitDepth = ((int[]) bitsPerSample.getValue())[0]; // Assume all equal!
|
||||
if (bitDepth == 8) {
|
||||
return Arrays.asList(ImageTypeSpecifier.createInterleaved(ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB), new int [] {0, 1, 2}, DataBuffer.TYPE_BYTE, false, false)).iterator();
|
||||
}
|
||||
else if (bitDepth == 16) {
|
||||
return Arrays.asList(ImageTypeSpecifier.createInterleaved(ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB), new int [] {0, 1, 2}, DataBuffer.TYPE_USHORT, false, false)).iterator();
|
||||
}
|
||||
|
||||
throw new IIOException("Unsupported bit depth: " + bitDepth);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedImage read(int imageIndex, ImageReadParam param) throws IOException {
|
||||
readIFD(imageIndexToIFDNumber(imageIndex));
|
||||
|
||||
if (imageIndex == 0) {
|
||||
// This one says Compression 6 (old JPEG) and contains normal JPEG data at StripOffsets (but has no JPEGInterchangeFormat tag)
|
||||
int compression = getValueAsInt(TIFF.TAG_COMPRESSION, "Compression");
|
||||
if (compression != 6) {
|
||||
throw new IIOException("Unknown TIFF compression for CR2 IFD0: " + compression);
|
||||
}
|
||||
|
||||
int stripOffsets = getValueAsInt(TIFF.TAG_STRIP_OFFSETS, "StripOffsets");
|
||||
int stripByteCounts = getValueAsInt(TIFF.TAG_STRIP_BYTE_COUNTS, "StripByteCounts");
|
||||
imageInput.seek(stripOffsets);
|
||||
|
||||
List<JPEGSegment> segments = JPEGSegmentUtil.readSegments(new SubImageInputStream(imageInput, stripByteCounts), JPEGSegmentUtil.ALL_SEGMENTS);
|
||||
System.err.println("segments: " + segments);
|
||||
|
||||
imageInput.seek(stripOffsets);
|
||||
BufferedImage image = ImageIO.read(new SubImageInputStream(imageInput, stripByteCounts));
|
||||
System.err.println("image: " + image);
|
||||
return image;
|
||||
}
|
||||
|
||||
if (imageIndex == 1) {
|
||||
// This one is semi-ok, for older cameras is says Compression 6 (old JPEG), for 7D it says Compression 1 (None)
|
||||
// We'll just ignore the compression and assume it's 1 (None).
|
||||
// TODO: Probably a good idea to verify that we have no other compression than 6/1
|
||||
// TODO: Consider just masking out this image, as it's not of much use...
|
||||
|
||||
int width = getWidth(imageIndex);
|
||||
int height = getHeight(imageIndex);
|
||||
|
||||
BufferedImage destination = getDestination(param, getImageTypes(imageIndex), width, height);
|
||||
WritableRaster raster;
|
||||
|
||||
int dataType = destination.getSampleModel().getDataType();
|
||||
if (dataType == DataBuffer.TYPE_BYTE) {
|
||||
// Emulate raw type (as dest, but RGB instead of BGR)
|
||||
raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, width, 1, width * 3, 3, new int[] {0, 1, 2}, null);
|
||||
}
|
||||
else if (dataType == DataBuffer.TYPE_USHORT) {
|
||||
// Emulate raw type (as dest, but RGB instead of BGR)
|
||||
raster = Raster.createInterleavedRaster(DataBuffer.TYPE_USHORT, width, 1, width * 3, 3, new int[] {0, 1, 2}, null);
|
||||
}
|
||||
else {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
DataBuffer dataBuffer = raster.getDataBuffer();
|
||||
|
||||
SampleModel destSampleModel = destination.getSampleModel();
|
||||
DataBuffer destBuffer = destination.getRaster().getDataBuffer();
|
||||
SampleModel srcSampleModel = raster.getSampleModel();
|
||||
|
||||
if (destBuffer.getSize() != getValueAsInt(TIFF.TAG_STRIP_BYTE_COUNTS, "StripByteCounts")) {
|
||||
System.err.println("dataBuffer: " + dataBuffer.getSize());
|
||||
System.err.println("StripByteCounts: " + getValueAsInt(TIFF.TAG_STRIP_BYTE_COUNTS, "StripByteCounts"));
|
||||
}
|
||||
|
||||
int stripOffsets = getValueAsInt(TIFF.TAG_STRIP_OFFSETS, "StripOffsets");
|
||||
imageInput.seek(stripOffsets);
|
||||
|
||||
Object data = null;
|
||||
for (int y = 0; y < height; y++) {
|
||||
if (dataType == DataBuffer.TYPE_BYTE) {
|
||||
imageInput.readFully(((DataBufferByte) dataBuffer).getData());
|
||||
}
|
||||
else {
|
||||
imageInput.readFully(((DataBufferUShort) dataBuffer).getData(), 0, dataBuffer.getSize());
|
||||
}
|
||||
|
||||
data = srcSampleModel.getDataElements(0, 0, width, 1, data, dataBuffer);
|
||||
destSampleModel.setDataElements(0, y, width, 1, data, destBuffer);
|
||||
}
|
||||
|
||||
// TODO: This seems to work as crop values, if so correct w/h from getImageWidth/Height??
|
||||
Entry unknown = currentIFD.getEntryById(50908);
|
||||
if (unknown != null) {
|
||||
Graphics2D g = destination.createGraphics();
|
||||
try {
|
||||
long[] values = (long[]) unknown.getValue();
|
||||
|
||||
g.setPaint(new Color(63, 223, 88, 128));
|
||||
// g.drawRect((int) values[2], (int) values[3], (int) values[0], (int) values[1]);
|
||||
// g.fillRect((int) values[2], (int) values[3], (int) values[0], (int) values[1]);
|
||||
g.fillRect(0, 0, width, (int) values[3]);
|
||||
g.fillRect(0, (int) values[3], (int) values[2], (int) (values[1] + values[3]));
|
||||
g.fillRect((int) (values[2] + values[0]), (int) values[3], (int) values[2], (int) (values[1] + values[3]));
|
||||
g.fillRect(0, (int) (values[3] + values[1]), width, height - (int) (values[3] + values[1]));
|
||||
}
|
||||
finally {
|
||||
g.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
return destination;
|
||||
}
|
||||
|
||||
if (imageIndex == 2) {
|
||||
// TODO: This is the real RAW data. It's supposed to be lossless JPEG encoded, single channel,
|
||||
// and has to be interpolated to become full 3 channel data from the Bayer CFA array,
|
||||
// then further processed with white balance correction, black subtraction and color scaling.
|
||||
// At least. ;-)
|
||||
|
||||
// We should probably just mask this image out, until we can read it
|
||||
|
||||
int stripOffsets = getValueAsInt(TIFF.TAG_STRIP_OFFSETS, "StripOffsets");
|
||||
int stripByteCounts = getValueAsInt(TIFF.TAG_STRIP_BYTE_COUNTS, "StripByteCounts");
|
||||
long[] slices = getValueAsLongArray(50752, "Slices", true);
|
||||
|
||||
try {
|
||||
final Slice slice = Slice.createSlice(slices);
|
||||
SliceContext.set(slice);
|
||||
|
||||
// TODO: Get correct dimensions (sensor size?)
|
||||
int width = getWidth(0);
|
||||
int height = getHeight(0);
|
||||
|
||||
imageInput.seek(stripOffsets);
|
||||
return ImageIO.read(new SubImageInputStream(imageInput, stripByteCounts));
|
||||
} finally {
|
||||
SliceContext.remove();
|
||||
}
|
||||
|
||||
// byte[] data = new LosslessJPEGDecoder().decompress(new SubImageInputStream(imageInput, stripByteCounts), null);
|
||||
//
|
||||
// // TODO: We really have 2 bytes/sample
|
||||
// short[] data2 = new short[data.length / 2];
|
||||
// ByteBuffer wrap = ByteBuffer.wrap(data);
|
||||
// wrap.asShortBuffer().get(data2);
|
||||
//
|
||||
// System.err.println("data.length: " + data2.length);
|
||||
// System.err.println("width x height: " + width * height);
|
||||
//
|
||||
// DataBuffer dataBuffer = new DataBufferUShort(data2, data2.length);
|
||||
// WritableRaster raster = Raster.createInterleavedRaster(dataBuffer, width, height, width, 1, new int[] {0}, null);
|
||||
// ColorModel cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), false, false, Transparency.OPAQUE, raster.getTransferType());
|
||||
// BufferedImage image = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
|
||||
//
|
||||
// System.err.println("image: " + image);
|
||||
//
|
||||
// return image;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
CR2ImageReader reader = new CR2ImageReader(new CR2ImageReaderSpi());
|
||||
|
||||
for (String arg : args) {
|
||||
ImageInputStream stream = ImageIO.createImageInputStream(new File(arg));
|
||||
reader.setInput(stream);
|
||||
|
||||
int numImages = reader.getNumImages(true);
|
||||
for (int i = 0; i < numImages; i++) {
|
||||
int numThumbnails = reader.getNumThumbnails(i);
|
||||
for (int n = 0; n < numThumbnails; n++) {
|
||||
showIt(reader.readThumbnail(i, n), arg + " image " + i + " thumbnail " + n);
|
||||
}
|
||||
|
||||
showIt(reader.read(i), arg + " image " + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name "TwelveMonkeys" nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.cr2;
|
||||
|
||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
|
||||
import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
|
||||
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* CR2ImageReaderSpi
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: CR2ImageReaderSpi.java,v 1.0 07.04.14 21:26 haraldk Exp$
|
||||
*/
|
||||
public final class CR2ImageReaderSpi extends ImageReaderSpiBase {
|
||||
public CR2ImageReaderSpi() {
|
||||
super(new CR2ProviderInfo());
|
||||
}
|
||||
|
||||
public boolean canDecodeInput(final Object pSource) throws IOException {
|
||||
if (!(pSource instanceof ImageInputStream)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ImageInputStream stream = (ImageInputStream) pSource;
|
||||
|
||||
stream.mark();
|
||||
try {
|
||||
byte[] bom = new byte[2];
|
||||
stream.readFully(bom);
|
||||
|
||||
ByteOrder originalOrder = stream.getByteOrder();
|
||||
|
||||
try {
|
||||
if (bom[0] == 'I' && bom[1] == 'I') {
|
||||
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
}
|
||||
else if (bom[0] == 'M' && bom[1] == 'M') {
|
||||
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
||||
int tiffMagic = stream.readUnsignedShort();
|
||||
if (tiffMagic != TIFF.TIFF_MAGIC) {
|
||||
return false;
|
||||
}
|
||||
|
||||
stream.skipBytes(4); // TIFF IFD0 offset
|
||||
|
||||
if (stream.readByte() != 'C' || stream.readByte() != 'R') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Version 2.0
|
||||
return stream.readUnsignedByte() == 2 && stream.readUnsignedByte() == 0;
|
||||
}
|
||||
finally {
|
||||
stream.setByteOrder(originalOrder);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
stream.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageReader createReaderInstance(Object extension) throws IOException {
|
||||
return new CR2ImageReader(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription(Locale locale) {
|
||||
return "Canon RAW (CR2) format Reader";
|
||||
}
|
||||
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oleg Ermolaev
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name "TwelveMonkeys" nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.cr2;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
||||
|
||||
/**
|
||||
* @author Oleg Ermolaev Date: 04.05.2018 1:50
|
||||
*/
|
||||
class CR2ProviderInfo extends ReaderWriterProviderInfo {
|
||||
protected CR2ProviderInfo() {
|
||||
super(
|
||||
CR2ProviderInfo.class,
|
||||
new String[]{"cr2", "CR2"},
|
||||
new String[]{"cr2"},
|
||||
new String[]{"image/x-canon-raw", // TODO: Look up
|
||||
},
|
||||
"com.twelvemonkeys.imageio.plugins.cr2.CR2ImageReader",
|
||||
new String[]{"com.twelvemonkeys.imageio.plugins.cr2.CR2ImageReaderSpi"},
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
com.twelvemonkeys.imageio.plugins.cr2.CR2ImageReaderSpi
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name "TwelveMonkeys" nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.cr2;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import java.awt.*;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* CR2ImageReaderTest
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: CR2ImageReaderTest.java,v 1.0 07.04.14 21:52 haraldk Exp$
|
||||
*/
|
||||
@Ignore
|
||||
public class CR2ImageReaderTest extends ImageReaderAbstractTest<CR2ImageReader> {
|
||||
@Override
|
||||
protected List<TestData> getTestData() {
|
||||
return Arrays.asList(
|
||||
new TestData(
|
||||
getClassLoaderResource("/cr2/IMG_3483.CR2"), // Canon EOS 400D
|
||||
new Dimension(3888, 2592) // This is what the TIFF structure says...
|
||||
/* from http://lclevy.free.fr/dng/:
|
||||
new Dimension(3888 / 4, 2592 / 4), // The image is supposed to be 1/4 of the size in the TIFF tags...
|
||||
new Dimension(-1, -1), // "small version", no size information, only JPEG data (EXIF thumbnail)
|
||||
new Dimension(384, 256), // according to http://lclevy.free.fr/dng/ this is not compressed, but TIFF structure says JPEG... Perhaps JPEG lossless?
|
||||
new Dimension(3888, 2592) // Full size image, no size information
|
||||
*/
|
||||
)
|
||||
// TODO: EOS 7D sample
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ImageReaderSpi createProvider() {
|
||||
return new CR2ImageReaderSpi();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getFormatNames() {
|
||||
return Arrays.asList("cr2");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getSuffixes() {
|
||||
return Arrays.asList("cr2");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getMIMETypes() {
|
||||
return Arrays.asList("image/x-canon-raw");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Known issue: Subsampled reading not supported")
|
||||
@Override
|
||||
public void testReadWithSubsampleParamPixels() throws IOException {
|
||||
super.testReadWithSubsampleParamPixels();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Known issue: Source region reading not supported")
|
||||
@Override
|
||||
public void testReadWithSourceRegionParamEqualImage() throws IOException {
|
||||
super.testReadWithSourceRegionParamEqualImage();
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,7 @@
|
||||
Contents:
|
||||
IMG_3483.CR2 - Rusty chain at Vækerø harbour, Oslo, Norway, Canon 400D, by me
|
||||
IMG_4841.CR2 - Chimneys at Casa Mila, Barcelona, Spain, Canon 400D, by me
|
||||
IMG_6933.CR2 - Panda PEZ dispenser at home, Oslo Noway, Canon 7D, by me
|
||||
|
||||
Above mentioned images are freely distributable for testing purposes.
|
||||
-- Harald K
|
||||
@@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2014, Harald Kuhr
|
||||
~ All rights reserved.
|
||||
~
|
||||
~ Redistribution and use in source and binary forms, with or without
|
||||
~ modification, are permitted provided that the following conditions are met:
|
||||
~ * Redistributions of source code must retain the above copyright
|
||||
~ notice, this list of conditions and the following disclaimer.
|
||||
~ * Redistributions in binary form must reproduce the above copyright
|
||||
~ notice, this list of conditions and the following disclaimer in the
|
||||
~ documentation and/or other materials provided with the distribution.
|
||||
~ * Neither the name "TwelveMonkeys" nor the
|
||||
~ names of its contributors may be used to endorse or promote products
|
||||
~ derived from this software without specific prior written permission.
|
||||
~
|
||||
~ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
~ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
~ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
~ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
~ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
~ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
~ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
~ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
~ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
~ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
~ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>imageio</artifactId>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>imageio-crw</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: CRW plugin</name>
|
||||
<description>ImageIO plugin for Canon RAW (CRW) format.</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
<type>test-jar</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-metadata</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<version>${project.version}</version>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-jpeg</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.twelvemonkeys.imageio.plugins.crw;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* CRW
|
||||
*
|
||||
* @see <a href="https://sno.phy.queensu.ca/~phil/exiftool/canon_raw.html">The Canon RAW (CRW) File Format</a>
|
||||
*/
|
||||
public interface CRW {
|
||||
int TAG_SLICES = 50752;
|
||||
}
|
||||
+388
@@ -0,0 +1,388 @@
|
||||
package com.twelvemonkeys.imageio.plugins.crw;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
|
||||
/**
|
||||
* CRWDecoder
|
||||
*
|
||||
* @see <a href="http://cybercom.net/~dcoffin/dcraw/decompress.c">A simple reference decompressor for CRW files</a>
|
||||
* @author Harald Kuhr (Java port)
|
||||
* @author Dave Coffin (original decompress.c)
|
||||
*/
|
||||
final class CRWDecoder {
|
||||
|
||||
static class Decode {
|
||||
Decode[] branch = new Decode[2];
|
||||
int leaf;
|
||||
}
|
||||
|
||||
private final ImageInputStream imageInput;
|
||||
|
||||
private final int height;
|
||||
private final int width;
|
||||
private final int table;
|
||||
|
||||
private Decode[] first_decode = new Decode[32];
|
||||
private Decode[] second_decode = new Decode[512];
|
||||
|
||||
CRWDecoder(ImageInputStream imageInput, int width, int height, int table) {
|
||||
this.imageInput = imageInput;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.table = table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code false} if the image starts with compressed data,
|
||||
* {@code true} if it starts with uncompressed low-order bits.
|
||||
* <p>
|
||||
* In Canon compressed data, 0xff is always followed by 0x00.
|
||||
*/
|
||||
private boolean canonHasLowbits() throws IOException {
|
||||
byte[] test = new byte[0x4000]; // TODO: Should probably be (height * width / 4) * enough bytes...
|
||||
|
||||
imageInput.seek(0);
|
||||
imageInput.readFully(test);
|
||||
|
||||
boolean ret = true;
|
||||
// for (int i = 540; i < test.length - 1; i++) {
|
||||
for (int i = 0; i < test.length - 1; i++) { // Note: The original 540 is probably CIFF header length + offset (26 + 514)
|
||||
if ((test[i] & 0xff) == 0xff) {
|
||||
if (test[i + 1] != 0) {
|
||||
return true;
|
||||
}
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
A rough description of Canon's compression algorithm:
|
||||
|
||||
+ Each pixel outputs a 10-bit sample, from 0 to 1023.
|
||||
+ Split the data into blocks of 64 samples each.
|
||||
+ Subtract from each sample the value of the sample two positions
|
||||
to the left, which has the same color filter. From the two
|
||||
leftmost samples in each row, subtract 512.
|
||||
+ For each nonzero sample, make a token consisting of two four-bit
|
||||
numbers. The low nibble is the number of bits required to
|
||||
represent the sample, and the high nibble is the number of
|
||||
zero samples preceding this sample.
|
||||
+ Output this token as a variable-length bitstring using
|
||||
one of three tablesets. Follow it with a fixed-length
|
||||
bitstring containing the sample.
|
||||
|
||||
The "first_decode" table is used for the first sample in each
|
||||
block, and the "second_decode" table is used for the others.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Construct a decode tree according the specification in *source.
|
||||
* The first 16 bytes specify how many codes should be 1-bit, 2-bit
|
||||
* 3-bit, etc. Bytes after that are the leaf values.
|
||||
* <p>
|
||||
* For example, if the source is
|
||||
* <p>
|
||||
* { 0,1,4,2,3,1,2,0,0,0,0,0,0,0,0,0,
|
||||
* 0x04,0x03,0x05,0x06,0x02,0x07,0x01,0x08,0x09,0x00,0x0a,0x0b,0xff },
|
||||
* <p>
|
||||
* then the code is
|
||||
* <p>
|
||||
* 00 0x04
|
||||
* 010 0x03
|
||||
* 011 0x05
|
||||
* 100 0x06
|
||||
* 101 0x02
|
||||
* 1100 0x07
|
||||
* 1101 0x01
|
||||
* 11100 0x08
|
||||
* 11101 0x09
|
||||
* 11110 0x00
|
||||
* 111110 0x0a
|
||||
* 1111110 0x0b
|
||||
* 1111111 0xff
|
||||
*/
|
||||
private Decode[] free; /* Next unused node */
|
||||
private int freeIndex;
|
||||
private int leaf; /* no. of leaves already added */
|
||||
|
||||
// private void make_decoder(struct decode *dest, const uchar *source, int level)
|
||||
private void make_decoder(Decode[] dest, int destIndex, final byte[] source, int level) {
|
||||
// static struct decode *free; /* Next unused node */
|
||||
// static int leaf; /* no. of leaves already added */
|
||||
int i, next;
|
||||
|
||||
if (level==0) {
|
||||
free = dest;
|
||||
freeIndex = 0;
|
||||
|
||||
leaf = 0;
|
||||
}
|
||||
// free++;
|
||||
freeIndex++;
|
||||
// At what level should the next leaf appear?
|
||||
for (i=next=0; i <= leaf && next < 16; ) {
|
||||
i += (source[next++] & 0xff);
|
||||
}
|
||||
|
||||
if (i > leaf) {
|
||||
if (level < next) { /* Are we there yet? */
|
||||
// dest->branch[0] = free;
|
||||
// make_decoder(free, source, level + 1);
|
||||
dest[destIndex].branch[0] = free[freeIndex];
|
||||
make_decoder(free, freeIndex, source, level + 1);
|
||||
// dest->branch[1] = free;
|
||||
// make_decoder(free, source, level + 1);
|
||||
dest[destIndex].branch[1] = free[freeIndex];
|
||||
make_decoder(free, freeIndex, source, level + 1);
|
||||
} else {
|
||||
// dest->leaf = source[16 + leaf++];
|
||||
dest[destIndex].leaf = (source[16 + leaf++] & 0xff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static final byte[][] first_tree/*[3][29]*/ = {
|
||||
{ 0,1,4,2,3,1,2,0,0,0,0,0,0,0,0,0,
|
||||
0x04,0x03,0x05,0x06,0x02,0x07,0x01,0x08,0x09,0x00,0x0a,0x0b, (byte) 0xff},
|
||||
|
||||
{ 0,2,2,3,1,1,1,1,2,0,0,0,0,0,0,0,
|
||||
0x03,0x02,0x04,0x01,0x05,0x00,0x06,0x07,0x09,0x08,0x0a,0x0b, (byte) 0xff},
|
||||
|
||||
{ 0,0,6,3,1,1,2,0,0,0,0,0,0,0,0,0,
|
||||
0x06,0x05,0x07,0x04,0x08,0x03,0x09,0x02,0x00,0x0a,0x01,0x0b, (byte) 0xff},
|
||||
};
|
||||
|
||||
static final byte[][] second_tree/*[3][180]*/ = {
|
||||
{ 0,2,2,2,1,4,2,1,2,5,1,1,0,0,0, (byte) 139,
|
||||
0x03,0x04,0x02,0x05,0x01,0x06,0x07,0x08,
|
||||
0x12,0x13,0x11,0x14,0x09,0x15,0x22,0x00,0x21,0x16,0x0a, (byte) 0xf0,
|
||||
0x23,0x17,0x24,0x31,0x32,0x18,0x19,0x33,0x25,0x41,0x34,0x42,
|
||||
0x35,0x51,0x36,0x37,0x38,0x29,0x79,0x26,0x1a,0x39,0x56,0x57,
|
||||
0x28,0x27,0x52,0x55,0x58,0x43,0x76,0x59,0x77,0x54,0x61, (byte) 0xf9,
|
||||
0x71,0x78,0x75, (byte) 0x96, (byte) 0x97,0x49, (byte) 0xb7,0x53, (byte) 0xd7,0x74, (byte) 0xb6, (byte) 0x98,
|
||||
0x47,0x48, (byte) 0x95,0x69, (byte) 0x99, (byte) 0x91, (byte) 0xfa, (byte) 0xb8,0x68, (byte) 0xb5, (byte) 0xb9, (byte) 0xd6,
|
||||
(byte) 0xf7, (byte) 0xd8,0x67,0x46,0x45, (byte) 0x94,(byte) 0x89,(byte) 0xf8,(byte) 0x81,(byte) 0xd5,(byte) 0xf6,(byte) 0xb4,
|
||||
(byte) 0x88,(byte) 0xb1,0x2a,0x44,0x72,(byte) 0xd9,(byte) 0x87,0x66,(byte) 0xd4,(byte) 0xf5,0x3a,(byte) 0xa7,
|
||||
0x73,(byte) 0xa9,(byte) 0xa8,(byte) 0x86,0x62,(byte) 0xc7,0x65,(byte) 0xc8,(byte) 0xc9,(byte) 0xa1,(byte) 0xf4,(byte) 0xd1,
|
||||
(byte) 0xe9,0x5a,(byte) 0x92,(byte) 0x85,(byte) 0xa6,(byte) 0xe7,(byte) 0x93,(byte) 0xe8,(byte) 0xc1,(byte) 0xc6,0x7a,0x64,
|
||||
(byte) 0xe1,0x4a,0x6a,(byte) 0xe6,(byte) 0xb3,(byte) 0xf1,(byte) 0xd3,(byte) 0xa5,(byte) 0x8a,(byte) 0xb2,(byte) 0x9a,(byte) 0xba,
|
||||
(byte) 0x84,(byte) 0xa4,0x63,(byte) 0xe5,(byte) 0xc5,(byte) 0xf3,(byte) 0xd2,(byte) 0xc4,(byte) 0x82,(byte) 0xaa,(byte) 0xda,(byte) 0xe4,
|
||||
(byte) 0xf2,(byte) 0xca,(byte) 0x83,(byte) 0xa3,(byte) 0xa2,(byte) 0xc3,(byte) 0xea,(byte) 0xc2,(byte) 0xe2,(byte) 0xe3,(byte) 0xff,(byte) 0xff },
|
||||
|
||||
{ 0,2,2,1,4,1,4,1,3,3,1,0,0,0,0, (byte) 140,
|
||||
0x02,0x03,0x01,0x04,0x05,0x12,0x11,0x06,
|
||||
0x13,0x07,0x08,0x14,0x22,0x09,0x21,0x00,0x23,0x15,0x31,0x32,
|
||||
0x0a,0x16,(byte) 0xf0,0x24,0x33,0x41,0x42,0x19,0x17,0x25,0x18,0x51,
|
||||
0x34,0x43,0x52,0x29,0x35,0x61,0x39,0x71,0x62,0x36,0x53,0x26,
|
||||
0x38,0x1a,0x37,(byte) 0x81,0x27,(byte) 0x91,0x79,0x55,0x45,0x28,0x72,0x59,
|
||||
(byte) 0xa1,(byte) 0xb1,0x44,0x69,0x54,0x58,(byte) 0xd1,(byte) 0xfa,0x57,(byte) 0xe1,(byte) 0xf1,(byte) 0xb9,
|
||||
0x49,0x47,0x63,0x6a,(byte) 0xf9,0x56,0x46,(byte) 0xa8,0x2a,0x4a,0x78,(byte) 0x99,
|
||||
0x3a,0x75,0x74,(byte) 0x86,0x65,(byte) 0xc1,0x76,(byte) 0xb6,(byte) 0x96,(byte) 0xd6,(byte) 0x89,(byte) 0x85,
|
||||
(byte) 0xc9,(byte) 0xf5,(byte) 0x95,(byte) 0xb4,(byte) 0xc7,(byte) 0xf7,(byte) 0x8a,(byte) 0x97,(byte) 0xb8,0x73,(byte) 0xb7,(byte) 0xd8,
|
||||
(byte) 0xd9,(byte) 0x87,(byte) 0xa7,0x7a,0x48,(byte) 0x82,(byte) 0x84,(byte) 0xea,(byte) 0xf4,(byte) 0xa6,(byte) 0xc5,0x5a,
|
||||
(byte) 0x94,(byte) 0xa4,(byte) 0xc6,(byte) 0x92,(byte) 0xc3,0x68,(byte) 0xb5,(byte) 0xc8,(byte) 0xe4,(byte) 0xe5,(byte) 0xe6,(byte) 0xe9,
|
||||
(byte) 0xa2,(byte) 0xa3,(byte) 0xe3,(byte) 0xc2,0x66,0x67,(byte) 0x93,(byte) 0xaa,(byte) 0xd4,(byte) 0xd5,(byte) 0xe7,(byte) 0xf8,
|
||||
(byte) 0x88,(byte) 0x9a,(byte) 0xd7,0x77,(byte) 0xc4,0x64,(byte) 0xe2,(byte) 0x98,(byte) 0xa5,(byte) 0xca,(byte) 0xda,(byte) 0xe8,
|
||||
(byte) 0xf3,(byte) 0xf6,(byte) 0xa9,(byte) 0xb2,(byte) 0xb3,(byte) 0xf2,(byte) 0xd2,(byte) 0x83,(byte) 0xba,(byte) 0xd3,(byte) 0xff,(byte) 0xff },
|
||||
|
||||
{ 0,0,6,2,1,3,3,2,5,1,2,2,8,10,0,117,
|
||||
0x04,0x05,0x03,0x06,0x02,0x07,0x01,0x08,
|
||||
0x09,0x12,0x13,0x14,0x11,0x15,0x0a,0x16,0x17,(byte) 0xf0,0x00,0x22,
|
||||
0x21,0x18,0x23,0x19,0x24,0x32,0x31,0x25,0x33,0x38,0x37,0x34,
|
||||
0x35,0x36,0x39,0x79,0x57,0x58,0x59,0x28,0x56,0x78,0x27,0x41,
|
||||
0x29,0x77,0x26,0x42,0x76,(byte) 0x99,0x1a,0x55,(byte) 0x98,(byte) 0x97,(byte) 0xf9,0x48,
|
||||
0x54,(byte) 0x96,(byte) 0x89,0x47,(byte) 0xb7,0x49,(byte) 0xfa,0x75,0x68,(byte) 0xb6,0x67,0x69,
|
||||
(byte) 0xb9,(byte) 0xb8,(byte) 0xd8,0x52,(byte) 0xd7,(byte) 0x88,(byte) 0xb5,0x74,0x51,0x46,(byte) 0xd9,(byte) 0xf8,
|
||||
0x3a,(byte) 0xd6,(byte) 0x87,0x45,0x7a,(byte) 0x95,(byte) 0xd5,(byte) 0xf6,(byte) 0x86,(byte) 0xb4,(byte) 0xa9,(byte) 0x94,
|
||||
0x53,0x2a,(byte) 0xa8,0x43,(byte) 0xf5,(byte) 0xf7,(byte) 0xd4,0x66,(byte) 0xa7,0x5a,0x44,(byte) 0x8a,
|
||||
(byte) 0xc9,(byte) 0xe8,(byte) 0xc8,(byte) 0xe7,(byte) 0x9a,0x6a,0x73,0x4a,0x61,(byte) 0xc7,(byte) 0xf4,(byte) 0xc6,
|
||||
0x65,(byte) 0xe9,0x72,(byte) 0xe6,0x71,(byte) 0x91,(byte) 0x93,(byte) 0xa6,(byte) 0xda,(byte) 0x92,(byte) 0x85,0x62,
|
||||
(byte) 0xf3,(byte) 0xc5,(byte) 0xb2,(byte) 0xa4,(byte) 0x84,(byte) 0xba,0x64,(byte) 0xa5,(byte) 0xb3,(byte) 0xd2,(byte) 0x81,(byte) 0xe5,
|
||||
(byte) 0xd3,(byte) 0xaa,(byte) 0xc4,(byte) 0xca,(byte) 0xf2,(byte) 0xb1,(byte) 0xe4,(byte) 0xd1,(byte) 0x83,0x63,(byte) 0xea,(byte) 0xc3,
|
||||
(byte) 0xe2,(byte) 0x82,(byte) 0xf1,(byte) 0xa3,(byte) 0xc2,(byte) 0xa1,(byte) 0xc1,(byte) 0xe3,(byte) 0xa2,(byte) 0xe1,(byte) 0xff,(byte) 0xff }
|
||||
};
|
||||
|
||||
private void init_tables(int table) {
|
||||
if (table > 2) table = 2;
|
||||
// memset( first_decode, 0, sizeof first_decode);
|
||||
// memset(second_decode, 0, sizeof second_decode);
|
||||
// make_decoder( first_decode, first_tree[table], 0);
|
||||
// make_decoder(second_decode, second_tree[table], 0);
|
||||
|
||||
for (int i = 0; i < first_decode.length; i++) {
|
||||
first_decode[i] = new Decode();
|
||||
}
|
||||
for (int i = 0; i < second_decode.length; i++) {
|
||||
second_decode[i] = new Decode();
|
||||
}
|
||||
|
||||
make_decoder( first_decode, 0, first_tree[table], 0);
|
||||
make_decoder(second_decode, 0, second_tree[table], 0);
|
||||
}
|
||||
|
||||
//#if 0
|
||||
// writebits (int val, int nbits)
|
||||
// {
|
||||
// val <<= 32 - nbits;
|
||||
// while (nbits--) {
|
||||
// putchar(val & 0x80000000 ? '1':'0');
|
||||
// val <<= 1;
|
||||
// }
|
||||
// }
|
||||
//#endif
|
||||
|
||||
/*
|
||||
getbits(-1) initializes the buffer
|
||||
getbits(n) where 0 <= n <= 25 returns an n-bit integer
|
||||
*/
|
||||
private int bitbuf=0;
|
||||
private int vbits=0;
|
||||
|
||||
int getbits(int nbits) throws IOException {
|
||||
int c;
|
||||
|
||||
if (nbits == 0) return 0;
|
||||
|
||||
int ret;
|
||||
|
||||
if (nbits == -1) {
|
||||
ret = bitbuf = vbits = 0;
|
||||
}
|
||||
else {
|
||||
// ret = bitbuf << (32 - vbits) >> (32 - nbits);
|
||||
ret = bitbuf << (32 - vbits) >>> (32 - nbits);
|
||||
vbits -= nbits;
|
||||
}
|
||||
while (vbits < 25) {
|
||||
// c=fgetc(ifp);
|
||||
c=imageInput.readUnsignedByte();
|
||||
bitbuf = (bitbuf << 8) + c;
|
||||
// if (c == 0xff) fgetc(ifp); /* always extra 00 after ff */
|
||||
if (c == 0xff) {
|
||||
imageInput.readUnsignedByte(); // always extra 00 after ff
|
||||
}
|
||||
vbits += 8;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
short[] decode() throws IOException {
|
||||
short[] result = new short[width * height];
|
||||
|
||||
// struct Decode *decode, *dindex;
|
||||
Decode decode;
|
||||
Decode dindex;
|
||||
int i, j, leaf, len, diff, r;
|
||||
long save;
|
||||
int[] diffbuf = new int[64]; // h * w = 8 * 8 for each compressed block
|
||||
int carry=0, column=0;
|
||||
int[] base = new int[2];
|
||||
// unsigned short outbuf[64];
|
||||
short[] outbuf = new short[64];
|
||||
|
||||
int c;
|
||||
|
||||
boolean lowbits = canonHasLowbits();
|
||||
|
||||
init_tables(table);
|
||||
|
||||
// fseek (ifp, 540 + lowbits*height*width/4, SEEK_SET);
|
||||
// imageInput.seek(540 + (lowbits ? 1 : 0) * height * width / 4);
|
||||
imageInput.seek((lowbits ? 1 : 0) * height * width / 4); // NOTE: The original 540 offset is probably CIFF header length: 26 + DecoderTable[2]: 514
|
||||
getbits(-1); /* Prime the bit buffer */
|
||||
|
||||
while (column < width * height) {
|
||||
// memset(diffbuf,0,sizeof diffbuf);
|
||||
Arrays.fill(diffbuf, 0);
|
||||
|
||||
// decode = first_decode;
|
||||
decode = first_decode[0];
|
||||
for (i = 0; i < 64; i++) {
|
||||
|
||||
// for (dindex=decode; dindex->branch[0]; )
|
||||
// dindex = dindex->branch[getbits(1)];
|
||||
for (dindex = decode; dindex.branch[0] != null; ) {
|
||||
dindex = dindex.branch[getbits(1)];
|
||||
}
|
||||
|
||||
// leaf = dindex->leaf;
|
||||
leaf = dindex.leaf;
|
||||
// decode = second_decode;
|
||||
decode = second_decode[0];
|
||||
|
||||
if (leaf == 0 && i != 0) {
|
||||
break;
|
||||
}
|
||||
if (leaf == 0xff) {
|
||||
continue;
|
||||
}
|
||||
|
||||
i += (leaf >> 4);
|
||||
len = leaf & 15;
|
||||
|
||||
if (len == 0) {
|
||||
continue;
|
||||
}
|
||||
diff = getbits(len);
|
||||
if ((diff & (1 << (len - 1))) == 0) {
|
||||
diff -= (1 << len) - 1;
|
||||
}
|
||||
|
||||
if (i < 64) {
|
||||
diffbuf[i] = diff;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Ok ---
|
||||
|
||||
diffbuf[0] += carry;
|
||||
carry = diffbuf[0];
|
||||
|
||||
for (i = 0; i < 64; i++) {
|
||||
if (column++ % width == 0) {
|
||||
base[0] = base[1] = 512;
|
||||
}
|
||||
|
||||
// TODO: The original C code uses unsigned short, so this may overflow differently
|
||||
// outbuf[i] = ( base[i & 1] += diffbuf[i] );
|
||||
|
||||
outbuf[i] = (short) (base[i & 1] += diffbuf[i]);
|
||||
}
|
||||
|
||||
// -- Ok ---
|
||||
|
||||
if (lowbits) {
|
||||
// save = ftell(ifp);
|
||||
save = imageInput.getStreamPosition();
|
||||
// fseek (ifp, (column-64)/4 + 26, SEEK_SET);
|
||||
// imageInput.seek((column - 64) / 4 + 26);
|
||||
imageInput.seek((column - 64) / 4); // Note: The original 26 is CIFF header length (?)
|
||||
for (i = j = 0; j < 64 / 4; j++) {
|
||||
// c = fgetc(ifp);
|
||||
c = imageInput.readUnsignedByte();
|
||||
for (r = 0; r < 8; r += 2) {
|
||||
// TODO: Is this correct? The original C code is on one line, the Java equivalent then throws an AIOOBE...
|
||||
// outbuf[i++] = (outbuf[i] << 2) + ((c >> r) & 3);
|
||||
short sample = (short) ((outbuf[i] << 2) + ((c >> r) & 3));
|
||||
outbuf[i++] = sample;
|
||||
}
|
||||
}
|
||||
// fseek (ifp, save, SEEK_SET);
|
||||
imageInput.seek(save);
|
||||
}
|
||||
|
||||
// fwrite(outbuf,2,64,stdout);
|
||||
System.arraycopy(outbuf, 0, result, column - 64, 64);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+294
@@ -0,0 +1,294 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name "TwelveMonkeys" nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.crw;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFFReader;
|
||||
import com.twelvemonkeys.imageio.plugins.crw.ciff.CIFF;
|
||||
import com.twelvemonkeys.imageio.plugins.crw.ciff.CIFFDirectory;
|
||||
import com.twelvemonkeys.imageio.plugins.crw.ciff.CIFFEntry;
|
||||
import com.twelvemonkeys.imageio.plugins.crw.ciff.CIFFReader;
|
||||
import com.twelvemonkeys.imageio.stream.BufferedImageInputStream;
|
||||
import com.twelvemonkeys.imageio.stream.SubImageInputStream;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Canon CRW RAW ImageReader.
|
||||
* <p/>
|
||||
*
|
||||
* @see <a href="http://cybercom.net/~dcoffin/dcraw/">Decoding raw digital photos in Linux</a>
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author CRW C reference decoder written by Dave Coffin.
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: CRWImageReader.java,v 1.0 07.04.14 21:31 haraldk Exp$
|
||||
*/
|
||||
public final class CRWImageReader extends ImageReaderBase {
|
||||
// TODO: Avoid duped code from TIFFImageReader, create a ExifRAWBaseImageReader something
|
||||
// TODO: Probably a good idea to move some of the getAsShort/Int/Long/Array to TIFF/EXIF metadata module
|
||||
// TODO: Automatic EXIF rotation, if we find a good way to do that for JPEG/EXIF/TIFF and keeping the metadata sane...
|
||||
|
||||
final static boolean DEBUG = true; //"true".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.imageio.plugins.crw.debug"));
|
||||
|
||||
// TODO: Thumbnail may or may not be present
|
||||
|
||||
private CIFFDirectory heap;
|
||||
private boolean hasJPEGPreview;
|
||||
private boolean hasRAWData;
|
||||
|
||||
CRWImageReader(final ImageReaderSpi provider) {
|
||||
super(provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resetMembers() {
|
||||
heap = null;
|
||||
hasJPEGPreview = false;
|
||||
hasRAWData = false;
|
||||
}
|
||||
|
||||
private void readMetadata() throws IOException {
|
||||
if (imageInput == null) {
|
||||
throw new IllegalStateException("input not set");
|
||||
}
|
||||
|
||||
if (heap == null) {
|
||||
heap = new CIFFReader().read(imageInput);
|
||||
|
||||
hasJPEGPreview = heap.getEntryById(CIFF.TAG_JPEG_PREVIEW) != null;
|
||||
hasRAWData = heap.getEntryById(CIFF.TAG_RAW_DATA) != null;
|
||||
|
||||
if (DEBUG) {
|
||||
System.err.println("directory: " + heap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumImages(final boolean allowSearch) throws IOException {
|
||||
readMetadata();
|
||||
|
||||
return (hasJPEGPreview ? 1 : 0) + (hasRAWData ? 1 : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumThumbnails(int imageIndex) throws IOException {
|
||||
readMetadata();
|
||||
checkBounds(imageIndex);
|
||||
|
||||
// TODO: Count thumbnails!
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean readerSupportsThumbnails() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getThumbnailWidth(int imageIndex, int thumbnailIndex) throws IOException {
|
||||
readMetadata();
|
||||
checkBounds(imageIndex);
|
||||
|
||||
// TODO: Need to get from JPEGImageReader (no ImageWidth tag), this is an ok (but lame) implementation for now
|
||||
return super.getThumbnailWidth(imageIndex, thumbnailIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getThumbnailHeight(int imageIndex, int thumbnailIndex) throws IOException {
|
||||
readMetadata();
|
||||
checkBounds(imageIndex);
|
||||
|
||||
// TODO: Need to get from JPEGImageReader (no ImageHeight tag), this is an ok (but lame) implementation for now
|
||||
return super.getThumbnailHeight(imageIndex, thumbnailIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedImage readThumbnail(int imageIndex, int thumbnailIndex) throws IOException {
|
||||
readMetadata();
|
||||
|
||||
if (imageIndex != 0) {
|
||||
throw new IndexOutOfBoundsException("No thumbnail for imageIndex: " + imageIndex);
|
||||
}
|
||||
if (thumbnailIndex >= getNumThumbnails(0)) {
|
||||
throw new IndexOutOfBoundsException("thumbnailIndex out of bounds: " + thumbnailIndex);
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException("readThumbnail");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth(int imageIndex) throws IOException {
|
||||
readMetadata();
|
||||
|
||||
if (imageIndex == 0 && hasJPEGPreview) {
|
||||
return getJPEGPreviewDimension().width;
|
||||
}
|
||||
|
||||
return getRawImageDimension().width;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight(int imageIndex) throws IOException {
|
||||
readMetadata();
|
||||
|
||||
if (imageIndex == 0 && hasJPEGPreview) {
|
||||
return getJPEGPreviewDimension().height;
|
||||
}
|
||||
|
||||
return getRawImageDimension().height;
|
||||
}
|
||||
|
||||
private Dimension getJPEGPreviewDimension() {
|
||||
// TODO: This is incorrect for the G1 sample, which stores the RAW size here...
|
||||
CIFFDirectory imageProperties = heap.getSubDirectory(CIFF.TAG_IMAGE_PROPERTIES);
|
||||
CIFFEntry imageSpec = imageProperties.getEntryById(CIFF.TAG_IMAGE_SPEC);
|
||||
int[] imageSpecValue = (int[]) imageSpec.getValue();
|
||||
|
||||
return new Dimension(imageSpecValue[0], imageSpecValue[1]);
|
||||
}
|
||||
|
||||
private Dimension getRawImageDimension() {
|
||||
CIFFDirectory exifInfo = getExifInfo();
|
||||
CIFFEntry sensorInfo = exifInfo.getEntryById(CIFF.TAG_SENSOR_INFO);
|
||||
|
||||
if (sensorInfo != null) {
|
||||
short[] sensorInfoValue = (short[]) sensorInfo.getValue();
|
||||
|
||||
return new Dimension(sensorInfoValue[1], sensorInfoValue[2]);
|
||||
}
|
||||
|
||||
// PowerShot Pro70 et al, don't have a JPEG preview and contains the dimensions in the ImageSpec tag
|
||||
return getJPEGPreviewDimension();
|
||||
}
|
||||
|
||||
private CIFFDirectory getExifInfo() {
|
||||
CIFFDirectory imageProperties = heap.getSubDirectory(CIFF.TAG_IMAGE_PROPERTIES);
|
||||
return imageProperties.getSubDirectory(CIFF.TAG_EXIF_INFORMATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IOException {
|
||||
readMetadata();
|
||||
|
||||
if (imageIndex == 0 && hasJPEGPreview) {
|
||||
BufferedImage image = readJPEGPreview();
|
||||
return singletonList(ImageTypeSpecifier.createFromRenderedImage(image)).iterator();
|
||||
}
|
||||
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedImage read(int imageIndex, ImageReadParam param) throws IOException {
|
||||
readMetadata();
|
||||
|
||||
if (imageIndex == 0 && hasJPEGPreview) {
|
||||
return readJPEGPreview();
|
||||
}
|
||||
|
||||
return readRAWData();
|
||||
}
|
||||
|
||||
private BufferedImage readJPEGPreview() throws IOException {
|
||||
CIFFEntry jpegPreview = heap.getEntryById(CIFF.TAG_JPEG_PREVIEW);
|
||||
|
||||
imageInput.seek(jpegPreview.offset());
|
||||
|
||||
return ImageIO.read(new SubImageInputStream(imageInput, jpegPreview.length()));
|
||||
}
|
||||
|
||||
private BufferedImage readRAWData() throws IOException {
|
||||
CIFFDirectory exifInfo = getExifInfo();
|
||||
CIFFEntry decoderTable = exifInfo.getEntryById(CIFF.TAG_DECODER_TABLE);
|
||||
int[] decoderTableValue = decoderTable != null ? (int[]) decoderTable.getValue() : null;
|
||||
int table = decoderTableValue != null ? decoderTableValue[0] : 0;
|
||||
long offset = decoderTableValue != null ? decoderTableValue[2] : 0;
|
||||
|
||||
Dimension size = getRawImageDimension();
|
||||
int width = size.width;
|
||||
int height = size.height;
|
||||
|
||||
// TODO: This is probably not the best way to get the bits/pixel, but seems to be correct (when it's there).
|
||||
// However,
|
||||
CIFFEntry whiteSample = exifInfo.getEntryById(CIFF.TAG_WHITE_SAMPLE);
|
||||
short[] whiteSampleValue = whiteSample != null ? (short[]) whiteSample.getValue() : null;
|
||||
short bitsPerSample = whiteSampleValue != null && whiteSample.length() > 5 ? whiteSampleValue[5] : 12;
|
||||
|
||||
CIFFEntry rawData = heap.getEntryById(CIFF.TAG_RAW_DATA);
|
||||
|
||||
imageInput.seek(rawData.offset() + offset);
|
||||
|
||||
ImageInputStream stream = new BufferedImageInputStream(new SubImageInputStream(imageInput, rawData.length()));
|
||||
|
||||
CRWDecoder decoder = new CRWDecoder(stream, width, height, table);
|
||||
short[] data = decoder.decode();
|
||||
|
||||
DataBuffer dataBuffer = new DataBufferUShort(data, data.length);
|
||||
WritableRaster raster = Raster.createInterleavedRaster(dataBuffer, width, height, width, 1, new int[] {0}, null);
|
||||
|
||||
ColorModel cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), new int[]{bitsPerSample}, false, false, Transparency.OPAQUE, dataBuffer.getDataType());
|
||||
|
||||
return new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
CRWImageReader reader = new CRWImageReader(new CRWImageReaderSpi());
|
||||
|
||||
for (String arg : args) {
|
||||
ImageInputStream stream = ImageIO.createImageInputStream(new File(arg));
|
||||
System.err.println("canDecode: " + reader.getOriginatingProvider().canDecodeInput(stream));
|
||||
|
||||
reader.setInput(stream);
|
||||
|
||||
int numImages = reader.getNumImages(true);
|
||||
for (int i = 0; i < numImages; i++) {
|
||||
int numThumbnails = reader.getNumThumbnails(i);
|
||||
for (int n = 0; n < numThumbnails; n++) {
|
||||
showIt(reader.readThumbnail(i, n), arg + " image " + i + " thumbnail " + n);
|
||||
}
|
||||
|
||||
showIt(reader.read(i), arg + " image " + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name "TwelveMonkeys" nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.crw;
|
||||
|
||||
import static java.util.Arrays.copyOfRange;
|
||||
|
||||
import com.twelvemonkeys.imageio.plugins.crw.ciff.CIFF;
|
||||
import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
|
||||
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.spi.ServiceRegistry;
|
||||
|
||||
/**
|
||||
* CRWImageReaderSpi
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: CRWImageReaderSpi.java,v 1.0 07.04.14 21:26 haraldk Exp$
|
||||
*/
|
||||
public final class CRWImageReaderSpi extends ImageReaderSpiBase {
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public CRWImageReaderSpi() {
|
||||
super(new CRWProviderInfo());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRegistration(ServiceRegistry registry, Class<?> category) {
|
||||
// TODO: This code assumes that any TIFFImageReaderSpi is already installed... It may be installed at a later time. :-(
|
||||
// Make sure we're ordered before any TIFF reader
|
||||
Iterator<ImageReaderSpi> spis = registry.getServiceProviders(ImageReaderSpi.class, new ServiceRegistry.Filter() {
|
||||
@Override
|
||||
public boolean filter(Object provider) {
|
||||
return provider instanceof ImageReaderSpi && isTIFFReaderSpi((ImageReaderSpi) provider);
|
||||
}
|
||||
|
||||
private boolean isTIFFReaderSpi(final ImageReaderSpi provider) {
|
||||
String[] formatNames = provider.getFormatNames();
|
||||
for (String formatName : formatNames) {
|
||||
if (formatName.equalsIgnoreCase("TIFF")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}, true);
|
||||
|
||||
while (spis.hasNext()) {
|
||||
ImageReaderSpi spi = spis.next();
|
||||
registry.setOrdering(ImageReaderSpi.class, this, spi);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canDecodeInput(final Object pSource) throws IOException {
|
||||
if (!(pSource instanceof ImageInputStream)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ImageInputStream stream = (ImageInputStream) pSource;
|
||||
|
||||
stream.mark();
|
||||
try {
|
||||
byte[] bom = new byte[2];
|
||||
stream.readFully(bom);
|
||||
|
||||
ByteOrder originalOrder = stream.getByteOrder();
|
||||
|
||||
try {
|
||||
// CIFF byte order mark II (Intel) or MM (Motorola), just like TIFF
|
||||
if (bom[0] == 'I' && bom[1] == 'I') {
|
||||
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
}
|
||||
else if (bom[0] == 'M' && bom[1] == 'M') {
|
||||
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
||||
// CIFF header is always 26 bytes
|
||||
int size = stream.readInt();
|
||||
if (size != CIFF.HEADER_SIZE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// CRW uses type HEAP and subtype CCDR
|
||||
byte[] type = new byte[8];
|
||||
stream.readFully(type);
|
||||
|
||||
if (!Arrays.equals(CIFF.TYPE_HEAP, copyOfRange(type, 0, 4))
|
||||
|| !Arrays.equals(CIFF.SUBTYPE_CCDR, copyOfRange(type, 4, 8))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Version 1.2
|
||||
return stream.readUnsignedInt() == CIFF.VERSION_1_2;
|
||||
}
|
||||
finally {
|
||||
stream.setByteOrder(originalOrder);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
stream.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CRWImageReader createReaderInstance(Object extension) {
|
||||
return new CRWImageReader(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription(Locale locale) {
|
||||
return "Canon RAW (CRW) format Reader";
|
||||
}
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package com.twelvemonkeys.imageio.plugins.crw;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
||||
|
||||
/**
|
||||
* CRWProviderInfo
|
||||
*/
|
||||
final class CRWProviderInfo extends ReaderWriterProviderInfo {
|
||||
CRWProviderInfo() {
|
||||
super(
|
||||
CRWProviderInfo.class,
|
||||
new String[] {"crw", "CRW"},
|
||||
new String[] {"crw"},
|
||||
new String[] {
|
||||
"image/x-canon-raw", // TODO: Look up
|
||||
},
|
||||
"CRWImageReader",
|
||||
new String[] {"CRWImageReaderSpi"},
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
null, null,
|
||||
null, null,
|
||||
true,
|
||||
null, null,
|
||||
null, null
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oleg Ermolaev
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name "TwelveMonkeys" nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.crw;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* For CR2 RAW image.
|
||||
*
|
||||
* @author Oleg Ermolaev Date: 05.05.2018 2:04
|
||||
*/
|
||||
final class Slice {
|
||||
private final int firstWidthCount;
|
||||
private final int firstWidth;
|
||||
private final int lastWidth;
|
||||
|
||||
private Slice(int firstWidthCount, int firstWidth, int lastWidth) {
|
||||
this.firstWidthCount = firstWidthCount;
|
||||
this.firstWidth = firstWidth;
|
||||
this.lastWidth = lastWidth;
|
||||
}
|
||||
|
||||
static Slice createSlice(long[] values) throws IIOException {
|
||||
if (values == null || values.length != 3) {
|
||||
throw new IIOException("Unexpected slices array: " + Arrays.toString(values));
|
||||
}
|
||||
|
||||
long firstWidthCount = values[0];
|
||||
long firstWidth = values[1];
|
||||
long lastWidth = values[2];
|
||||
|
||||
if (!(0 < firstWidthCount && firstWidthCount <= Integer.MAX_VALUE) ||
|
||||
!(0 < firstWidth && firstWidth <= Integer.MAX_VALUE) ||
|
||||
!(0 < lastWidth && lastWidth <= Integer.MAX_VALUE) ||
|
||||
firstWidthCount * firstWidth + lastWidth > Integer.MAX_VALUE) {
|
||||
throw new IIOException("Unexpected slices array: " + Arrays.toString(values));
|
||||
}
|
||||
|
||||
return new Slice((int) firstWidthCount, (int) firstWidth, (int) lastWidth);
|
||||
}
|
||||
|
||||
private int getWidth() {
|
||||
return firstWidthCount * firstWidth + lastWidth;
|
||||
}
|
||||
|
||||
int[] unslice(int[][] data, int componentCount, int height) throws IIOException {
|
||||
final int width = getWidth();
|
||||
final int[] result = new int[width * height];
|
||||
|
||||
for (int componentIndex = 0; componentIndex < componentCount; componentIndex++) {
|
||||
if (result.length != data[componentIndex].length * componentCount) {
|
||||
throw new IIOException(String.format("Invalid array size for component #%d", componentIndex));
|
||||
}
|
||||
}
|
||||
|
||||
int position = 0;
|
||||
int currentWidth = firstWidth / componentCount;
|
||||
|
||||
for (int sliceIndex = 0; sliceIndex < firstWidthCount + 1; ++sliceIndex) {
|
||||
if (sliceIndex == firstWidthCount) {
|
||||
currentWidth = lastWidth / componentCount;
|
||||
}
|
||||
|
||||
final int sliceOffset = sliceIndex * firstWidth;
|
||||
for (int y = 0; y < height; ++y) {
|
||||
final int yOffset = y * width;
|
||||
|
||||
for (int x = 0; x < currentWidth; ++x) {
|
||||
final int xOffset = x * componentCount;
|
||||
|
||||
for (int componentIndex = 0; componentIndex < componentCount; componentIndex++) {
|
||||
result[sliceOffset + yOffset + xOffset + componentIndex] = data[componentIndex][position];
|
||||
}
|
||||
|
||||
position++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
package com.twelvemonkeys.imageio.plugins.crw.ciff;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* CIFF
|
||||
*
|
||||
* @see <a href="http://xyrion.org/ciff/CIFFspecV1R04.pdf">CIFF: Specification on Image Data File</a>
|
||||
*/
|
||||
public interface CIFF {
|
||||
|
||||
int HEADER_SIZE = 26;
|
||||
int VERSION_1_2 = 0x00010002;
|
||||
|
||||
byte[] TYPE_HEAP = "HEAP".getBytes(StandardCharsets.US_ASCII);
|
||||
|
||||
// According to CIFF spec, other subtypes are "JPGM", "TIFP" and "ARCH".
|
||||
byte[] SUBTYPE_ARCH = "ARCH".getBytes(StandardCharsets.US_ASCII);
|
||||
byte[] SUBTYPE_CCDR = "CCDR".getBytes(StandardCharsets.US_ASCII);
|
||||
byte[] SUBTYPE_JPGM = "JPGM".getBytes(StandardCharsets.US_ASCII);
|
||||
byte[] SUBTYPE_TIFP = "TIFP".getBytes(StandardCharsets.US_ASCII);
|
||||
|
||||
int STORAGE_HEAP = 0x0000; // kStg_InHeapSpace
|
||||
int STORAGE_RECORD = 0x4000; // kStg_InRecordEntry
|
||||
|
||||
int DATA_TYPE_BYTE = 0x0000;
|
||||
int DATA_TYPE_ASCII = 0x0800;
|
||||
int DATA_TYPE_WORD = 0x1000;
|
||||
int DATA_TYPE_DWORD = 0x1800;
|
||||
int DATA_TYPE_UNDEFINED = 0x2000; // kDT_BYTE2
|
||||
|
||||
int DATA_TYPE_HEAP_1 = 0x2800; // kDT_HeapTypeProperty1
|
||||
int DATA_TYPE_HEAP_2 = 0x3000; // kDT_HeapTypeProperty2
|
||||
|
||||
// From Spec
|
||||
int TAG_WILDCARD = 0xffff;
|
||||
int TAG_NULL = 0x0000; // null record
|
||||
int TAG_FREE = 0x0001; // free record
|
||||
int TAG_EX_USED = 0x0002; // special type for implementation purpose
|
||||
|
||||
int TAG_DESCRIPTION = DATA_TYPE_ASCII | 0x0005;
|
||||
int TAG_MODEL_NAME = DATA_TYPE_ASCII | 0x000a;
|
||||
int TAG_FIRMWARE_VERSION = DATA_TYPE_ASCII | 0x000b;
|
||||
int TAG_COMPONENT_VERSION = DATA_TYPE_ASCII | 0x000c;
|
||||
int TAG_ROM_OPERATION_MODE = DATA_TYPE_ASCII | 0x000d;
|
||||
int TAG_OWNER_NAME = DATA_TYPE_ASCII | 0x0010;
|
||||
int TAG_IMAGE_FILE_NAME = DATA_TYPE_ASCII | 0x0016;
|
||||
int TAG_THUMBNAIL_FILE_NAME = DATA_TYPE_ASCII | 0x0017;
|
||||
int TAG_TARGET_IMAGE_TYPE = DATA_TYPE_WORD | 0x000a;
|
||||
int TAG_SR_RELEASE_METHOD = DATA_TYPE_WORD | 0x0010;
|
||||
int TAG_SR_RELEASE_TIMING = DATA_TYPE_WORD | 0x0011;
|
||||
int TAG_RELEASE_SETTING = DATA_TYPE_WORD | 0x0016;
|
||||
int TAG_BODY_SENSITIVITY = DATA_TYPE_WORD | 0x001c;
|
||||
int TAG_IMAGE_FORMAT = DATA_TYPE_DWORD | 0x0003;
|
||||
int TAG_RECORD_ID = DATA_TYPE_DWORD | 0x0004;
|
||||
int TAG_SELF_TIMER_TIME = DATA_TYPE_DWORD | 0x0006;
|
||||
int TAG_SR_TARGET_DISTANCE_SETTING = DATA_TYPE_DWORD | 0x0007;
|
||||
int TAG_BODY_ID = DATA_TYPE_DWORD | 0x000b;
|
||||
int TAG_CAPTURED_TIME = DATA_TYPE_DWORD | 0x000e;
|
||||
int TAG_IMAGE_SPEC = DATA_TYPE_DWORD | 0x0010;
|
||||
int TAG_SR_EF = DATA_TYPE_DWORD | 0x0013;
|
||||
int TAG_MI_EV = DATA_TYPE_DWORD | 0x0014;
|
||||
int TAG_SERIAL_NUMBER = DATA_TYPE_DWORD | 0x0017;
|
||||
int TAG_SR_EXPOSURE = DATA_TYPE_DWORD | 0x0018;
|
||||
int TAG_CAMERA_OBJECT = 0x0007 | DATA_TYPE_HEAP_1;
|
||||
int TAG_SHOOTING_RECORD = 0x0002 | DATA_TYPE_HEAP_2;
|
||||
int TAG_MEASURED_INFO = 0x0003 | DATA_TYPE_HEAP_2;
|
||||
int TAG_CAMERA_SPECIFICATiON = 0x0004 | DATA_TYPE_HEAP_2; // kTC_CameraSpecificaiton
|
||||
|
||||
// From Phil Harvey's ExifTool (https://sno.phy.queensu.ca/~phil/exiftool/canon_raw.html)
|
||||
|
||||
// 0x0006 - 0x300b - 8 -
|
||||
int TAG_CANON_COLOR_INFO1 = DATA_TYPE_BYTE | 0x0032; // - Subdir: 0x300b, Exif: -
|
||||
// 0x0036 - 0x300b ? varies -
|
||||
// 0x003f - 0x300b ? 5120 -
|
||||
// 0x0040 - 0x300b ? 256 -
|
||||
// 0x0041 - 0x300b ? 256 -
|
||||
|
||||
// ASCII Strings
|
||||
// int TAG_CANON_FILE_DESCRIPTION = 0x0805; // - Subdir: 0x2804, Exif: -
|
||||
// int TAG_USER_COMMENT = DATA_TYPE_ASCII | 0x0005; // - Subdir: 0x300a, Exif: -
|
||||
// int TAG_CANON_RAW_MAKE_MODEL = DATA_TYPE_ASCII | 0x080a; // - Subdir: 0x2807, Exif: -
|
||||
// int TAG_CANON_FIRMWARE_VERSION = DATA_TYPE_ASCII | 0x080b; // 32 Firmware version. eg) "Firmware Version 1.1.1" - Subdir: 0x3004, Exif: 0x07
|
||||
// int TAG_COMPONENT_VERSION = 0x080c; // ?
|
||||
// int TAG_ROM_OPERATION_MODE = 0x080d; // 4 eg) The string "USA" for 300D's sold in North America - Subdir: 0x3004, Exif: -
|
||||
// int TAG_OWNER_NAME = 0x0810; // 32 Owner's name. eg) "Phil Harvey" - Subdir: 0x2807, Exif: 0x09
|
||||
int TAG_CANON_IMAGE_TYPE = DATA_TYPE_ASCII | 0x0815; // 32 Type of file. eg) "CRW:EOS DIGITAL REBEL CMOS RAW" - Subdir: 0x2804, Exif: 0x06
|
||||
// int TAG_ORIGINAL_FILE_NAME = 0x0816; // 32 Original file name. eg) "CRW_1834.CRW" - Subdir: 0x300a, Exif: -
|
||||
// int TAG_THUMBNAIL_FILE_NAME = 0x0817; // 32 Thumbnail file name. eg) "CRW_1834.THM" - Subdir: 0x300a, Exif: -
|
||||
|
||||
// SHORT (2-Byte Alignmnt)
|
||||
// int TAG_TARGET_IMAGE_TYPE = 0x100a; // 2 0=real-world subject, 1=written document - Subdir: 0x300a, Exif: -
|
||||
// int TAG_SHUTTER_RELEASE_METHOD = 0x1010; // 2 0=single shot, 1=continuous shooting - Subdir: 0x3002, Exif: -
|
||||
// int TAG_SHUTTER_RELEASE_TIMING = 0x1011; // 2 0=priority on shutter, 1=priority on focus - Subdir: 0x3002, Exif: -
|
||||
// 0x1014 - 0x3002 - 8 -
|
||||
// int TAG_RELEASE_SETTING = 0x1016; // 2 - - Subdir: 0x3002, Exif: -
|
||||
int TAG_BASE_ISO = DATA_TYPE_WORD | 0x101c; // 2 The camera body's base ISO sensitivity - Subdir: 0x3004, Exif: -
|
||||
// 0x1026 - 0x300a - 6 -
|
||||
int TAG_CANON_FLASH_INFO = DATA_TYPE_WORD | 0x1028; // 8 Unknown information, flash related - Subdir: 0x300b, Exif: 0x03
|
||||
int TAG_FOCAL_LENGTH = DATA_TYPE_WORD | 0x1029; // 8 Four 16 bit integers: 0) unknown, 1) focal length in mm, 2-3) sensor width and height in units of 1/1000 inch - Subdir: 0x300b, Exif: 0x02
|
||||
int TAG_CANON_SHOT_INFO = DATA_TYPE_WORD | 0x102a; // varies Data block giving shot information - Subdir: 0x300b, Exif: 0x04
|
||||
int TAG_CANON_COLOR_INFO2 = DATA_TYPE_WORD | 0x102c; // 256 Data block of color information (format unknown) - Subdir: 0x300b, Exif: -
|
||||
int TAG_CANON_CAMERA_SETTINGS = DATA_TYPE_WORD | 0x102d; // varies Data block giving camera settings - Subdir: 0x300b, Exif: 0x01
|
||||
int TAG_WHITE_SAMPLE = DATA_TYPE_WORD | 0x1030; // 102 or 118 White sample information with encrypted 8x8 sample data - Subdir: 0x300b, Exif: -
|
||||
int TAG_SENSOR_INFO = DATA_TYPE_WORD | 0x1031; // 34 Sensor size and resolution information - Subdir: 0x300b, Exif: -
|
||||
int TAG_CANON_CUSTOM_FUNCTIONS = DATA_TYPE_WORD | 0x1033; // varies Data block giving Canon custom settings - Subdir: 0x300b, Exif: 0x0f
|
||||
int TAG_CANON_AF_INFO = DATA_TYPE_WORD | 0x1038; // varies Data block giving AF-specific information - Subdir: 0x300b, Exif: 0x12
|
||||
// 0x1039 0x13 0x300b ? 8 -
|
||||
// 0x103c - 0x300b ? 156 -
|
||||
// 0x107f - 0x300b - varies -
|
||||
int TAG_CANON_FILE_INFO = DATA_TYPE_WORD | 0x1093; // 18 Data block giving file-specific information - Subdir: 0x300b, Exif: 0x93
|
||||
// 0x10a8 0xa8 0x300b ? 20 -
|
||||
int TAG_COLOR_BALANCE = DATA_TYPE_WORD | 0x10a9; // 82 Table of 16-bit integers. The first integer (like many other data blocks) is the number of bytes in the record. This is followed by red, green1, green2 and blue levels for WhiteBalance settings: auto, daylight, shade, cloudy, tungsten, fluorescent, flash, custom and kelvin. The final 4 entries appear to be some sort of baseline red, green1, green2 and blue levels. - Subdir: 0x300b, Exif: 0xa9
|
||||
// 0x10aa 0xaa 0x300b ? 10 -
|
||||
// 0x10ad - 0x300b ? 62 -
|
||||
int TAG_COLOR_TEMPERATURE = DATA_TYPE_WORD | 0x10ae; // 2 16-bit integer giving the color temperature - Subdir: 0x300b, Exif: 0xae
|
||||
// 0x10af - 0x300b ? 2 -
|
||||
int TAG_COLOR_SPACE = DATA_TYPE_WORD | 0x10b4; // 2 16-bit integer specifying the color space (1=sRGB, 2=Adobe RGB, 0xffff=uncalibrated) - Subdir: 0x300b, Exif: 0xb4
|
||||
int TAG_RAW_JPEG_INFO = DATA_TYPE_WORD | 0x10b5; // 10 Data block giving embedded JPG information - Subdir: 0x300b, Exif: 0xb5
|
||||
// 0x10c0 0xc0 0x300b ? 26 -
|
||||
// 0x10c1 0xc1 0x300b ? 26 -
|
||||
// 0x10c2 - 0x300b ? 884 -
|
||||
|
||||
// LONG (4-Byte Alignment)
|
||||
// int TAG_IMAGE_FORMAT = 0x1803; // 8 32-bit integer specifying image format (0x20001 for CRW), followed by 32-bit float giving target compression ratio - Subdir: 0x300a, Exif: -
|
||||
// int TAG_RECORD_ID = 0x1804; // 4 The number of pictures taken since the camera was manufactured - Subdir: 0x300a, Exif: -
|
||||
// 0x1805 - 0x3002 - 8 -
|
||||
// int TAG_SELF_TIMER_TIME = 0x1806; // 4 32-bit integer giving self-timer time in milliseconds - Subdir: 0x3002, Exif: -
|
||||
// int TAG_TARGET_DISTANCE_SETTING = 0x1807; // 4 32-bit float giving target distance in mm - Subdir: 0x3002, Exif: -
|
||||
// int TAG_SERIAL_NUMBER = 0x180b; // 4 The camera body number for EOS models. eg) 00560012345 - Subdir: 0x3004, Exif: 0x0c
|
||||
int TAG_TIME_STAMP = DATA_TYPE_DWORD | 0x180e; // 12 32-bit integer giving the time in seconds when the picture was taken, followed by a 32-bit timezone in seconds - Subdir: 0x300a, Exif: -
|
||||
int TAG_IMAGE_INFO = DATA_TYPE_DWORD | 0x1810; // 28 Data block containing image information, including rotation - Subdir: 0x300a, Exif: -
|
||||
// 0x1812 - 0x3004 - 40 -
|
||||
int TAG_FLASH_INFO = DATA_TYPE_DWORD | 0x1813; // 8 Two 32-bit floats: The flash guide number and the flash threshold - Subdir: 0x3002, Exif: -
|
||||
int TAG_MEASURED_EV = DATA_TYPE_DWORD | 0x1814; // 4 32-bit float giving the measured EV - Subdir: 0x3003, Exif: -
|
||||
int TAG_FILE_NUMBER = DATA_TYPE_DWORD | 0x1817; // 4 32-bit integer giving the number of this file. eg) 1181834 - Subdir: 0x300a, Exif: 0x08
|
||||
// int TAG_EXPOSURE_INFO = DATA_TYPE_DWORD | 0x1818; // 12 Three 32-bit floats: Exposure compensation, Tv, Av - Subdir: 0x3002, Exif: -
|
||||
// 0x1819 - 0x300b - 64 -
|
||||
int TAG_CANON_MODEL_ID = DATA_TYPE_DWORD | 0x1834; // 4 Unsigned 32-bit integer giving unique model ID - Subdir: 0x300b, Exif: 0x10
|
||||
int TAG_DECODER_TABLE = DATA_TYPE_DWORD | 0x1835; // 16 RAW decoder table information - Subdir: 0x300b, Exif: -
|
||||
int TAG_SERIAL_NUMBER_FORMAT = DATA_TYPE_DWORD | 0x183b; // 4 32-bit integer (0x90000000=format 1, 0xa0000000=format 2) - Subdir: 0x300b, Exif: 0x15
|
||||
|
||||
// UNDEFINED (Mixed Data Records)
|
||||
int TAG_RAW_DATA = DATA_TYPE_UNDEFINED | 0x2005; // The raw data itself (the bulk of the CRW file) - Subdir: root
|
||||
int TAG_JPEG_PREVIEW = DATA_TYPE_UNDEFINED | 0x2007; // The embedded JPEG image (2048x1360 pixels for the 300D with Canon firmware) - Subdir: root
|
||||
int TAG_THUMBNAIL = DATA_TYPE_UNDEFINED | 0x2008; // Thumbnail image (JPEG, 160x120 pixels) - Subdir: root
|
||||
|
||||
// SubDirectory Blocks
|
||||
int TAG_IMAGE_DESCRIPTION = DATA_TYPE_HEAP_1 | 0x2804; // The image description subdirectory - Subdir: 0x300a
|
||||
// int TAG_CAMERA_OBJECT = 0x2807; // The camera object subdirectory - Subdir: 0x300a
|
||||
// int TAG_SHOOTING_RECORD = 0x3002; // The shooting record subdirectory - Subdir: 0x300a
|
||||
// int TAG_MEASURED_INFO = 0x3003; // The measured information subdirectory - Subdir: 0x300a
|
||||
int TAG_CAMERA_SPECIFICATION = DATA_TYPE_HEAP_2 | 0x3004; // The camera specification subdirectory - Subdir: 0x2807
|
||||
|
||||
int TAG_IMAGE_PROPERTIES = DATA_TYPE_HEAP_2 | 0x300a; // The main subdirectory containing all meta information - Subdir: root
|
||||
int TAG_EXIF_INFORMATION = DATA_TYPE_HEAP_2 | 0x300b; // The subdirectory containing most of the JPEG/TIFF Exif information - Subdir: 0x300a
|
||||
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.twelvemonkeys.imageio.plugins.crw.ciff;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.twelvemonkeys.imageio.metadata.AbstractDirectory;
|
||||
|
||||
/**
|
||||
* CIFFDirectory
|
||||
*/
|
||||
public final class CIFFDirectory extends AbstractDirectory {
|
||||
|
||||
CIFFDirectory(Collection<CIFFEntry> entries) {
|
||||
super(entries);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CIFFEntry getEntryById(Object identifier) {
|
||||
return (CIFFEntry) super.getEntryById(identifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CIFFEntry getEntryByFieldName(String fieldName) {
|
||||
return (CIFFEntry) super.getEntryByFieldName(fieldName);
|
||||
}
|
||||
|
||||
public CIFFDirectory getSubDirectory(int tagId) {
|
||||
CIFFEntry entry = getEntryById(tagId);
|
||||
return entry == null ? null : (CIFFDirectory) entry.getValue();
|
||||
}
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
package com.twelvemonkeys.imageio.plugins.crw.ciff;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import com.twelvemonkeys.imageio.metadata.AbstractEntry;
|
||||
import com.twelvemonkeys.lang.StringUtil;
|
||||
|
||||
/**
|
||||
* CIFFEntry
|
||||
*/
|
||||
public final class CIFFEntry extends AbstractEntry {
|
||||
|
||||
private final long offset;
|
||||
private final long length;
|
||||
|
||||
/**
|
||||
* Creates a CIFFEntry for "in-record" storage.
|
||||
*
|
||||
* @param identifier
|
||||
* @param value
|
||||
*/
|
||||
CIFFEntry(int identifier, Object value) {
|
||||
super(identifier, value);
|
||||
this.offset = -1;
|
||||
this.length = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a CIFFEntry for "in-heap" storage.
|
||||
*
|
||||
* @param identifier
|
||||
* @param value
|
||||
*/
|
||||
CIFFEntry(int identifier, Object value, long offset, long length) {
|
||||
super(identifier, value);
|
||||
this.offset = offset;
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
int tagId() {
|
||||
return (int) getIdentifier();
|
||||
}
|
||||
|
||||
boolean isHeapStorage() {
|
||||
return offset != -1 && length != -1;
|
||||
}
|
||||
|
||||
public long offset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
public long length() {
|
||||
return length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFieldName() {
|
||||
switch ((Integer) getIdentifier()) {
|
||||
case CIFF.TAG_RAW_DATA:
|
||||
return "RawData";
|
||||
case CIFF.TAG_JPEG_PREVIEW:
|
||||
return "JPEGPreview";
|
||||
case CIFF.TAG_THUMBNAIL:
|
||||
return "Thumbnail";
|
||||
case CIFF.TAG_IMAGE_PROPERTIES:
|
||||
return "ImageProperties";
|
||||
case CIFF.TAG_EXIF_INFORMATION:
|
||||
return "ExifInformation";
|
||||
default:
|
||||
Field[] fields = CIFF.class.getFields();
|
||||
|
||||
for (Field field : fields) {
|
||||
try {
|
||||
if (field.getType() == Integer.TYPE && field.getName().startsWith("TAG_")) {
|
||||
if (field.get(null).equals(getIdentifier())) {
|
||||
return StringUtil.lispToCamel(field.getName().substring(4).replace("_", "-").toLowerCase(), true);
|
||||
}
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
// Should never happen, but in case, abort
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected String getNativeIdentifier() {
|
||||
return String.format("0x%04x", (Integer) getIdentifier());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeName() {
|
||||
int dataType = tagId() & 0x3800;
|
||||
|
||||
switch (dataType) {
|
||||
case CIFF.DATA_TYPE_BYTE:
|
||||
return "BYTE";
|
||||
case CIFF.DATA_TYPE_ASCII:
|
||||
return "ASCII";
|
||||
case CIFF.DATA_TYPE_WORD:
|
||||
return "SHORT";
|
||||
case CIFF.DATA_TYPE_DWORD:
|
||||
return "LONG";
|
||||
case CIFF.DATA_TYPE_UNDEFINED:
|
||||
return "UNDEFINED";
|
||||
case CIFF.DATA_TYPE_HEAP_1:
|
||||
case CIFF.DATA_TYPE_HEAP_2:
|
||||
return super.getTypeName();
|
||||
default:
|
||||
throw new IllegalStateException(String.format("Unsupported data type: 0x%04x", dataType));
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user