mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-10-03 23:53:15 -04:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
6
.travis.yml
Normal file
6
.travis.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
language: java
|
||||
jdk:
|
||||
- oraclejdk8
|
||||
- oraclejdk7
|
||||
# Some JPEGImageReader tests fail on OpenJDK, need to investigate/fix before enabling
|
||||
# - openjdk7
|
213
README.md
213
README.md
@@ -1,9 +1,15 @@
|
||||
## Background
|
||||
## Latest
|
||||
|
||||
TwelveMonkeys ImageIO is a collection of plug-ins for Java's ImageIO.
|
||||
Master branch build status: [](https://travis-ci.org/haraldk/TwelveMonkeys)
|
||||
|
||||
TwelveMonkeys ImageIO [3.2.1](http://search.maven.org/#search%7Cga%7C1%7Cg%3Acom.twelvemonkeys*%20AND%20v%3A%223.2.1%22) is released (Dec. 11th, 2015).
|
||||
|
||||
## 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.
|
||||
The main purpose of this project is to provide support for formats not covered by the JDK itself.
|
||||
The main purpose of this project is to provide support for formats not covered by the JRE itself.
|
||||
|
||||
Support for formats is important, both to be able to read data found
|
||||
"in the wild", as well as to maintain access to data in legacy formats.
|
||||
@@ -16,27 +22,38 @@ The goal is to create a set of efficient and robust ImageIO plug-ins, that can b
|
||||
|
||||
Mainstream format support
|
||||
|
||||
#### 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:
|
||||
* 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 )
|
||||
* 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'
|
||||
* JPEGs containing ICC profiles with class other than 'Display'
|
||||
* JPEGs containing ICC profiles that are incompatible with stream data
|
||||
* JPEGs with corrupted ICC profiles
|
||||
* JPEGs with corrupted `ICC_PROFILE` segments
|
||||
* 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
|
||||
* 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
|
||||
* 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)
|
||||
* 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)
|
||||
* 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)
|
||||
* Illegal combinations of JFIF, Exif and Adobe markers, using "unknown" segments in the
|
||||
* 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 in progress:
|
||||
* CMYK JPEGs
|
||||
@@ -46,10 +63,13 @@ Mainstream format support
|
||||
|
||||
* 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).
|
||||
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).
|
||||
|
||||
#### NetPBM Portable Any Map (PNM) *3.1*
|
||||
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
|
||||
@@ -60,8 +80,9 @@ If you are one of the authors, or know one of the authors and/or the current lic
|
||||
* Write support for the following formats:
|
||||
* PPM in 'P6' (binary) format
|
||||
* PAM in 'P7' (binary) format
|
||||
* Standard metadata support
|
||||
|
||||
#### Adobe Photoshop Document (PSD)
|
||||
#### PSD - Adobe Photoshop Document
|
||||
|
||||
* Read support for the following file types:
|
||||
* Monochrome, 1 channel, 1 bit
|
||||
@@ -72,15 +93,16 @@ If you are one of the authors, or know one of the authors and/or the current lic
|
||||
* CMYK, 4-5 channels, 8, 16 and 32 bit
|
||||
* Read support for the following compression types:
|
||||
* Uncompressed
|
||||
* RLE (PackBits)
|
||||
* 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
|
||||
|
||||
#### Aldus/Adobe Tagged Image File Format (TIFF)
|
||||
#### 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
|
||||
@@ -89,27 +111,42 @@ If you are one of the authors, or know one of the authors and/or the current lic
|
||||
* 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)
|
||||
* 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 in progress
|
||||
* Will support writing most "Baseline" TIFF file types
|
||||
* 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
|
||||
|
||||
#### Commodore Amiga/Electronic Arts Interchange File Format (IFF)
|
||||
#### HDR - Radiance High Dynamic Range RGBE Format
|
||||
|
||||
* Legacy format, allows reading popular image from the Commodore Amiga computer.
|
||||
* 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
|
||||
@@ -125,7 +162,7 @@ Legacy formats
|
||||
* Uncompressed
|
||||
* RLE (PackBits)
|
||||
|
||||
#### ZSoft Paintbrush Format (PCX) *3.1*
|
||||
#### 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
|
||||
@@ -135,8 +172,9 @@ Legacy formats
|
||||
* Support for the following compression types:
|
||||
* Uncompressed (experimental)
|
||||
* RLE compressed
|
||||
* Standard metadata support
|
||||
|
||||
#### Apple Mac Paint Picture Format (PICT)
|
||||
#### PICT - Apple Mac Paint Picture Format
|
||||
|
||||
* Legacy format, especially useful for reading OS X clipboard data.
|
||||
* Read support for the following file types:
|
||||
@@ -147,7 +185,7 @@ Legacy formats
|
||||
* Write support for RGB pixel data:
|
||||
* QuickDraw pixmap
|
||||
|
||||
#### Silicon Graphics Image Format (SGI) *3.1*
|
||||
#### SGI - Silicon Graphics Image Format
|
||||
|
||||
* Read support for the following file types:
|
||||
* 1, 2, 3 or 4 channel image data
|
||||
@@ -155,8 +193,9 @@ Legacy formats
|
||||
* Support for the following compression types:
|
||||
* Uncompressed
|
||||
* RLE compressed
|
||||
* Standard metadata support
|
||||
|
||||
#### Truevision TGA Image Format (TGA) *3.1*
|
||||
#### TGA - Truevision TGA Image Format
|
||||
|
||||
* Read support for the following file types:
|
||||
* ColorMapped
|
||||
@@ -165,38 +204,43 @@ Legacy formats
|
||||
* Support for the following compression types:
|
||||
* Uncompressed
|
||||
* RLE compressed
|
||||
* Standard metadata support
|
||||
|
||||
Icon/other formats
|
||||
|
||||
#### Apple Icon Image (ICNS)
|
||||
#### 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)
|
||||
|
||||
#### MS Windows Icon and Cursor Formats (ICO & CUR)
|
||||
#### 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
|
||||
* *3.1* Note: These formats are now part of the BMP plugin
|
||||
|
||||
#### MS Windows Thumbs DB (Thumbs.db)
|
||||
#### Thumbs.db - MS Windows Thumbs DB
|
||||
|
||||
* Read support
|
||||
|
||||
Other formats, using 3rd party libraries
|
||||
|
||||
#### Scalable Vector Graphics (SVG)
|
||||
#### SVG - Scalable Vector Graphics
|
||||
|
||||
* Read-only support using Batik
|
||||
|
||||
#### MS Windows MetaFile (WMF)
|
||||
#### 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+.*
|
||||
|
||||
|
||||
## Basic usage
|
||||
|
||||
@@ -323,11 +367,12 @@ Unless you add `ImageIO.scanForPlugins()` somewhere in your code, the plugins mi
|
||||
I 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) may occur.
|
||||
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 recommended to use the `IIOProviderContextListener` that implements
|
||||
it is *strongly recommended* to use the `IIOProviderContextListener` that implements
|
||||
dynamic loading and unloading of ImageIO plugins for web applications.
|
||||
|
||||
<web-app ...>
|
||||
@@ -343,6 +388,12 @@ dynamic loading and unloading of ImageIO plugins for web applications.
|
||||
|
||||
</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.
|
||||
|
||||
#### Using the ResampleOp
|
||||
|
||||
@@ -388,9 +439,9 @@ Build the project (using [Maven](http://maven.apache.org/download.cgi)):
|
||||
|
||||
$ mvn package
|
||||
|
||||
Currently, the only supported JDK for making a build is Oracle JDK 7.x.
|
||||
Currently, the recommended JDK for making a build is Oracle JDK 7.x or 8.x.
|
||||
|
||||
It's possible to build using OpenJDK, but some tests will 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. To build using JDK 8, you need to pass `-Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider` to revert to the color manangement system used in Java 7.
|
||||
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.
|
||||
|
||||
Because the unit tests needs quite a bit of memory to run, you might have to set the environment variable `MAVEN_OPTS`
|
||||
to give the Java process that runs Maven more memory. I suggest something like `-Xmx512m -XX:MaxPermSize=256m`.
|
||||
@@ -416,7 +467,7 @@ To verify that the JPEG plugin is installed and used at run-time, you could use
|
||||
|
||||
The first line should print:
|
||||
|
||||
reader: com.twelvemonkeys.imageio.jpeg.JPEGImageReader@somehash
|
||||
reader: com.twelvemonkeys.imageio.plugins.jpeg.JPEGImageReader@somehash
|
||||
|
||||
#### Maven dependency example
|
||||
|
||||
@@ -428,12 +479,12 @@ To depend on the JPEG and TIFF plugin using Maven, add the following to your POM
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-jpeg</artifactId>
|
||||
<version>3.0.2</version> <!-- Alternatively, build your own version -->
|
||||
<version>3.2.1</version> <!-- Alternatively, build your own version -->
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-tiff</artifactId>
|
||||
<version>3.0.2</version> <!-- Alternatively, build your own version -->
|
||||
<version>3.2.1</version> <!-- Alternatively, build your own version -->
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
@@ -441,25 +492,66 @@ To depend on the JPEG and TIFF plugin using Maven, add the following to your POM
|
||||
|
||||
To depend on the JPEG and TIFF plugin in your IDE or program, add all of the following JARs to your class path:
|
||||
|
||||
twelvemonkeys-common-lang-3.0.2.jar
|
||||
twelvemonkeys-common-io-3.0.2.jar
|
||||
twelvemonkeys-common-image-3.0.2.jar
|
||||
twelvemonkeys-imageio-core-3.0.2.jar
|
||||
twelvemonkeys-imageio-metadata-3.0.2.jar
|
||||
twelvemonkeys-imageio-jpeg-3.0.2.jar
|
||||
twelvemonkeys-imageio-tiff-3.0.2.jar
|
||||
twelvemonkeys-common-lang-3.2.1.jar
|
||||
twelvemonkeys-common-io-3.2.1.jar
|
||||
twelvemonkeys-common-image-3.2.1.jar
|
||||
twelvemonkeys-imageio-core-3.2.1.jar
|
||||
twelvemonkeys-imageio-metadata-3.2.1.jar
|
||||
twelvemonkeys-imageio-jpeg-3.2.1.jar
|
||||
twelvemonkeys-imageio-tiff-3.2.1.jar
|
||||
|
||||
### Links to prebuilt binaries
|
||||
|
||||
##### Latest version (3.2.x)
|
||||
|
||||
Requires Java 7 or later.
|
||||
|
||||
Common dependencies
|
||||
* [common-lang-3.2.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-lang/3.2.1/common-lang-3.2.1.jar)
|
||||
* [common-io-3.2.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-io/3.2.1/common-io-3.2.1.jar)
|
||||
* [common-image-3.2.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-image/3.2.1/common-image-3.2.1.jar)
|
||||
|
||||
ImageIO dependencies
|
||||
* [imageio-core-3.2.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-core/3.2.1/imageio-core-3.2.1.jar)
|
||||
* [imageio-metadata-3.2.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-metadata/3.2.1/imageio-metadata-3.2.1.jar)
|
||||
|
||||
ImageIO plugins
|
||||
* [imageio-bmp-3.2.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-bmp/3.2.1/imageio-bmp-3.2.1.jar)
|
||||
* [imageio-jpeg-3.2.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jpeg/3.2.1/imageio-jpeg-3.2.1.jar)
|
||||
* [imageio-tiff-3.2.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tiff/3.2.1/imageio-tiff-3.2.1.jar)
|
||||
* [imageio-pnm-3.2.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pnm/3.2.1/imageio-pnm-3.2.1.jar)
|
||||
* [imageio-psd-3.2.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-psd/3.2.1/imageio-psd-3.2.1.jar)
|
||||
* [imageio-hdr-3.2.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-hdr/3.2.1/imageio-hdr-3.2.1.jar)
|
||||
* [imageio-iff-3.2.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-iff/3.2.1/imageio-iff-3.2.1.jar)
|
||||
* [imageio-pcx-3.2.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pcx/3.2.1/imageio-pcx-3.2.1.jar)
|
||||
* [imageio-pict-3.2.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pict/3.2.1/imageio-pict-3.2.1.jar)
|
||||
* [imageio-sgi-3.2.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-sgi/3.2.1/imageio-sgi-3.2.1.jar)
|
||||
* [imageio-tga-3.2.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tga/3.2.1/imageio-tga-3.2.1.jar)
|
||||
* [imageio-icns-3.2.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-icns/3.2.1/imageio-icns-3.2.1.jar)
|
||||
* [imageio-thumbsdb-3.2.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-thumbsdb/3.2.1/imageio-thumbsdb-3.2.1.jar)
|
||||
|
||||
ImageIO plugins requiring 3rd party libs
|
||||
* [imageio-batik-3.2.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-batik/3.2.1/imageio-batik-3.2.1.jar)
|
||||
|
||||
Photoshop Path support for ImageIO
|
||||
* [imageio-clippath-3.2.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-clippath/3.2.1/imageio-clippath-3.2.1.jar)
|
||||
|
||||
Servlet support
|
||||
* [servlet-3.2.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/servlet/servlet/3.2.1/servlet-3.2.1.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*.
|
||||
|
||||
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)
|
||||
|
||||
|
||||
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 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)
|
||||
@@ -469,19 +561,20 @@ ImageIO plugins
|
||||
* [imageio-icns-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-icns/3.0.2/imageio-icns-3.0.2.jar)
|
||||
* [imageio-ico-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-ico/3.0.2/imageio-ico-3.0.2.jar)
|
||||
* [imageio-thumbsdb-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-thumbsdb/3.0.2/imageio-thumbsdb-3.0.2.jar)
|
||||
|
||||
|
||||
ImageIO plugins requiring 3rd party libs
|
||||
* [imageio-batik-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-batik/3.0.2/imageio-batik-3.0.2.jar)
|
||||
* [imageio-jmagick-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jmagick/3.0.2/imageio-jmagick-3.0.2.jar)
|
||||
|
||||
|
||||
Servlet support
|
||||
* [servlet-3.0.2.jar](http://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):
|
||||
|
||||
Copyright (c) 2008-2013, Harald Kuhr
|
||||
Copyright (c) 2008-2015, Harald Kuhr
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -521,7 +614,7 @@ a: The easiest way is to build your own project using Maven, and just add depend
|
||||
|
||||
q: What changes do I have to make to my code in order to use the plug-ins?
|
||||
|
||||
a: The short answer is: None. For basic usage, like ImageIO.read(...) or ImageIO.getImageReaders(...), there is no need
|
||||
a: The short answer is: None. For basic usage, like `ImageIO.read(...)` or `ImageIO.getImageReaders(...)`, there is no need
|
||||
to change your code. Most of the functionality is available through standard ImageIO APIs, and great care has been taken
|
||||
not to introduce extra API where none is necessary.
|
||||
|
||||
@@ -538,11 +631,11 @@ All you have have to do, is to make sure you have the TwelveMonkeys JARs in your
|
||||
|
||||
You can read more about the registry and the lookup mechanism in the [IIORegistry API doc](http://docs.oracle.com/javase/7/docs/api/javax/imageio/spi/IIORegistry.html).
|
||||
|
||||
The fine print: The TwelveMonkeys service providers for TIFF and JPEG overrides the onRegistration method, and
|
||||
utilizes the pairwise partial ordering mechanism of the IIOServiceRegistry to make sure it is installed before
|
||||
the Sun/Oracle provided JPEGImageReader and the Apple provided TIFFImageReader on OS X, 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.
|
||||
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
|
||||
the Sun/Oracle provided `JPEGImageReader` and `BMPImageReader`, and the Apple provided `TIFFImageReader` on OS X,
|
||||
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: What about JAI? Several of the formats are already supported by JAI.
|
||||
|
142
bom/pom.xml
Normal file
142
bom/pom.xml
Normal file
@@ -0,0 +1,142 @@
|
||||
<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">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys</groupId>
|
||||
<artifactId>twelvemonkeys</artifactId>
|
||||
<version>3.3-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>com.twelvemonkeys.bom</groupId>
|
||||
<artifactId>bom</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<name>TwelveMonkeys :: BOM</name>
|
||||
<description>
|
||||
TwelveMonkeys "Bill of Materials" (BOM).
|
||||
</description>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<!-- Common dependencies-->
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common-lang</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common-io</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common-image</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- ImageIO common dependencies -->
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-metadata</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-clippath</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- ImageIO plugins -->
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-bmp</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-hdr</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-icns</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-iff</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-jpeg</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-pcx</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-pdf</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-pict</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-pnm</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-psd</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-sgi</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-tga</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-thumbsdb</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-tiff</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- ImageIO 3rd party dependent plugins -->
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-batik</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Servlet -->
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.servlet</groupId>
|
||||
<artifactId>servlet</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
</project>
|
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>3.1-SNAPSHOT</version>
|
||||
<version>3.3-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>common-image</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
@@ -358,8 +358,9 @@ public final class ImageUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a copy of the given image. The image will have the same
|
||||
* color model and raster type, but will not share image (pixel) data.
|
||||
* Creates a deep copy of the given image. The image will have the same
|
||||
* color model and raster type, but will not share image (pixel) data
|
||||
* with the input image.
|
||||
*
|
||||
* @param pImage the image to clone.
|
||||
*
|
||||
@@ -378,7 +379,7 @@ public final class ImageUtil {
|
||||
cm.createCompatibleWritableRaster(pImage.getWidth(), pImage.getHeight()),
|
||||
cm.isAlphaPremultiplied(), null);
|
||||
|
||||
drawOnto(pImage, img);
|
||||
drawOnto(img, pImage);
|
||||
|
||||
return img;
|
||||
}
|
||||
|
@@ -547,7 +547,7 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
|
||||
|
||||
// TODO: What if output != null and wrong size? Create new? Render on only a part? Document?
|
||||
|
||||
// If filter type != POINT or BOX an input has IndexColorModel, convert
|
||||
// If filter type != POINT or BOX and input has IndexColorModel, convert
|
||||
// to true color, with alpha reflecting that of the original color model.
|
||||
BufferedImage temp;
|
||||
ColorModel cm;
|
||||
@@ -590,7 +590,7 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
|
||||
/*
|
||||
// TODO: This idea from Chet and Romain is actually not too bad..
|
||||
// It reuses the image/raster/graphics...
|
||||
// However, they forget to end with a halve operation..
|
||||
// However, they don't end with a halve operation..
|
||||
private static BufferedImage getFasterScaledInstance(BufferedImage img,
|
||||
int targetWidth, int targetHeight, Object hint,
|
||||
boolean progressiveBilinear) {
|
||||
@@ -895,7 +895,7 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
|
||||
* filter function definitions
|
||||
*/
|
||||
|
||||
static interface InterpolationFilter {
|
||||
interface InterpolationFilter {
|
||||
double filter(double t);
|
||||
|
||||
double support();
|
||||
@@ -1336,7 +1336,7 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
|
||||
}
|
||||
|
||||
//contribX.n = 0;
|
||||
contribX.p = new Contributor[(int) (width * 2.0 + 1.0)];
|
||||
contribX.p = new Contributor[(int) (width * 2.0 + 1.0 + 0.5)];
|
||||
|
||||
center = (double) i / xscale;
|
||||
int left = (int) Math.ceil(center - width);// Note: Assumes width <= .5
|
||||
@@ -1387,7 +1387,7 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
|
||||
else {
|
||||
/* Expanding image */
|
||||
//contribX.n = 0;
|
||||
contribX.p = new Contributor[(int) (fwidth * 2.0 + 1.0)];
|
||||
contribX.p = new Contributor[(int) (fwidth * 2.0 + 1.0 + 0.5)];
|
||||
|
||||
center = (double) i / xscale;
|
||||
int left = (int) Math.ceil(center - fwidth);
|
||||
@@ -1465,7 +1465,7 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
|
||||
|
||||
for (int i = 0; i < dstHeight; i++) {
|
||||
//contribY[i].n = 0;
|
||||
contribY[i].p = new Contributor[(int) (width * 2.0 + 1)];
|
||||
contribY[i].p = new Contributor[(int) (width * 2.0 + 1 + 0.5)];
|
||||
|
||||
double center = (double) i / yscale;
|
||||
int left = (int) Math.ceil(center - width);
|
||||
@@ -1516,7 +1516,7 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
|
||||
else {
|
||||
for (int i = 0; i < dstHeight; ++i) {
|
||||
//contribY[i].n = 0;
|
||||
contribY[i].p = new Contributor[(int) (fwidth * 2 + 1)];
|
||||
contribY[i].p = new Contributor[(int) (fwidth * 2 + 1 + 0.5)];
|
||||
|
||||
double center = (double) i / yscale;
|
||||
double left = Math.ceil(center - fwidth);
|
||||
|
@@ -103,11 +103,6 @@ public class ImageUtilTestCase {
|
||||
// Should have same dimensions
|
||||
assertEquals(scaled.getWidth(null), bufferedScaled.getWidth());
|
||||
assertEquals(scaled.getHeight(null), bufferedScaled.getHeight());
|
||||
|
||||
// Hmmm...
|
||||
assertTrue(new Integer(42).equals(bufferedScaled.getProperty("lucky-number"))
|
||||
|| bufferedScaled.getPropertyNames() == null
|
||||
|| bufferedScaled.getPropertyNames().length == 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@@ -69,7 +69,7 @@ public class ResampleOpTestCase {
|
||||
}
|
||||
|
||||
private void assertResampleBufferedImageTypes(final int pFilterType) {
|
||||
List<String> exceptions = new ArrayList<String>();
|
||||
List<String> exceptions = new ArrayList<>();
|
||||
|
||||
// Test all image types in BufferedImage
|
||||
for (int type = BufferedImage.TYPE_INT_ARGB; type <= BufferedImage.TYPE_BYTE_INDEXED; type++) {
|
||||
@@ -304,6 +304,30 @@ public class ResampleOpTestCase {
|
||||
assertResampleBufferedImageTypes(ResampleOp.FILTER_LANCZOS);
|
||||
}
|
||||
|
||||
// https://github.com/haraldk/TwelveMonkeys/issues/195
|
||||
@Test
|
||||
public void testAIOOBEHeight() {
|
||||
BufferedImage myImage = new BufferedImage(100, 354, BufferedImage.TYPE_INT_ARGB);
|
||||
|
||||
for (int i = 19; i > 0; i--) {
|
||||
ResampleOp resampler = new ResampleOp(100, i, ResampleOp.FILTER_LANCZOS);
|
||||
BufferedImage resizedImage = resampler.filter(myImage, null);
|
||||
assertNotNull(resizedImage);
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/haraldk/TwelveMonkeys/issues/195
|
||||
@Test
|
||||
public void testAIOOBEWidth() {
|
||||
BufferedImage myImage = new BufferedImage(2832, 283, BufferedImage.TYPE_INT_ARGB);
|
||||
|
||||
for (int i = 145; i > 143; i--) {
|
||||
ResampleOp resampler = new ResampleOp(i, 14, ResampleOp.FILTER_LANCZOS);
|
||||
BufferedImage resizedImage = resampler.filter(myImage, null);
|
||||
assertNotNull(resizedImage);
|
||||
}
|
||||
}
|
||||
|
||||
@Ignore("Not for general unit testing")
|
||||
@Test
|
||||
public void testTime() {
|
||||
|
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>3.1-SNAPSHOT</version>
|
||||
<version>3.3-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>common-io</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
@@ -22,7 +22,7 @@
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>common-lang</artifactId>
|
||||
<classifier>tests</classifier>
|
||||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>3.1-SNAPSHOT</version>
|
||||
<version>3.3-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>common-lang</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
@@ -142,7 +142,7 @@ public final class DateUtil {
|
||||
* @param pTime time
|
||||
* @return the time rounded to the closest second.
|
||||
*/
|
||||
public static long roundToSecond(long pTime) {
|
||||
public static long roundToSecond(final long pTime) {
|
||||
return (pTime / SECOND) * SECOND;
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ public final class DateUtil {
|
||||
* @param pTime time
|
||||
* @return the time rounded to the closest minute.
|
||||
*/
|
||||
public static long roundToMinute(long pTime) {
|
||||
public static long roundToMinute(final long pTime) {
|
||||
return (pTime / MINUTE) * MINUTE;
|
||||
}
|
||||
|
||||
@@ -162,9 +162,20 @@ public final class DateUtil {
|
||||
* @param pTime time
|
||||
* @return the time rounded to the closest hour.
|
||||
*/
|
||||
public static long roundToHour(long pTime) {
|
||||
// TODO: What if timezone offset is sub hour? Are there any? I think so...
|
||||
return ((pTime / HOUR) * HOUR);
|
||||
public static long roundToHour(final long pTime) {
|
||||
return roundToHour(pTime, TimeZone.getDefault());
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds the given time down to the closest hour, using the given timezone.
|
||||
*
|
||||
* @param pTime time
|
||||
* @param pTimeZone the timezone to use when rounding
|
||||
* @return the time rounded to the closest hour.
|
||||
*/
|
||||
public static long roundToHour(final long pTime, final TimeZone pTimeZone) {
|
||||
int offset = pTimeZone.getOffset(pTime);
|
||||
return ((pTime / HOUR) * HOUR) - offset;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -173,7 +184,7 @@ public final class DateUtil {
|
||||
* @param pTime time
|
||||
* @return the time rounded to the closest day.
|
||||
*/
|
||||
public static long roundToDay(long pTime) {
|
||||
public static long roundToDay(final long pTime) {
|
||||
return roundToDay(pTime, TimeZone.getDefault());
|
||||
}
|
||||
|
||||
@@ -184,7 +195,7 @@ public final class DateUtil {
|
||||
* @param pTimeZone the timezone to use when rounding
|
||||
* @return the time rounded to the closest day.
|
||||
*/
|
||||
public static long roundToDay(long pTime, TimeZone pTimeZone) {
|
||||
public static long roundToDay(final long pTime, final TimeZone pTimeZone) {
|
||||
int offset = pTimeZone.getOffset(pTime);
|
||||
return (((pTime + offset) / DAY) * DAY) - offset;
|
||||
}
|
||||
|
@@ -204,7 +204,7 @@ public final class Platform {
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/lang/Platform.java#1 $
|
||||
*/
|
||||
public static enum Architecture {
|
||||
public enum Architecture {
|
||||
X86("x86"),
|
||||
I386("i386"),
|
||||
I686("i686"),
|
||||
@@ -215,7 +215,7 @@ public final class Platform {
|
||||
|
||||
final String name;// for debug only
|
||||
|
||||
private Architecture(String pName) {
|
||||
Architecture(String pName) {
|
||||
name = pName;
|
||||
}
|
||||
|
||||
@@ -233,7 +233,7 @@ public final class Platform {
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/lang/Platform.java#1 $
|
||||
*/
|
||||
public static enum OperatingSystem {
|
||||
public enum OperatingSystem {
|
||||
Windows("Windows", "win"),
|
||||
Linux("Linux", "lnx"),
|
||||
Solaris("Solaris", "sun"),
|
||||
@@ -244,7 +244,7 @@ public final class Platform {
|
||||
final String id;
|
||||
final String name;// for debug only
|
||||
|
||||
private OperatingSystem(String pName, String pId) {
|
||||
OperatingSystem(String pName, String pId) {
|
||||
name = pName;
|
||||
id = pId != null ? pId : pName.toLowerCase();
|
||||
}
|
||||
|
@@ -29,11 +29,15 @@
|
||||
package com.twelvemonkeys.lang;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* DateUtilTest
|
||||
@@ -42,9 +46,30 @@ import static org.junit.Assert.*;
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: DateUtilTest.java,v 1.0 11.04.12 16:21 haraldk Exp$
|
||||
*/
|
||||
@RunWith(Parameterized.class)
|
||||
public class DateUtilTest {
|
||||
private static Calendar getCalendar(long time) {
|
||||
Calendar calendar = Calendar.getInstance(TimeZone.getDefault());
|
||||
|
||||
private final TimeZone timeZone;
|
||||
|
||||
@Parameterized.Parameters
|
||||
public static List<Object[]> timeZones() {
|
||||
return Arrays.asList(new Object[][] {
|
||||
{TimeZone.getTimeZone("UTC")},
|
||||
{TimeZone.getTimeZone("CET")},
|
||||
{TimeZone.getTimeZone("IST")}, // 30 min off
|
||||
});
|
||||
}
|
||||
|
||||
public DateUtilTest(final TimeZone timeZone) {
|
||||
this.timeZone = timeZone;
|
||||
}
|
||||
|
||||
private Calendar getCalendar(long time) {
|
||||
return getCalendar(time, TimeZone.getDefault());
|
||||
}
|
||||
|
||||
private Calendar getCalendar(long time, final TimeZone timeZone) {
|
||||
Calendar calendar = Calendar.getInstance(timeZone);
|
||||
calendar.setTimeInMillis(time);
|
||||
|
||||
return calendar;
|
||||
@@ -74,6 +99,15 @@ public class DateUtilTest {
|
||||
assertEquals(0, calendar.get(Calendar.MINUTE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoundToHourTZ() {
|
||||
Calendar calendar = getCalendar(DateUtil.roundToHour(System.currentTimeMillis(), timeZone), timeZone);
|
||||
|
||||
assertEquals(0, calendar.get(Calendar.MILLISECOND));
|
||||
assertEquals(0, calendar.get(Calendar.SECOND));
|
||||
assertEquals(0, calendar.get(Calendar.MINUTE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoundToDay() {
|
||||
Calendar calendar = getCalendar(DateUtil.roundToDay(System.currentTimeMillis()));
|
||||
@@ -84,6 +118,16 @@ public class DateUtilTest {
|
||||
assertEquals(0, calendar.get(Calendar.HOUR_OF_DAY));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoundToDayTZ() {
|
||||
Calendar calendar = getCalendar(DateUtil.roundToDay(System.currentTimeMillis(), timeZone), timeZone);
|
||||
|
||||
assertEquals(0, calendar.get(Calendar.MILLISECOND));
|
||||
assertEquals(0, calendar.get(Calendar.SECOND));
|
||||
assertEquals(0, calendar.get(Calendar.MINUTE));
|
||||
assertEquals(0, calendar.get(Calendar.HOUR_OF_DAY));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCurrentTimeSecond() {
|
||||
Calendar calendar = getCalendar(DateUtil.currentTimeSecond());
|
||||
|
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys</groupId>
|
||||
<artifactId>twelvemonkeys</artifactId>
|
||||
<version>3.1-SNAPSHOT</version>
|
||||
<version>3.3-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
@@ -32,7 +32,7 @@
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>common-lang</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<classifier>tests</classifier>
|
||||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
72
contrib/pom.xml
Normal file
72
contrib/pom.xml
Normal file
@@ -0,0 +1,72 @@
|
||||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys</groupId>
|
||||
<artifactId>twelvemonkeys</artifactId>
|
||||
<version>3.3-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>com.twelvemonkeys.contrib</groupId>
|
||||
<artifactId>contrib</artifactId>
|
||||
<name>TwelveMonkeys :: Contrib</name>
|
||||
<description>
|
||||
Contributions to TwelveMonkeys which are not matching into the ImageIO plug-ins.
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common-lang</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common-io</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common-image</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common-lang</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common-io</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-metadata</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-tiff</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-tiff</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.7</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
@@ -0,0 +1,603 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 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.contrib.tiff;
|
||||
|
||||
import com.twelvemonkeys.imageio.metadata.*;
|
||||
import com.twelvemonkeys.imageio.metadata.exif.EXIFReader;
|
||||
import com.twelvemonkeys.imageio.metadata.exif.EXIFWriter;
|
||||
import com.twelvemonkeys.imageio.metadata.exif.Rational;
|
||||
import com.twelvemonkeys.imageio.metadata.exif.TIFF;
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import javax.imageio.stream.ImageOutputStream;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.image.AffineTransformOp;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* TIFFUtilities for manipulation TIFF Images and Metadata
|
||||
*
|
||||
* @author <a href="mailto:mail@schmidor.de">Oliver Schmidtmer</a>
|
||||
* @author last modified by $Author$
|
||||
* @version $Id$
|
||||
*/
|
||||
public class TIFFUtilities {
|
||||
private TIFFUtilities() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges all pages from the input TIFF files into one TIFF file at the
|
||||
* output location.
|
||||
*
|
||||
* @param inputFiles
|
||||
* @param outputFile
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void merge(List<File> inputFiles, File outputFile) throws IOException {
|
||||
ImageOutputStream output = null;
|
||||
try {
|
||||
output = ImageIO.createImageOutputStream(outputFile);
|
||||
|
||||
for (File file : inputFiles) {
|
||||
ImageInputStream input = null;
|
||||
try {
|
||||
input = ImageIO.createImageInputStream(file);
|
||||
List<TIFFPage> pages = getPages(input);
|
||||
writePages(output, pages);
|
||||
}
|
||||
finally {
|
||||
if (input != null) {
|
||||
input.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (output != null) {
|
||||
output.flush();
|
||||
output.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits all pages from the input TIFF file to one file per page in the
|
||||
* output directory.
|
||||
*
|
||||
* @param inputFile
|
||||
* @param outputDirectory
|
||||
* @return generated files
|
||||
* @throws IOException
|
||||
*/
|
||||
public static List<File> split(File inputFile, File outputDirectory) throws IOException {
|
||||
ImageInputStream input = null;
|
||||
List<File> outputFiles = new ArrayList<>();
|
||||
try {
|
||||
input = ImageIO.createImageInputStream(inputFile);
|
||||
List<TIFFPage> pages = getPages(input);
|
||||
int pageNo = 1;
|
||||
for (TIFFPage tiffPage : pages) {
|
||||
ArrayList<TIFFPage> outputPages = new ArrayList<TIFFPage>(1);
|
||||
ImageOutputStream outputStream = null;
|
||||
try {
|
||||
File outputFile = new File(outputDirectory, String.format("%04d", pageNo) + ".tif");
|
||||
outputStream = ImageIO.createImageOutputStream(outputFile);
|
||||
outputPages.clear();
|
||||
outputPages.add(tiffPage);
|
||||
writePages(outputStream, outputPages);
|
||||
outputFiles.add(outputFile);
|
||||
}
|
||||
finally {
|
||||
if (outputStream != null) {
|
||||
outputStream.flush();
|
||||
outputStream.close();
|
||||
}
|
||||
}
|
||||
++pageNo;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (input != null) {
|
||||
input.close();
|
||||
}
|
||||
}
|
||||
return outputFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates all pages of a TIFF file by changing TIFF.TAG_ORIENTATION.
|
||||
* <p>
|
||||
* NOTICE: TIFF.TAG_ORIENTATION is an advice how the image is meant do be
|
||||
* displayed. Other metadata, such as width and height, relate to the image
|
||||
* as how it is stored. The ImageIO TIFF plugin does not handle orientation.
|
||||
* Use {@link TIFFUtilities#applyOrientation(BufferedImage, int)} for
|
||||
* applying TIFF.TAG_ORIENTATION.
|
||||
* </p>
|
||||
*
|
||||
* @param imageInput
|
||||
* @param imageOutput
|
||||
* @param degree Rotation amount, supports 90<39>, 180<38> and 270<37>.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void rotatePages(ImageInputStream imageInput, ImageOutputStream imageOutput, int degree)
|
||||
throws IOException {
|
||||
rotatePage(imageInput, imageOutput, degree, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates a page of a TIFF file by changing TIFF.TAG_ORIENTATION.
|
||||
* <p>
|
||||
* NOTICE: TIFF.TAG_ORIENTATION is an advice how the image is meant do be
|
||||
* displayed. Other metadata, such as width and height, relate to the image
|
||||
* as how it is stored. The ImageIO TIFF plugin does not handle orientation.
|
||||
* Use {@link TIFFUtilities#applyOrientation(BufferedImage, int)} for
|
||||
* applying TIFF.TAG_ORIENTATION.
|
||||
* </p>
|
||||
*
|
||||
* @param imageInput
|
||||
* @param imageOutput
|
||||
* @param degree Rotation amount, supports 90<39>, 180<38> and 270<37>.
|
||||
* @param pageIndex page which should be rotated or -1 for all pages.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void rotatePage(ImageInputStream imageInput, ImageOutputStream imageOutput, int degree, int pageIndex)
|
||||
throws IOException {
|
||||
ImageInputStream input = null;
|
||||
try {
|
||||
List<TIFFPage> pages = getPages(imageInput);
|
||||
if (pageIndex != -1) {
|
||||
pages.get(pageIndex).rotate(degree);
|
||||
}
|
||||
else {
|
||||
for (TIFFPage tiffPage : pages) {
|
||||
tiffPage.rotate(degree);
|
||||
}
|
||||
}
|
||||
writePages(imageOutput, pages);
|
||||
}
|
||||
finally {
|
||||
if (input != null) {
|
||||
input.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static List<TIFFPage> getPages(ImageInputStream imageInput) throws IOException {
|
||||
ArrayList<TIFFPage> pages = new ArrayList<TIFFPage>();
|
||||
|
||||
CompoundDirectory IFDs = (CompoundDirectory) new EXIFReader().read(imageInput);
|
||||
|
||||
int pageCount = IFDs.directoryCount();
|
||||
for (int pageIndex = 0; pageIndex < pageCount; pageIndex++) {
|
||||
pages.add(new TIFFPage(IFDs.getDirectory(pageIndex), imageInput));
|
||||
}
|
||||
|
||||
return pages;
|
||||
}
|
||||
|
||||
public static void writePages(ImageOutputStream imageOutput, List<TIFFPage> pages) throws IOException {
|
||||
EXIFWriter exif = new EXIFWriter();
|
||||
long nextPagePos = imageOutput.getStreamPosition();
|
||||
if (nextPagePos == 0) {
|
||||
exif.writeTIFFHeader(imageOutput);
|
||||
nextPagePos = imageOutput.getStreamPosition();
|
||||
imageOutput.writeInt(0);
|
||||
}
|
||||
else {
|
||||
// already has pages, so remember place of EOF to replace with
|
||||
// IFD offset
|
||||
nextPagePos -= 4;
|
||||
}
|
||||
|
||||
for (TIFFPage tiffPage : pages) {
|
||||
long ifdOffset = tiffPage.write(imageOutput, exif);
|
||||
|
||||
long tmp = imageOutput.getStreamPosition();
|
||||
imageOutput.seek(nextPagePos);
|
||||
imageOutput.writeInt((int) ifdOffset);
|
||||
imageOutput.seek(tmp);
|
||||
nextPagePos = tmp;
|
||||
imageOutput.writeInt(0);
|
||||
}
|
||||
}
|
||||
|
||||
public static BufferedImage applyOrientation(BufferedImage input, int orientation) {
|
||||
boolean flipExtends = false;
|
||||
int w = input.getWidth();
|
||||
int h = input.getHeight();
|
||||
double cW = w / 2.0;
|
||||
double cH = h / 2.0;
|
||||
|
||||
AffineTransform orientationTransform = new AffineTransform();
|
||||
switch (orientation) {
|
||||
case TIFFBaseline.ORIENTATION_TOPLEFT:
|
||||
// normal
|
||||
return input;
|
||||
case TIFFExtension.ORIENTATION_TOPRIGHT:
|
||||
// flipped vertically
|
||||
orientationTransform.translate(cW, cH);
|
||||
orientationTransform.scale(-1, 1);
|
||||
orientationTransform.translate(-cW, -cH);
|
||||
break;
|
||||
case TIFFExtension.ORIENTATION_BOTRIGHT:
|
||||
// rotated 180
|
||||
orientationTransform.quadrantRotate(2, cW, cH);
|
||||
break;
|
||||
case TIFFExtension.ORIENTATION_BOTLEFT:
|
||||
// flipped horizontally
|
||||
orientationTransform.translate(cW, cH);
|
||||
orientationTransform.scale(1, -1);
|
||||
orientationTransform.translate(-cW, -cH);
|
||||
break;
|
||||
case TIFFExtension.ORIENTATION_LEFTTOP:
|
||||
orientationTransform.translate(cW, cH);
|
||||
orientationTransform.scale(-1, 1);
|
||||
orientationTransform.quadrantRotate(1);
|
||||
orientationTransform.translate(-cW, -cH);
|
||||
flipExtends = true;
|
||||
break;
|
||||
case TIFFExtension.ORIENTATION_RIGHTTOP:
|
||||
// rotated 90
|
||||
orientationTransform.quadrantRotate(1, cW, cH);
|
||||
flipExtends = true;
|
||||
break;
|
||||
case TIFFExtension.ORIENTATION_RIGHTBOT:
|
||||
orientationTransform.translate(cW, cH);
|
||||
orientationTransform.scale(1, -1);
|
||||
orientationTransform.quadrantRotate(1);
|
||||
orientationTransform.translate(-cW, -cH);
|
||||
flipExtends = true;
|
||||
break;
|
||||
case TIFFExtension.ORIENTATION_LEFTBOT:
|
||||
// rotated 270
|
||||
orientationTransform.quadrantRotate(3, cW, cH);
|
||||
flipExtends = true;
|
||||
break;
|
||||
}
|
||||
|
||||
int newW, newH;
|
||||
if (flipExtends) {
|
||||
newW = h;
|
||||
newH = w;
|
||||
}
|
||||
else {
|
||||
newW = w;
|
||||
newH = h;
|
||||
}
|
||||
|
||||
AffineTransform transform = AffineTransform.getTranslateInstance((newW - w) / 2.0, (newH - h) / 2.0);
|
||||
transform.concatenate(orientationTransform);
|
||||
AffineTransformOp transformOp = new AffineTransformOp(transform, null);
|
||||
return transformOp.filter(input, null);
|
||||
}
|
||||
|
||||
public static class TIFFPage {
|
||||
private Directory IFD;
|
||||
private ImageInputStream stream;
|
||||
|
||||
private TIFFPage(Directory IFD, ImageInputStream stream) {
|
||||
this.IFD = IFD;
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
private long write(ImageOutputStream outputStream, EXIFWriter exifWriter) throws IOException {
|
||||
List<Entry> newIFD = writeDirectoryData(IFD, outputStream);
|
||||
return exifWriter.writeIFD(newIFD, outputStream);
|
||||
}
|
||||
|
||||
private List<Entry> writeDirectoryData(Directory IFD, ImageOutputStream outputStream) throws IOException {
|
||||
ArrayList<Entry> newIFD = new ArrayList<Entry>();
|
||||
Iterator<Entry> it = IFD.iterator();
|
||||
while (it.hasNext()) {
|
||||
Entry e = it.next();
|
||||
if (e.getValue() instanceof Directory) {
|
||||
List<Entry> subIFD = writeDirectoryData((Directory) e.getValue(), outputStream);
|
||||
new TIFFEntry((Integer) e.getIdentifier(), TIFF.TYPE_IFD, new AbstractDirectory(subIFD) {
|
||||
});
|
||||
}
|
||||
|
||||
newIFD.add(e);
|
||||
}
|
||||
|
||||
long[] offsets = new long[0];
|
||||
long[] byteCounts = new long[0];
|
||||
int[] newOffsets = new int[0];
|
||||
|
||||
Entry stripOffsetsEntry = IFD.getEntryById(TIFF.TAG_STRIP_OFFSETS);
|
||||
Entry stripByteCountsEntry = IFD.getEntryById(TIFF.TAG_STRIP_BYTE_COUNTS);
|
||||
if (stripOffsetsEntry != null && stripByteCountsEntry != null) {
|
||||
offsets = getValueAsLongArray(stripOffsetsEntry);
|
||||
byteCounts = getValueAsLongArray(stripByteCountsEntry);
|
||||
|
||||
newOffsets = writeData(offsets, byteCounts, outputStream);
|
||||
|
||||
newIFD.remove(stripOffsetsEntry);
|
||||
newIFD.add(new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, newOffsets));
|
||||
}
|
||||
|
||||
Entry oldJpegData = IFD.getEntryById(TIFF.TAG_JPEG_INTERCHANGE_FORMAT);
|
||||
Entry oldJpegDataLength = IFD.getEntryById(TIFF.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
|
||||
if (oldJpegData != null && oldJpegData.valueCount() > 0 && oldJpegDataLength != null && oldJpegDataLength.valueCount() > 0) {
|
||||
if (!Arrays.equals(getValueAsLongArray(oldJpegData), offsets) || !Arrays.equals(getValueAsLongArray(oldJpegDataLength), byteCounts)) {
|
||||
// data already written from TIFF.TAG_STRIP_OFFSETS
|
||||
offsets = getValueAsLongArray(oldJpegData);
|
||||
byteCounts = getValueAsLongArray(oldJpegDataLength);
|
||||
newOffsets = writeData(offsets, byteCounts, outputStream);
|
||||
}
|
||||
newIFD.remove(oldJpegData);
|
||||
newIFD.add(new TIFFEntry(TIFF.TAG_JPEG_INTERCHANGE_FORMAT, newOffsets));
|
||||
}
|
||||
|
||||
Entry oldJpegTable;
|
||||
long[] tableOffsets;
|
||||
|
||||
oldJpegTable = IFD.getEntryById(TIFF.TAG_OLD_JPEG_AC_TABLES);
|
||||
if (oldJpegTable != null && oldJpegTable.valueCount() > 0) {
|
||||
tableOffsets = getValueAsLongArray(oldJpegTable);
|
||||
byteCounts = new long[tableOffsets.length];
|
||||
Arrays.fill(byteCounts, 64);
|
||||
newOffsets = writeData(tableOffsets, byteCounts, outputStream);
|
||||
newIFD.remove(oldJpegTable);
|
||||
newIFD.add(new TIFFEntry(TIFF.TAG_OLD_JPEG_AC_TABLES, newOffsets));
|
||||
}
|
||||
|
||||
oldJpegTable = IFD.getEntryById(TIFF.TAG_OLD_JPEG_Q_TABLES);
|
||||
if (oldJpegTable != null && oldJpegTable.valueCount() > 0) {
|
||||
tableOffsets = getValueAsLongArray(oldJpegTable);
|
||||
byteCounts = new long[tableOffsets.length];
|
||||
Arrays.fill(byteCounts, 64);
|
||||
newOffsets = writeData(tableOffsets, byteCounts, outputStream);
|
||||
newIFD.remove(oldJpegTable);
|
||||
newIFD.add(new TIFFEntry(TIFF.TAG_OLD_JPEG_Q_TABLES, newOffsets));
|
||||
}
|
||||
|
||||
oldJpegTable = IFD.getEntryById(TIFF.TAG_OLD_JPEG_DC_TABLES);
|
||||
if (oldJpegTable != null && oldJpegTable.valueCount() > 0) {
|
||||
tableOffsets = getValueAsLongArray(oldJpegTable);
|
||||
byteCounts = new long[tableOffsets.length];
|
||||
Arrays.fill(byteCounts, 64);
|
||||
newOffsets = writeData(tableOffsets, byteCounts, outputStream);
|
||||
newIFD.remove(oldJpegTable);
|
||||
newIFD.add(new TIFFEntry(TIFF.TAG_OLD_JPEG_DC_TABLES, newOffsets));
|
||||
}
|
||||
|
||||
return newIFD;
|
||||
}
|
||||
|
||||
private int[] writeData(long[] offsets, long[] byteCounts, ImageOutputStream outputStream) throws IOException {
|
||||
int[] newOffsets = new int[offsets.length];
|
||||
for (int i = 0; i < offsets.length; i++) {
|
||||
newOffsets[i] = (int) outputStream.getStreamPosition();
|
||||
stream.seek(offsets[i]);
|
||||
|
||||
byte[] buffer = new byte[(int) byteCounts[i]];
|
||||
stream.readFully(buffer);
|
||||
outputStream.write(buffer);
|
||||
}
|
||||
return newOffsets;
|
||||
}
|
||||
|
||||
private long[] getValueAsLongArray(Entry entry) throws IIOException {
|
||||
//TODO: code duplication from TIFFReader, should be extracted to metadata api
|
||||
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)", entry.getFieldName(), entry.getTypeName(), entry.getValue().getClass()));
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates the image by changing TIFF.TAG_ORIENTATION.
|
||||
* <p>
|
||||
* NOTICE: TIFF.TAG_ORIENTATION is an advice how the image is meant do
|
||||
* be displayed. Other metadata, such as width and height, relate to the
|
||||
* image as how it is stored. The ImageIO TIFF plugin does not handle
|
||||
* orientation. Use
|
||||
* {@link TIFFUtilities#applyOrientation(BufferedImage, int)} for
|
||||
* applying TIFF.TAG_ORIENTATION.
|
||||
* </p>
|
||||
*
|
||||
* @param degree Rotation amount, supports 90<39>, 180<38> and 270<37>.
|
||||
*/
|
||||
public void rotate(int degree) {
|
||||
Validate.isTrue(degree % 90 == 0 && degree > 0 && degree < 360,
|
||||
"Only rotations by 90, 180 and 270 degree are supported");
|
||||
|
||||
ArrayList<Entry> newIDFData = new ArrayList<>();
|
||||
Iterator<Entry> it = IFD.iterator();
|
||||
while (it.hasNext()) {
|
||||
newIDFData.add(it.next());
|
||||
}
|
||||
|
||||
short orientation = TIFFBaseline.ORIENTATION_TOPLEFT;
|
||||
Entry orientationEntry = IFD.getEntryById(TIFF.TAG_ORIENTATION);
|
||||
if (orientationEntry != null) {
|
||||
orientation = ((Number) orientationEntry.getValue()).shortValue();
|
||||
newIDFData.remove(orientationEntry);
|
||||
}
|
||||
|
||||
int steps = degree / 90;
|
||||
for (int i = 0; i < steps; i++) {
|
||||
switch (orientation) {
|
||||
case TIFFBaseline.ORIENTATION_TOPLEFT:
|
||||
orientation = TIFFExtension.ORIENTATION_RIGHTTOP;
|
||||
break;
|
||||
case TIFFExtension.ORIENTATION_TOPRIGHT:
|
||||
orientation = TIFFExtension.ORIENTATION_RIGHTBOT;
|
||||
break;
|
||||
case TIFFExtension.ORIENTATION_BOTRIGHT:
|
||||
orientation = TIFFExtension.ORIENTATION_LEFTBOT;
|
||||
break;
|
||||
case TIFFExtension.ORIENTATION_BOTLEFT:
|
||||
orientation = TIFFExtension.ORIENTATION_LEFTTOP;
|
||||
break;
|
||||
case TIFFExtension.ORIENTATION_LEFTTOP:
|
||||
orientation = TIFFExtension.ORIENTATION_TOPRIGHT;
|
||||
break;
|
||||
case TIFFExtension.ORIENTATION_RIGHTTOP:
|
||||
orientation = TIFFExtension.ORIENTATION_BOTRIGHT;
|
||||
break;
|
||||
case TIFFExtension.ORIENTATION_RIGHTBOT:
|
||||
orientation = TIFFExtension.ORIENTATION_BOTLEFT;
|
||||
break;
|
||||
case TIFFExtension.ORIENTATION_LEFTBOT:
|
||||
orientation = TIFFBaseline.ORIENTATION_TOPLEFT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
newIDFData.add(new TIFFEntry(TIFF.TAG_ORIENTATION, (short) orientation));
|
||||
IFD = new AbstractDirectory(newIDFData) {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Temporary clone, to be removed after TMI204 has been closed
|
||||
*/
|
||||
public static final class TIFFEntry extends AbstractEntry {
|
||||
// TODO: Expose a merge of this and the EXIFEntry class...
|
||||
private final short type;
|
||||
|
||||
private static short guessType(final Object val) {
|
||||
// TODO: This code is duplicated in EXIFWriter.getType, needs refactor!
|
||||
Object value = Validate.notNull(val);
|
||||
|
||||
boolean array = value.getClass().isArray();
|
||||
if (array) {
|
||||
value = Array.get(value, 0);
|
||||
}
|
||||
|
||||
// Note: This "narrowing" is to keep data consistent between read/write.
|
||||
// TODO: Check for negative values and use signed types?
|
||||
if (value instanceof Byte) {
|
||||
return TIFF.TYPE_BYTE;
|
||||
}
|
||||
if (value instanceof Short) {
|
||||
if (!array && (Short) value < Byte.MAX_VALUE) {
|
||||
return TIFF.TYPE_BYTE;
|
||||
}
|
||||
|
||||
return TIFF.TYPE_SHORT;
|
||||
}
|
||||
if (value instanceof Integer) {
|
||||
if (!array && (Integer) value < Short.MAX_VALUE) {
|
||||
return TIFF.TYPE_SHORT;
|
||||
}
|
||||
|
||||
return TIFF.TYPE_LONG;
|
||||
}
|
||||
if (value instanceof Long) {
|
||||
if (!array && (Long) value < Integer.MAX_VALUE) {
|
||||
return TIFF.TYPE_LONG;
|
||||
}
|
||||
}
|
||||
|
||||
if (value instanceof Rational) {
|
||||
return TIFF.TYPE_RATIONAL;
|
||||
}
|
||||
|
||||
if (value instanceof String) {
|
||||
return TIFF.TYPE_ASCII;
|
||||
}
|
||||
|
||||
// TODO: More types
|
||||
|
||||
throw new UnsupportedOperationException(String.format("Method guessType not implemented for value of type %s", value.getClass()));
|
||||
}
|
||||
|
||||
public TIFFEntry(final int identifier, final Object value) {
|
||||
this(identifier, guessType(value), value);
|
||||
}
|
||||
|
||||
TIFFEntry(int identifier, short type, Object value) {
|
||||
super(identifier, value);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeName() {
|
||||
return TIFF.TYPE_NAMES[type];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Temporary clone, to be removed after TMI204 has been closed
|
||||
*/
|
||||
public interface TIFFExtension {
|
||||
int ORIENTATION_TOPRIGHT = 2;
|
||||
int ORIENTATION_BOTRIGHT = 3;
|
||||
int ORIENTATION_BOTLEFT = 4;
|
||||
int ORIENTATION_LEFTTOP = 5;
|
||||
int ORIENTATION_RIGHTTOP = 6;
|
||||
int ORIENTATION_RIGHTBOT = 7;
|
||||
int ORIENTATION_LEFTBOT = 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Temporary clone, to be removed after TMI204 has been closed
|
||||
*/
|
||||
public interface TIFFBaseline {
|
||||
int ORIENTATION_TOPLEFT = 1;
|
||||
}
|
||||
}
|
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 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.contrib.tiff;
|
||||
|
||||
import com.twelvemonkeys.contrib.tiff.TIFFUtilities.TIFFExtension;
|
||||
import com.twelvemonkeys.imageio.plugins.tiff.TIFFMedataFormat;
|
||||
import com.twelvemonkeys.io.FileUtil;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import javax.imageio.stream.ImageOutputStream;
|
||||
import javax.xml.xpath.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.DataBufferByte;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* TIFFUtilitiesTest
|
||||
*
|
||||
* @author <a href="mailto:mail@schmidor.de">Oliver Schmidtmer</a>
|
||||
* @author last modified by $Author$
|
||||
* @version $Id$
|
||||
*/
|
||||
public class TIFFUtilitiesTest {
|
||||
|
||||
@Test
|
||||
public void testMerge() throws IOException {
|
||||
// Files from ImageIO TIFF Plugin
|
||||
InputStream stream1 = getClassLoaderResource("/tiff/ccitt/group3_1d.tif").openStream();
|
||||
InputStream stream2 = getClassLoaderResource("/tiff/ccitt/group3_2d.tif").openStream();
|
||||
InputStream stream3 = getClassLoaderResource("/tiff/ccitt/group4.tif").openStream();
|
||||
|
||||
File file1 = File.createTempFile("imageiotest", ".tif");
|
||||
File file2 = File.createTempFile("imageiotest", ".tif");
|
||||
File file3 = File.createTempFile("imageiotest", ".tif");
|
||||
File output = File.createTempFile("imageiotest", ".tif");
|
||||
|
||||
byte[] data;
|
||||
|
||||
data = FileUtil.read(stream1);
|
||||
FileUtil.write(file1, data);
|
||||
stream1.close();
|
||||
|
||||
data = FileUtil.read(stream2);
|
||||
FileUtil.write(file2, data);
|
||||
stream2.close();
|
||||
|
||||
data = FileUtil.read(stream3);
|
||||
FileUtil.write(file3, data);
|
||||
stream3.close();
|
||||
|
||||
List<File> input = Arrays.asList(file1, file2, file3);
|
||||
TIFFUtilities.merge(input, output);
|
||||
|
||||
ImageInputStream iis = ImageIO.createImageInputStream(output);
|
||||
ImageReader reader = ImageIO.getImageReaders(iis).next();
|
||||
reader.setInput(iis);
|
||||
Assert.assertEquals(3, reader.getNumImages(true));
|
||||
|
||||
iis.close();
|
||||
output.delete();
|
||||
file1.delete();
|
||||
file2.delete();
|
||||
file3.delete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSplit() throws IOException {
|
||||
InputStream inputStream = getClassLoaderResource("/contrib/tiff/multipage.tif").openStream();
|
||||
File inputFile = File.createTempFile("imageiotest", "tif");
|
||||
byte[] data = FileUtil.read(inputStream);
|
||||
FileUtil.write(inputFile, data);
|
||||
inputStream.close();
|
||||
|
||||
File outputDirectory = Files.createTempDirectory("imageio").toFile();
|
||||
|
||||
TIFFUtilities.split(inputFile, outputDirectory);
|
||||
|
||||
ImageReader reader = ImageIO.getImageReadersByFormatName("TIF").next();
|
||||
|
||||
File[] outputFiles = outputDirectory.listFiles();
|
||||
Assert.assertEquals(3, outputFiles.length);
|
||||
for (File outputFile : outputFiles) {
|
||||
ImageInputStream iis = ImageIO.createImageInputStream(outputFile);
|
||||
reader.setInput(iis);
|
||||
Assert.assertEquals(1, reader.getNumImages(true));
|
||||
iis.close();
|
||||
outputFile.delete();
|
||||
}
|
||||
outputDirectory.delete();
|
||||
inputFile.delete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRotate() throws IOException, XPathExpressionException {
|
||||
ImageReader reader = ImageIO.getImageReadersByFormatName("TIF").next();
|
||||
|
||||
InputStream inputStream = getClassLoaderResource("/contrib/tiff/multipage.tif").openStream();
|
||||
File inputFile = File.createTempFile("imageiotest", ".tif");
|
||||
byte[] data = FileUtil.read(inputStream);
|
||||
FileUtil.write(inputFile, data);
|
||||
inputStream.close();
|
||||
|
||||
XPath xPath = XPathFactory.newInstance().newXPath();
|
||||
XPathExpression expression = xPath.compile("TIFFIFD/TIFFField[@number='274']/TIFFBytes/TIFFByte/@value");
|
||||
|
||||
// rotate all pages
|
||||
ImageInputStream inputTest1 = ImageIO.createImageInputStream(inputFile);
|
||||
File outputTest1 = File.createTempFile("imageiotest", ".tif");
|
||||
ImageOutputStream iosTest1 = ImageIO.createImageOutputStream(outputTest1);
|
||||
TIFFUtilities.rotatePages(inputTest1, iosTest1, 90);
|
||||
iosTest1.close();
|
||||
|
||||
ImageInputStream checkTest1 = ImageIO.createImageInputStream(outputTest1);
|
||||
reader.setInput(checkTest1);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Node metaData = reader.getImageMetadata(i)
|
||||
.getAsTree(TIFFMedataFormat.SUN_NATIVE_IMAGE_METADATA_FORMAT_NAME);
|
||||
short orientation = ((Number) expression.evaluate(metaData, XPathConstants.NUMBER)).shortValue();
|
||||
Assert.assertEquals(orientation, TIFFExtension.ORIENTATION_RIGHTTOP);
|
||||
}
|
||||
checkTest1.close();
|
||||
|
||||
// rotate single page further
|
||||
ImageInputStream inputTest2 = ImageIO.createImageInputStream(outputTest1);
|
||||
File outputTest2 = File.createTempFile("imageiotest", ".tif");
|
||||
ImageOutputStream iosTest2 = ImageIO.createImageOutputStream(outputTest2);
|
||||
TIFFUtilities.rotatePage(inputTest2, iosTest2, 90, 1);
|
||||
iosTest2.close();
|
||||
|
||||
ImageInputStream checkTest2 = ImageIO.createImageInputStream(outputTest2);
|
||||
reader.setInput(checkTest2);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Node metaData = reader.getImageMetadata(i)
|
||||
.getAsTree(TIFFMedataFormat.SUN_NATIVE_IMAGE_METADATA_FORMAT_NAME);
|
||||
short orientation = ((Number) expression.evaluate(metaData, XPathConstants.NUMBER)).shortValue();
|
||||
Assert.assertEquals(orientation, i == 1
|
||||
? TIFFExtension.ORIENTATION_BOTRIGHT
|
||||
: TIFFExtension.ORIENTATION_RIGHTTOP);
|
||||
}
|
||||
checkTest2.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testApplyOrientation() throws IOException {
|
||||
InputStream inputStream = getClassLoaderResource("/contrib/tiff/multipage.tif").openStream();
|
||||
File inputFile = File.createTempFile("imageiotest", "tif");
|
||||
byte[] data = FileUtil.read(inputStream);
|
||||
FileUtil.write(inputFile, data);
|
||||
inputStream.close();
|
||||
|
||||
BufferedImage image = ImageIO.read(inputFile);
|
||||
|
||||
// rotate by 90<39>
|
||||
BufferedImage image90 = TIFFUtilities.applyOrientation(image, TIFFExtension.ORIENTATION_RIGHTTOP);
|
||||
// rotate by 270<37>
|
||||
BufferedImage image360 = TIFFUtilities.applyOrientation(image90, TIFFExtension.ORIENTATION_LEFTBOT);
|
||||
|
||||
byte[] original = ((DataBufferByte) image.getData().getDataBuffer()).getData();
|
||||
byte[] rotated = ((DataBufferByte) image360.getData().getDataBuffer()).getData();
|
||||
|
||||
Assert.assertArrayEquals(original, rotated);
|
||||
}
|
||||
|
||||
protected URL getClassLoaderResource(final String pName) {
|
||||
return getClass().getResource(pName);
|
||||
}
|
||||
}
|
BIN
contrib/src/test/resources/contrib/tiff/multipage.tif
Normal file
BIN
contrib/src/test/resources/contrib/tiff/multipage.tif
Normal file
Binary file not shown.
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.1-SNAPSHOT</version>
|
||||
<version>3.3-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-batik</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: Batik Plugin</name>
|
||||
@@ -23,27 +23,54 @@
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
<classifier>tests</classifier>
|
||||
<type>test-jar</type>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>batik</groupId>
|
||||
<groupId>org.apache.xmlgraphics</groupId>
|
||||
<artifactId>batik-rasterizer-ext</artifactId>
|
||||
<version>1.6-1</version>
|
||||
<version>${batik.version}</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.apache.xmlgraphics</groupId>
|
||||
<artifactId>batik-extensions</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.xmlgraphics</groupId>
|
||||
<artifactId>batik-extension</artifactId>
|
||||
<version>${batik.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>batik</groupId>
|
||||
<groupId>org.apache.xmlgraphics</groupId>
|
||||
<artifactId>xmlgraphics-commons</artifactId>
|
||||
<version>2.0.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.xmlgraphics</groupId>
|
||||
<artifactId>batik-anim</artifactId>
|
||||
<version>${batik.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.xmlgraphics</groupId>
|
||||
<artifactId>batik-svggen</artifactId>
|
||||
<version>1.6-1</version>
|
||||
<version>${batik.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>batik</groupId>
|
||||
<groupId>org.apache.xmlgraphics</groupId>
|
||||
<artifactId>batik-transcoder</artifactId>
|
||||
<version>1.6-1</version>
|
||||
<version>${batik.version}</version>
|
||||
<scope>provided</scope>
|
||||
|
||||
<!--
|
||||
@@ -59,4 +86,8 @@
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<batik.version>1.8</batik.version>
|
||||
</properties>
|
||||
</project>
|
||||
|
@@ -31,9 +31,9 @@ package com.twelvemonkeys.imageio.plugins.svg;
|
||||
import com.twelvemonkeys.image.ImageUtil;
|
||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||
import org.apache.batik.anim.dom.SVGDOMImplementation;
|
||||
import org.apache.batik.anim.dom.SVGOMDocument;
|
||||
import org.apache.batik.bridge.*;
|
||||
import org.apache.batik.dom.svg.SVGDOMImplementation;
|
||||
import org.apache.batik.dom.svg.SVGOMDocument;
|
||||
import org.apache.batik.dom.util.DOMUtilities;
|
||||
import org.apache.batik.ext.awt.image.GraphicsUtil;
|
||||
import org.apache.batik.gvt.CanvasGraphicsNode;
|
||||
@@ -74,18 +74,19 @@ import java.util.Map;
|
||||
* @see <A href="http://www.mail-archive.com/batik-dev@xml.apache.org/msg00992.html">batik-dev</A>
|
||||
*/
|
||||
public class SVGImageReader extends ImageReaderBase {
|
||||
private Rasterizer rasterizer = new Rasterizer();
|
||||
private Rasterizer rasterizer;
|
||||
|
||||
/**
|
||||
* Creates an {@code SVGImageReader}.
|
||||
*
|
||||
* @param pProvider the provider
|
||||
*/
|
||||
public SVGImageReader(ImageReaderSpi pProvider) {
|
||||
public SVGImageReader(final ImageReaderSpi pProvider) {
|
||||
super(pProvider);
|
||||
}
|
||||
|
||||
protected void resetMembers() {
|
||||
rasterizer = new Rasterizer();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -134,7 +135,7 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
try {
|
||||
processImageStarted(pIndex);
|
||||
|
||||
rasterizer.mTranscoderInput.setURI(baseURI);
|
||||
rasterizer.transcoderInput.setURI(baseURI);
|
||||
BufferedImage image = rasterizer.getImage();
|
||||
|
||||
Graphics2D g = destination.createGraphics();
|
||||
@@ -252,31 +253,29 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
*/
|
||||
private class Rasterizer extends SVGAbstractTranscoder /*ImageTranscoder*/ {
|
||||
|
||||
BufferedImage mImage = null;
|
||||
private TranscoderInput mTranscoderInput;
|
||||
private float mDefaultWidth;
|
||||
private float mDefaultHeight;
|
||||
private boolean mInit = false;
|
||||
private SVGOMDocument mDocument;
|
||||
private String mURI;
|
||||
private GraphicsNode mGVTRoot;
|
||||
private TranscoderException mException;
|
||||
private BridgeContext mContext;
|
||||
BufferedImage image = null;
|
||||
private TranscoderInput transcoderInput;
|
||||
private float defaultWidth;
|
||||
private float defaultHeight;
|
||||
private boolean initialized = false;
|
||||
private SVGOMDocument document;
|
||||
private String uri;
|
||||
private GraphicsNode gvtRoot;
|
||||
private TranscoderException exception;
|
||||
private BridgeContext context;
|
||||
|
||||
public BufferedImage createImage(int w, int h) {
|
||||
return ImageUtil.createTransparent(w, h);//, BufferedImage.TYPE_INT_ARGB);
|
||||
public BufferedImage createImage(final int width, final int height) {
|
||||
return ImageUtil.createTransparent(width, height);//, BufferedImage.TYPE_INT_ARGB);
|
||||
}
|
||||
|
||||
// This is cheating... We don't fully transcode after all
|
||||
protected void transcode(Document document, String uri, TranscoderOutput output) throws TranscoderException {
|
||||
protected void transcode(Document document, final String uri, final TranscoderOutput output) throws TranscoderException {
|
||||
// Sets up root, curTxf & curAoi
|
||||
// ----
|
||||
if ((document != null) &&
|
||||
!(document.getImplementation() instanceof SVGDOMImplementation)) {
|
||||
DOMImplementation impl;
|
||||
impl = (DOMImplementation) hints.get(KEY_DOM_IMPLEMENTATION);
|
||||
// impl = ExtensibleSVGDOMImplementation.getDOMImplementation();
|
||||
if ((document != null) && !(document.getImplementation() instanceof SVGDOMImplementation)) {
|
||||
DOMImplementation impl = (DOMImplementation) hints.get(KEY_DOM_IMPLEMENTATION);
|
||||
document = DOMUtilities.deepCloneDocument(document, impl);
|
||||
|
||||
if (uri != null) {
|
||||
try {
|
||||
URL url = new URL(uri);
|
||||
@@ -289,7 +288,6 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
|
||||
ctx = createBridgeContext();
|
||||
SVGOMDocument svgDoc = (SVGOMDocument) document;
|
||||
//SVGSVGElement root = svgDoc.getRootElement();
|
||||
|
||||
// build the GVT tree
|
||||
builder = new GVTBuilder();
|
||||
@@ -297,7 +295,7 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
boolean isDynamic =
|
||||
(hints.containsKey(KEY_EXECUTE_ONLOAD) &&
|
||||
(Boolean) hints.get(KEY_EXECUTE_ONLOAD) &&
|
||||
BaseScriptingEnvironment.isDynamicDocument(ctx, svgDoc));
|
||||
BaseScriptingEnvironment.isDynamicDocument(ctx, svgDoc));
|
||||
|
||||
if (isDynamic) {
|
||||
ctx.setDynamicState(BridgeContext.DYNAMIC);
|
||||
@@ -311,8 +309,7 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
catch (BridgeException ex) {
|
||||
// Note: This might fail, but we STILL have the dimensions we need
|
||||
// However, we need to reparse later...
|
||||
//throw new TranscoderException(ex);
|
||||
mException = new TranscoderException(ex);
|
||||
exception = new TranscoderException(ex);
|
||||
}
|
||||
|
||||
// ----
|
||||
@@ -320,24 +317,23 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
// get the 'width' and 'height' attributes of the SVG document
|
||||
Dimension2D docSize = ctx.getDocumentSize();
|
||||
if (docSize != null) {
|
||||
mDefaultWidth = (float) docSize.getWidth();
|
||||
mDefaultHeight = (float) docSize.getHeight();
|
||||
defaultWidth = (float) docSize.getWidth();
|
||||
defaultHeight = (float) docSize.getHeight();
|
||||
}
|
||||
else {
|
||||
mDefaultWidth = 200;
|
||||
mDefaultHeight = 200;
|
||||
defaultWidth = 200;
|
||||
defaultHeight = 200;
|
||||
}
|
||||
|
||||
// Hack to work around exception above
|
||||
if (root != null) {
|
||||
mGVTRoot = root;
|
||||
gvtRoot = root;
|
||||
}
|
||||
mDocument = svgDoc;
|
||||
mURI = uri;
|
||||
this.document = svgDoc;
|
||||
this.uri = uri;
|
||||
|
||||
//ctx.dispose();
|
||||
// Hack to avoid the transcode method wacking my context...
|
||||
mContext = ctx;
|
||||
context = ctx;
|
||||
ctx = null;
|
||||
}
|
||||
|
||||
@@ -352,24 +348,24 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
|
||||
|
||||
// Hacky workaround below...
|
||||
if (mGVTRoot == null) {
|
||||
if (gvtRoot == null) {
|
||||
// Try to reparse, if we had no URI last time...
|
||||
if (mURI != mTranscoderInput.getURI()) {
|
||||
if (uri != transcoderInput.getURI()) {
|
||||
try {
|
||||
mContext.dispose();
|
||||
mDocument.setURLObject(new URL(mTranscoderInput.getURI()));
|
||||
transcode(mDocument, mTranscoderInput.getURI(), null);
|
||||
context.dispose();
|
||||
document.setURLObject(new URL(transcoderInput.getURI()));
|
||||
transcode(document, transcoderInput.getURI(), null);
|
||||
}
|
||||
catch (MalformedURLException ignore) {
|
||||
// Ignored
|
||||
}
|
||||
}
|
||||
|
||||
if (mGVTRoot == null) {
|
||||
throw mException;
|
||||
if (gvtRoot == null) {
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
ctx = mContext;
|
||||
ctx = context;
|
||||
// /Hacky
|
||||
if (abortRequested()) {
|
||||
processReadAborted();
|
||||
@@ -377,13 +373,13 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
}
|
||||
processImageProgress(20f);
|
||||
|
||||
// -- --
|
||||
SVGSVGElement root = mDocument.getRootElement();
|
||||
// ----
|
||||
SVGSVGElement root = document.getRootElement();
|
||||
// ----
|
||||
|
||||
|
||||
// ----
|
||||
setImageSize(mDefaultWidth, mDefaultHeight);
|
||||
setImageSize(defaultWidth, defaultHeight);
|
||||
|
||||
if (abortRequested()) {
|
||||
processReadAborted();
|
||||
@@ -393,22 +389,22 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
|
||||
// compute the preserveAspectRatio matrix
|
||||
AffineTransform Px;
|
||||
String ref = new ParsedURL(mURI).getRef();
|
||||
String ref = new ParsedURL(uri).getRef();
|
||||
|
||||
try {
|
||||
Px = ViewBox.getViewTransform(ref, root, width, height);
|
||||
Px = ViewBox.getViewTransform(ref, root, width, height, null);
|
||||
|
||||
}
|
||||
catch (BridgeException ex) {
|
||||
throw new TranscoderException(ex);
|
||||
}
|
||||
|
||||
if (Px.isIdentity() && (width != mDefaultWidth || height != mDefaultHeight)) {
|
||||
if (Px.isIdentity() && (width != defaultWidth || height != defaultHeight)) {
|
||||
// The document has no viewBox, we need to resize it by hand.
|
||||
// we want to keep the document size ratio
|
||||
float xscale, yscale;
|
||||
xscale = width / mDefaultWidth;
|
||||
yscale = height / mDefaultHeight;
|
||||
xscale = width / defaultWidth;
|
||||
yscale = height / defaultHeight;
|
||||
float scale = Math.min(xscale, yscale);
|
||||
Px = AffineTransform.getScaleInstance(scale, scale);
|
||||
}
|
||||
@@ -439,7 +435,7 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
}
|
||||
processImageProgress(50f);
|
||||
|
||||
CanvasGraphicsNode cgn = getCanvasGraphicsNode(mGVTRoot);
|
||||
CanvasGraphicsNode cgn = getCanvasGraphicsNode(gvtRoot);
|
||||
if (cgn != null) {
|
||||
cgn.setViewingTransform(Px);
|
||||
curTxf = new AffineTransform();
|
||||
@@ -461,7 +457,7 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
throw new TranscoderException(ex);
|
||||
}
|
||||
|
||||
this.root = mGVTRoot;
|
||||
this.root = gvtRoot;
|
||||
// ----
|
||||
|
||||
// NOTE: The code below is copied and pasted from the Batik
|
||||
@@ -509,6 +505,7 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
g2d.setPaint(bgcolor);
|
||||
g2d.fillRect(0, 0, w, h);
|
||||
}
|
||||
|
||||
if (rend != null) { // might be null if the svg document is empty
|
||||
g2d.drawRenderedImage(rend, new AffineTransform());
|
||||
}
|
||||
@@ -533,43 +530,44 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
throw exception;
|
||||
}
|
||||
finally {
|
||||
if (mContext != null) {
|
||||
mContext.dispose();
|
||||
if (context != null) {
|
||||
context.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void init() throws TranscoderException {
|
||||
if (!mInit) {
|
||||
if (mTranscoderInput == null) {
|
||||
if (!initialized) {
|
||||
if (transcoderInput == null) {
|
||||
throw new IllegalStateException("input == null");
|
||||
}
|
||||
|
||||
mInit = true;
|
||||
initialized = true;
|
||||
|
||||
super.transcode(mTranscoderInput, null);
|
||||
super.transcode(transcoderInput, null);
|
||||
}
|
||||
}
|
||||
|
||||
private BufferedImage getImage() throws TranscoderException {
|
||||
if (mImage == null) {
|
||||
mImage = readImage();
|
||||
if (image == null) {
|
||||
image = readImage();
|
||||
}
|
||||
return mImage;
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
protected int getDefaultWidth() throws TranscoderException {
|
||||
init();
|
||||
return (int) (mDefaultWidth + 0.5);
|
||||
return (int) (defaultWidth + 0.5);
|
||||
}
|
||||
|
||||
protected int getDefaultHeight() throws TranscoderException {
|
||||
init();
|
||||
return (int) (mDefaultHeight + 0.5);
|
||||
return (int) (defaultHeight + 0.5);
|
||||
}
|
||||
|
||||
public void setInput(TranscoderInput pInput) {
|
||||
mTranscoderInput = pInput;
|
||||
public void setInput(final TranscoderInput pInput) {
|
||||
transcoderInput = pInput;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -28,17 +28,18 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.svg;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ProviderInfo;
|
||||
import com.twelvemonkeys.lang.SystemUtil;
|
||||
import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
|
||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.spi.ServiceRegistry;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
|
||||
import static com.twelvemonkeys.imageio.plugins.svg.SVGProviderInfo.SVG_READER_AVAILABLE;
|
||||
|
||||
/**
|
||||
* SVGImageReaderSpi
|
||||
* <p/>
|
||||
@@ -46,52 +47,26 @@ import java.util.Locale;
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @version $Id: SVGImageReaderSpi.java,v 1.1 2003/12/02 16:45:00 haku Exp $
|
||||
*/
|
||||
public class SVGImageReaderSpi extends ImageReaderSpi {
|
||||
|
||||
private final static boolean SVG_READER_AVAILABLE = SystemUtil.isClassAvailable("com.twelvemonkeys.imageio.plugins.svg.SVGImageReader");
|
||||
public final class SVGImageReaderSpi extends ImageReaderSpiBase {
|
||||
|
||||
/**
|
||||
* Creates an {@code SVGImageReaderSpi}.
|
||||
*/
|
||||
public SVGImageReaderSpi() {
|
||||
this(IIOUtil.getProviderInfo(SVGImageReaderSpi.class));
|
||||
super(new SVGProviderInfo());
|
||||
}
|
||||
|
||||
private SVGImageReaderSpi(final ProviderInfo pProviderInfo) {
|
||||
super(
|
||||
pProviderInfo.getVendorName(), // Vendor name
|
||||
pProviderInfo.getVersion(), // Version
|
||||
SVG_READER_AVAILABLE ? new String[]{"svg", "SVG"} : new String[]{""}, // Names
|
||||
SVG_READER_AVAILABLE ? new String[]{"svg"} : null, // Suffixes
|
||||
SVG_READER_AVAILABLE ? new String[]{"image/svg", "image/x-svg", "image/svg+xml", "image/svg-xml"} : null, // Mime-types
|
||||
"com.twelvemonkeys.imageio.plugins.svg.SVGImageReader", // Reader class name
|
||||
new Class[] {ImageInputStream.class}, // Input types
|
||||
null, // Writer SPI names
|
||||
true, // Supports standard stream metadata format
|
||||
null, // Native stream metadata format name
|
||||
null, // Native stream metadata format class name
|
||||
null, // Extra stream metadata format names
|
||||
null, // Extra stream metadata format class names
|
||||
true, // Supports standard image metadata format
|
||||
null, // Native image metadata format name
|
||||
null, // Native image metadata format class name
|
||||
null, // Extra image metadata format names
|
||||
null // Extra image metadata format class names
|
||||
);
|
||||
}
|
||||
|
||||
public boolean canDecodeInput(Object pSource) throws IOException {
|
||||
public boolean canDecodeInput(final Object pSource) throws IOException {
|
||||
return pSource instanceof ImageInputStream && SVG_READER_AVAILABLE && canDecode((ImageInputStream) pSource);
|
||||
}
|
||||
|
||||
private static boolean canDecode(ImageInputStream pInput) throws IOException {
|
||||
private static boolean canDecode(final ImageInputStream pInput) throws IOException {
|
||||
// NOTE: This test is quite quick as it does not involve any parsing,
|
||||
// however it requires the doctype to be "svg", which may not be correct
|
||||
// in all cases...
|
||||
// however it may not recognize all kinds of SVG documents.
|
||||
try {
|
||||
pInput.mark();
|
||||
|
||||
// TODO: This is may not be ok for non-UTF/iso-latin encodings...
|
||||
// TODO: This is not ok for UTF-16 and other wide encodings
|
||||
// TODO: Use an XML (encoding) aware Reader instance instead
|
||||
// Need to figure out pretty fast if this is XML or not
|
||||
int b;
|
||||
@@ -99,46 +74,76 @@ public class SVGImageReaderSpi extends ImageReaderSpi {
|
||||
// Skip over leading WS
|
||||
}
|
||||
|
||||
if (!((b == '<') && (pInput.read() == '?') && (pInput.read() == 'x') && (pInput.read() == 'm')
|
||||
&& (pInput.read() == 'l'))) {
|
||||
// If it's not a tag, this can't be valid XML
|
||||
if (b != '<') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Okay, we have XML. But, is it really SVG?
|
||||
boolean docTypeFound = false;
|
||||
while (!docTypeFound) {
|
||||
while (pInput.read() != '<') {
|
||||
// Skip over, until begin tag
|
||||
}
|
||||
// Algorithm for detecting SVG:
|
||||
// - Skip until begin tag '<' and read 4 bytes
|
||||
// - if next is "?" skip until "?>" and start over
|
||||
// - else if next is "!--" skip until "-->" and start over
|
||||
// - else if next is "!DOCTYPE " skip any whitespace
|
||||
// - compare next 3 bytes against "svg", return result
|
||||
// - else
|
||||
// - compare next 3 bytes against "svg", return result
|
||||
|
||||
// If this is not a comment, or the DOCTYPE declaration, the doc
|
||||
// has no DOCTYPE and it can't be svg
|
||||
if (pInput.read() != '!') {
|
||||
byte[] buffer = new byte[4];
|
||||
while (true) {
|
||||
pInput.readFully(buffer);
|
||||
|
||||
if (buffer[0] == '?') {
|
||||
// This is the XML declaration or a processing instruction
|
||||
while (!(pInput.read() == '?' && pInput.read() == '>')) {
|
||||
// Skip until end of XML declaration or processing instruction
|
||||
}
|
||||
}
|
||||
else if (buffer[0] == '!') {
|
||||
if (buffer[1] == '-' && buffer[2] == '-') {
|
||||
// This is a comment
|
||||
while (!(pInput.read() == '-' && pInput.read() == '-' && pInput.read() == '>')) {
|
||||
// Skip until end of comment
|
||||
}
|
||||
}
|
||||
else if (buffer[1] == 'D' && buffer[2] == 'O' && buffer[3] == 'C'
|
||||
&& pInput.read() == 'T' && pInput.read() == 'Y'
|
||||
&& pInput.read() == 'P' && pInput.read() == 'E') {
|
||||
// This is the DOCTYPE declaration
|
||||
while (Character.isWhitespace((char) (b = pInput.read()))) {
|
||||
// Skip over WS
|
||||
}
|
||||
|
||||
if (b == 's' && pInput.read() == 'v' && pInput.read() == 'g') {
|
||||
// It's SVG, identified by DOCTYPE
|
||||
return true;
|
||||
}
|
||||
|
||||
// DOCTYPE found, but not SVG
|
||||
return false;
|
||||
}
|
||||
|
||||
// Something else, we'll skip
|
||||
}
|
||||
else {
|
||||
// This is a normal tag
|
||||
if (buffer[0] == 's' && buffer[1] == 'v' && buffer[2] == 'g'
|
||||
&& (Character.isWhitespace((char) buffer[3]) || buffer[3] == ':')) {
|
||||
// It's SVG, identified by root tag
|
||||
// TODO: Support svg with prefix + recognize namespace (http://www.w3.org/2000/svg)!
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the tag is not "svg", this isn't SVG
|
||||
return false;
|
||||
}
|
||||
|
||||
// There might be comments before the doctype, unfortunately...
|
||||
// If next is "--", this is a comment
|
||||
if ((b = pInput.read()) == '-' && pInput.read() == '-') {
|
||||
while (!(pInput.read() == '-' && pInput.read() == '-' && pInput.read() == '>')) {
|
||||
// Skip until end of comment
|
||||
}
|
||||
}
|
||||
|
||||
// If we are lucky, this is DOCTYPE declaration
|
||||
if (b == 'D' && pInput.read() == 'O' && pInput.read() == 'C'
|
||||
&& pInput.read() == 'T' && pInput.read() == 'Y' && pInput.read() == 'P'
|
||||
&& pInput.read() == 'E') {
|
||||
docTypeFound = true;
|
||||
while (Character.isWhitespace((char) (b = pInput.read()))) {
|
||||
// Skip over WS
|
||||
}
|
||||
if (b == 's' && pInput.read() == 'v' && pInput.read() == 'g') {
|
||||
//System.out.println("It's svg!");
|
||||
return true;
|
||||
}
|
||||
while (pInput.read() != '<') {
|
||||
// Skip over, until next begin tag
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (EOFException ignore) {
|
||||
// Possible for small files...
|
||||
return false;
|
||||
}
|
||||
finally {
|
||||
@@ -146,18 +151,17 @@ public class SVGImageReaderSpi extends ImageReaderSpi {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public ImageReader createReaderInstance(Object extension) throws IOException {
|
||||
public ImageReader createReaderInstance(final Object extension) throws IOException {
|
||||
return new SVGImageReader(this);
|
||||
}
|
||||
|
||||
public String getDescription(Locale locale) {
|
||||
return "Scaleable Vector Graphics (SVG) format image reader";
|
||||
public String getDescription(final Locale locale) {
|
||||
return "Scalable Vector Graphics (SVG) format image reader";
|
||||
}
|
||||
|
||||
@SuppressWarnings({"deprecation"})
|
||||
@Override
|
||||
public void onRegistration(ServiceRegistry registry, Class<?> category) {
|
||||
public void onRegistration(final ServiceRegistry registry, final Class<?> category) {
|
||||
if (!SVG_READER_AVAILABLE) {
|
||||
try {
|
||||
// NOTE: This will break, but it gives us some useful debug info
|
||||
@@ -170,5 +174,6 @@ public class SVGImageReaderSpi extends ImageReaderSpi {
|
||||
|
||||
IIOUtil.deregisterProvider(registry, this, category);
|
||||
}
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 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.svg;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
||||
import com.twelvemonkeys.lang.SystemUtil;
|
||||
|
||||
/**
|
||||
* SVGProviderInfo.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: harald.kuhr$
|
||||
* @version $Id: SVGProviderInfo.java,v 1.0 20/03/15 harald.kuhr Exp$
|
||||
*/
|
||||
final class SVGProviderInfo extends ReaderWriterProviderInfo {
|
||||
final static boolean SVG_READER_AVAILABLE = SystemUtil.isClassAvailable("com.twelvemonkeys.imageio.plugins.svg.SVGImageReader");
|
||||
|
||||
protected SVGProviderInfo() {
|
||||
super(
|
||||
SVGProviderInfo.class,
|
||||
SVG_READER_AVAILABLE ? new String[]{"svg", "SVG"} : new String[]{""}, // Names
|
||||
SVG_READER_AVAILABLE ? new String[]{"svg"} : null, // Suffixes
|
||||
SVG_READER_AVAILABLE ? new String[]{"image/svg", "image/x-svg", "image/svg+xml", "image/svg-xml"} : null, // Mime-types
|
||||
"com.twelvemonkeys.imageio.plugins.svg.SVGImageReader", // Reader class name
|
||||
new String[] {"com.twelvemonkeys.imageio.plugins.svg.SVGImageReaderSpi"},
|
||||
null,
|
||||
null,
|
||||
false, null, null, null, null,
|
||||
true, null, null, null, null
|
||||
);
|
||||
}
|
||||
}
|
@@ -28,64 +28,38 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.wmf;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ProviderInfo;
|
||||
import com.twelvemonkeys.lang.SystemUtil;
|
||||
import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
|
||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.spi.ServiceRegistry;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
|
||||
import static com.twelvemonkeys.imageio.plugins.wmf.WMFProviderInfo.WMF_READER_AVAILABLE;
|
||||
|
||||
/**
|
||||
* WMFImageReaderSpi
|
||||
* <p/>
|
||||
*
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @version $Id: WMFImageReaderSpi.java,v 1.1 2003/12/02 16:45:00 wmhakur Exp $
|
||||
*/
|
||||
public class WMFImageReaderSpi extends ImageReaderSpi {
|
||||
|
||||
// This is correct, as we rely on the SVG reader
|
||||
private final static boolean WMF_READER_AVAILABLE = SystemUtil.isClassAvailable("com.twelvemonkeys.imageio.plugins.svg.SVGImageReader");
|
||||
public final class WMFImageReaderSpi extends ImageReaderSpiBase {
|
||||
|
||||
/**
|
||||
* Creates a {@code WMFImageReaderSpi}.
|
||||
*/
|
||||
public WMFImageReaderSpi() {
|
||||
this(IIOUtil.getProviderInfo(WMFImageReaderSpi.class));
|
||||
super(new WMFProviderInfo());
|
||||
}
|
||||
|
||||
private WMFImageReaderSpi(final ProviderInfo pProviderInfo) {
|
||||
super(
|
||||
pProviderInfo.getVendorName(), // Vendor name
|
||||
pProviderInfo.getVersion(), // Version
|
||||
WMF_READER_AVAILABLE ? new String[]{"wmf", "WMF"} : new String[]{""}, // Names
|
||||
WMF_READER_AVAILABLE ? new String[]{"wmf", "emf"} : null, // Suffixes
|
||||
WMF_READER_AVAILABLE ? new String[]{"application/x-msmetafile", "image/x-wmf"} : null, // Mime-types
|
||||
"com.twelvemonkeys.imageio.plugins.wmf.WMFImageReader", // Reader class name..?
|
||||
new Class[] {ImageInputStream.class}, // Input types
|
||||
null, // Writer SPI names
|
||||
true, // Supports standard stream metadata format
|
||||
null, // Native stream metadata format name
|
||||
null, // Native stream metadata format class name
|
||||
null, // Extra stream metadata format names
|
||||
null, // Extra stream metadata format class names
|
||||
true, // Supports standard image metadata format
|
||||
null, // Native image metadata format name
|
||||
null, // Native image metadata format class name
|
||||
null, // Extra image metadata format names
|
||||
null // Extra image metadata format class names
|
||||
);
|
||||
}
|
||||
|
||||
public boolean canDecodeInput(Object source) throws IOException {
|
||||
public boolean canDecodeInput(final Object source) throws IOException {
|
||||
return source instanceof ImageInputStream && WMF_READER_AVAILABLE && canDecode((ImageInputStream) source);
|
||||
}
|
||||
|
||||
public static boolean canDecode(ImageInputStream pInput) throws IOException {
|
||||
public static boolean canDecode(final ImageInputStream pInput) throws IOException {
|
||||
if (pInput == null) {
|
||||
throw new IllegalArgumentException("input == null");
|
||||
}
|
||||
@@ -96,7 +70,6 @@ public class WMFImageReaderSpi extends ImageReaderSpi {
|
||||
for (byte header : WMF.HEADER) {
|
||||
int read = (byte) pInput.read();
|
||||
if (header != read) {
|
||||
// System.out.println("--> " + i + ": " + read + " (expected " + header + ")");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -108,18 +81,17 @@ public class WMFImageReaderSpi extends ImageReaderSpi {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public ImageReader createReaderInstance(Object extension) throws IOException {
|
||||
public ImageReader createReaderInstance(final Object extension) throws IOException {
|
||||
return new WMFImageReader(this);
|
||||
}
|
||||
|
||||
public String getDescription(Locale locale) {
|
||||
public String getDescription(final Locale locale) {
|
||||
return "Windows Meta File (WMF) image reader";
|
||||
}
|
||||
|
||||
@SuppressWarnings({"deprecation"})
|
||||
@Override
|
||||
public void onRegistration(ServiceRegistry registry, Class<?> category) {
|
||||
public void onRegistration(final ServiceRegistry registry, final Class<?> category) {
|
||||
if (!WMF_READER_AVAILABLE) {
|
||||
IIOUtil.deregisterProvider(registry, this, category);
|
||||
}
|
||||
|
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 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.wmf;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
||||
import com.twelvemonkeys.lang.SystemUtil;
|
||||
|
||||
/**
|
||||
* WMFProviderInfo.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: harald.kuhr$
|
||||
* @version $Id: WMFProviderInfo.java,v 1.0 20/03/15 harald.kuhr Exp$
|
||||
*/
|
||||
final class WMFProviderInfo extends ReaderWriterProviderInfo {
|
||||
// This is correct, as we rely on the SVG reader
|
||||
final static boolean WMF_READER_AVAILABLE = SystemUtil.isClassAvailable("com.twelvemonkeys.imageio.plugins.svg.SVGImageReader");
|
||||
|
||||
protected WMFProviderInfo() {
|
||||
super(
|
||||
WMFProviderInfo.class,
|
||||
WMF_READER_AVAILABLE ? new String[]{"wmf", "WMF"} : new String[]{""}, // Names
|
||||
WMF_READER_AVAILABLE ? new String[]{"wmf", "emf"} : null, // Suffixes
|
||||
WMF_READER_AVAILABLE ? new String[]{"application/x-msmetafile", "image/x-wmf"} : null, // Mime-types
|
||||
"com.twelvemonkeys.imageio.plugins.wmf.WMFImageReader", // Reader class name..?
|
||||
new String[] {"com.twelvemonkeys.imageio.plugins.wmf.WMFImageReaderSpi"},
|
||||
null,
|
||||
null,
|
||||
false, null, null, null, null,
|
||||
true, null, null, null, null
|
||||
);
|
||||
}
|
||||
}
|
@@ -28,30 +28,39 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.svg;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase;
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ImagingOpException;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* SVGImageReaderTestCase
|
||||
* SVGImageReaderTest
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: SVGImageReaderTestCase.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$
|
||||
* @version $Id: SVGImageReaderTest.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$
|
||||
*/
|
||||
public class SVGImageReaderTestCase extends ImageReaderAbstractTestCase<SVGImageReader> {
|
||||
public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader> {
|
||||
private SVGImageReaderSpi provider = new SVGImageReaderSpi();
|
||||
|
||||
protected List<TestData> getTestData() {
|
||||
return Arrays.asList(
|
||||
new TestData(getClassLoaderResource("/svg/batikLogo.svg"), new Dimension(450, 500))
|
||||
new TestData(getClassLoaderResource("/svg/batikLogo.svg"), new Dimension(450, 500)),
|
||||
new TestData(getClassLoaderResource("/svg/red-square.svg"), new Dimension(100, 100)),
|
||||
new TestData(getClassLoaderResource("/svg/blue-square.svg"), new Dimension(100, 100)),
|
||||
new TestData(getClassLoaderResource("/svg/Android_robot.svg"), new Dimension(400, 400))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -69,15 +78,15 @@ public class SVGImageReaderTestCase extends ImageReaderAbstractTestCase<SVGImage
|
||||
}
|
||||
|
||||
protected List<String> getFormatNames() {
|
||||
return Arrays.asList("svg");
|
||||
return Collections.singletonList("svg");
|
||||
}
|
||||
|
||||
protected List<String> getSuffixes() {
|
||||
return Arrays.asList("svg");
|
||||
return Collections.singletonList("svg");
|
||||
}
|
||||
|
||||
protected List<String> getMIMETypes() {
|
||||
return Arrays.asList("image/svg+xml");
|
||||
return Collections.singletonList("image/svg+xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -110,4 +119,22 @@ public class SVGImageReaderTestCase extends ImageReaderAbstractTestCase<SVGImage
|
||||
public void testReadWithSourceRegionParamEqualImage() throws IOException {
|
||||
super.testReadWithSourceRegionParamEqualImage();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRepeatedRead() throws IOException {
|
||||
Dimension dim = new Dimension(100, 100);
|
||||
ImageReader reader = createReader();
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
param.setSourceRenderSize(dim);
|
||||
|
||||
TestData redSquare = new TestData(getClassLoaderResource("/svg/red-square.svg"), dim);
|
||||
reader.setInput(redSquare.getInputStream());
|
||||
BufferedImage imageRed = reader.read(0, param);
|
||||
assertEquals(0xFF0000, imageRed.getRGB(50, 50) & 0xFFFFFF);
|
||||
|
||||
TestData blueSquare = new TestData(getClassLoaderResource("/svg/blue-square.svg"), dim);
|
||||
reader.setInput(blueSquare.getInputStream());
|
||||
BufferedImage imageBlue = reader.read(0, param);
|
||||
assertEquals(0x0000FF, imageBlue.getRGB(50, 50) & 0xFFFFFF);
|
||||
}
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
package com.twelvemonkeys.imageio.plugins.svg;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfoTest;
|
||||
|
||||
/**
|
||||
* SVGProviderInfoTest.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: harald.kuhr$
|
||||
* @version $Id: SVGProviderInfoTest.java,v 1.0 02/06/16 harald.kuhr Exp$
|
||||
*/
|
||||
public class SVGProviderInfoTest extends ReaderWriterProviderInfoTest {
|
||||
|
||||
@Override
|
||||
protected ReaderWriterProviderInfo createProviderInfo() {
|
||||
return new SVGProviderInfo();
|
||||
}
|
||||
}
|
@@ -28,7 +28,7 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.wmf;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase;
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -36,22 +36,22 @@ import javax.imageio.spi.ImageReaderSpi;
|
||||
import java.awt.*;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* SVGImageReaderTestCase
|
||||
* WMFImageReaderTest
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: SVGImageReaderTestCase.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$
|
||||
* @version $Id: WMFImageReaderTest.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$
|
||||
*/
|
||||
public class WMFImageReaderTestCase extends ImageReaderAbstractTestCase<WMFImageReader> {
|
||||
public class WMFImageReaderTest extends ImageReaderAbstractTest<WMFImageReader> {
|
||||
private WMFImageReaderSpi provider = new WMFImageReaderSpi();
|
||||
|
||||
protected List<TestData> getTestData() {
|
||||
return Arrays.asList(
|
||||
// TODO: Dimensions does not look right...
|
||||
new TestData(getClassLoaderResource("/wmf/test.wmf"), new Dimension(841, 673))
|
||||
return Collections.singletonList(
|
||||
new TestData(getClassLoaderResource("/wmf/test.wmf"), new Dimension(133, 106))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ public class WMFImageReaderTestCase extends ImageReaderAbstractTestCase<WMFImage
|
||||
}
|
||||
|
||||
protected List<String> getFormatNames() {
|
||||
return Arrays.asList("wmf");
|
||||
return Collections.singletonList("wmf");
|
||||
}
|
||||
|
||||
protected List<String> getSuffixes() {
|
@@ -0,0 +1,19 @@
|
||||
package com.twelvemonkeys.imageio.plugins.wmf;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfoTest;
|
||||
|
||||
/**
|
||||
* WMFProviderInfoTest.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: harald.kuhr$
|
||||
* @version $Id: WMFProviderInfoTest.java,v 1.0 02/06/16 harald.kuhr Exp$
|
||||
*/
|
||||
public class WMFProviderInfoTest extends ReaderWriterProviderInfoTest {
|
||||
|
||||
@Override
|
||||
protected ReaderWriterProviderInfo createProviderInfo() {
|
||||
return new WMFProviderInfo();
|
||||
}
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="-147 -70 294 345">
|
||||
<g fill="#a4c639">
|
||||
<use stroke-width="14.4" xlink:href="#b" stroke="#FFF"/>
|
||||
<use xlink:href="#a" transform="scale(-1,1)"/>
|
||||
<g id="a" stroke="#FFF" stroke-width="7.2">
|
||||
<rect rx="6.5" transform="rotate(29)" height="86" width="13" y="-86" x="14"/>
|
||||
<rect id="c" rx="24" height="133" width="48" y="41" x="-143"/>
|
||||
<use y="97" x="85" xlink:href="#c"/>
|
||||
</g>
|
||||
<g id="b">
|
||||
<ellipse cy="41" rx="91" ry="84"/>
|
||||
<rect rx="22" height="182" width="182" y="20" x="-91"/>
|
||||
</g>
|
||||
</g>
|
||||
<g stroke="#FFF" stroke-width="7.2" fill="#FFF">
|
||||
<path d="m-95 44.5h190"/><circle cx="-42" r="4"/><circle cx="42" r="4"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 706 B |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" id="blue-square" version="1.1">
|
||||
<g id="layer1">
|
||||
<rect id="rect2985" width="100" height="100" x="0" y="0"
|
||||
style="color:#000000;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 438 B |
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" id="red-square" version="1.1">
|
||||
<g id="layer1">
|
||||
<rect id="rect2985" width="100" height="100" x="0" y="0"
|
||||
style="color:#000000;fill:#ff0000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 441 B |
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.1-SNAPSHOT</version>
|
||||
<version>3.3-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-bmp</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: BMP plugin</name>
|
||||
@@ -18,7 +18,7 @@
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
<classifier>tests</classifier>
|
||||
<type>test-jar</type>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
@@ -28,17 +28,16 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||
|
||||
import com.twelvemonkeys.imageio.AbstractMetadata;
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.metadata.IIOMetadataFormatImpl;
|
||||
import javax.imageio.metadata.IIOMetadataNode;
|
||||
|
||||
/**
|
||||
* BMPMetadata.
|
||||
*/
|
||||
final class BMPMetadata extends IIOMetadata {
|
||||
final class BMPMetadata extends AbstractMetadata {
|
||||
/** We return metadata in the exact same form as the JRE built-in, to be compatible with the BMPImageWriter. */
|
||||
public static final String nativeMetadataFormatName = "javax_imageio_bmp_1.0";
|
||||
|
||||
@@ -46,46 +45,13 @@ final class BMPMetadata extends IIOMetadata {
|
||||
private final int[] colorMap;
|
||||
|
||||
BMPMetadata(final DIBHeader header, final int[] colorMap) {
|
||||
super(true, nativeMetadataFormatName, "com.sun.imageio.plugins.bmp.BMPMetadataFormat", null, null);
|
||||
this.header = Validate.notNull(header, "header");
|
||||
this.colorMap = colorMap == null || colorMap.length == 0 ? null : colorMap;
|
||||
|
||||
standardFormatSupported = true;
|
||||
}
|
||||
|
||||
@Override public boolean isReadOnly() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override public Node getAsTree(final String formatName) {
|
||||
if (IIOMetadataFormatImpl.standardMetadataFormatName.equals(formatName)) {
|
||||
return getStandardTree();
|
||||
}
|
||||
else if (nativeMetadataFormatName.equals(formatName)) {
|
||||
return getNativeTree();
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Unsupported metadata format: " + formatName);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void mergeTree(final String formatName, final Node root) {
|
||||
if (isReadOnly()) {
|
||||
throw new IllegalStateException("Metadata is read-only");
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void reset() {
|
||||
if (isReadOnly()) {
|
||||
throw new IllegalStateException("Metadata is read-only");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNativeMetadataFormatName() {
|
||||
return nativeMetadataFormatName;
|
||||
}
|
||||
|
||||
private Node getNativeTree() {
|
||||
protected Node getNativeTree() {
|
||||
IIOMetadataNode root = new IIOMetadataNode(nativeMetadataFormatName);
|
||||
|
||||
addChildNode(root, "BMPVersion", header.getBMPVersion());
|
||||
@@ -170,7 +136,8 @@ final class BMPMetadata extends IIOMetadata {
|
||||
return child;
|
||||
}
|
||||
|
||||
@Override protected IIOMetadataNode getStandardChromaNode() {
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardChromaNode() {
|
||||
// NOTE: BMP files may contain a color map, even if true color...
|
||||
// Not sure if this is a good idea to expose to the meta data,
|
||||
// as it might be unexpected... Then again...
|
||||
@@ -197,7 +164,8 @@ final class BMPMetadata extends IIOMetadata {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override protected IIOMetadataNode getStandardCompressionNode() {
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardCompressionNode() {
|
||||
IIOMetadataNode compression = new IIOMetadataNode("Compression");
|
||||
IIOMetadataNode compressionTypeName = addChildNode(compression, "CompressionTypeName", null);
|
||||
compressionTypeName.setAttribute("value", "NONE");
|
||||
@@ -229,7 +197,8 @@ final class BMPMetadata extends IIOMetadata {
|
||||
// }
|
||||
}
|
||||
|
||||
@Override protected IIOMetadataNode getStandardDataNode() {
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardDataNode() {
|
||||
IIOMetadataNode node = new IIOMetadataNode("Data");
|
||||
|
||||
// IIOMetadataNode planarConfiguration = new IIOMetadataNode("PlanarConfiguration");
|
||||
@@ -294,7 +263,8 @@ final class BMPMetadata extends IIOMetadata {
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
@Override protected IIOMetadataNode getStandardDimensionNode() {
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardDimensionNode() {
|
||||
if (header.xPixelsPerMeter > 0 || header.yPixelsPerMeter > 0) {
|
||||
IIOMetadataNode dimension = new IIOMetadataNode("Dimension");
|
||||
|
||||
@@ -302,16 +272,16 @@ final class BMPMetadata extends IIOMetadata {
|
||||
addChildNode(dimension, "HorizontalPhysicalPixelSpacing", null);
|
||||
addChildNode(dimension, "VerticalPhysicalPixelSpacing", null);
|
||||
|
||||
// IIOMetadataNode imageOrientation = new IIOMetadataNode("ImageOrientation");
|
||||
//
|
||||
// if (header.topDown) {
|
||||
// imageOrientation.setAttribute("value", "FlipH");
|
||||
// }
|
||||
// else {
|
||||
// imageOrientation.setAttribute("value", "Normal");
|
||||
// }
|
||||
//
|
||||
// dimension.appendChild(imageOrientation);
|
||||
// IIOMetadataNode imageOrientation = new IIOMetadataNode("ImageOrientation");
|
||||
//
|
||||
// if (header.topDown) {
|
||||
// imageOrientation.setAttribute("value", "FlipH");
|
||||
// }
|
||||
// else {
|
||||
// imageOrientation.setAttribute("value", "Normal");
|
||||
// }
|
||||
//
|
||||
// dimension.appendChild(imageOrientation);
|
||||
|
||||
return dimension;
|
||||
}
|
||||
@@ -325,7 +295,8 @@ final class BMPMetadata extends IIOMetadata {
|
||||
|
||||
// No tiling
|
||||
|
||||
@Override protected IIOMetadataNode getStandardTransparencyNode() {
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardTransparencyNode() {
|
||||
return null;
|
||||
|
||||
// IIOMetadataNode transparency = new IIOMetadataNode("Transparency");
|
||||
|
@@ -43,6 +43,7 @@ abstract class BitmapDescriptor {
|
||||
protected final DIBHeader header;
|
||||
|
||||
protected BufferedImage image;
|
||||
protected BitmapMask mask;
|
||||
|
||||
public BitmapDescriptor(final DirectoryEntry pEntry, final DIBHeader pHeader) {
|
||||
Validate.notNull(pEntry, "entry");
|
||||
@@ -69,4 +70,17 @@ abstract class BitmapDescriptor {
|
||||
protected final int getBitCount() {
|
||||
return entry.getBitCount() != 0 ? entry.getBitCount() : header.getBitCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + "[" + entry + ", " + header + "]";
|
||||
}
|
||||
|
||||
public final void setMask(final BitmapMask mask) {
|
||||
this.mask = mask;
|
||||
}
|
||||
|
||||
public final boolean hasMask() {
|
||||
return header.getHeight() == getHeight() * 2;
|
||||
}
|
||||
}
|
||||
|
@@ -28,8 +28,6 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||
|
||||
import com.twelvemonkeys.image.InverseColorMapIndexColorModel;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.awt.image.IndexColorModel;
|
||||
@@ -46,8 +44,6 @@ class BitmapIndexed extends BitmapDescriptor {
|
||||
protected final int[] bits;
|
||||
protected final int[] colors;
|
||||
|
||||
private BitmapMask mask;
|
||||
|
||||
public BitmapIndexed(final DirectoryEntry pEntry, final DIBHeader pHeader) {
|
||||
super(pEntry, pHeader);
|
||||
bits = new int[getWidth() * getHeight()];
|
||||
@@ -65,7 +61,7 @@ class BitmapIndexed extends BitmapDescriptor {
|
||||
// This is slightly obscure, and should probably be moved..
|
||||
Hashtable<String, Object> properties = null;
|
||||
if (entry instanceof DirectoryEntry.CUREntry) {
|
||||
properties = new Hashtable<String, Object>(1);
|
||||
properties = new Hashtable<>(1);
|
||||
properties.put("cursor_hotspot", ((DirectoryEntry.CUREntry) this.entry).getHotspot());
|
||||
}
|
||||
|
||||
@@ -89,8 +85,6 @@ class BitmapIndexed extends BitmapDescriptor {
|
||||
|
||||
raster.setSamples(0, 0, getWidth(), getHeight(), 0, bits);
|
||||
|
||||
//System.out.println("Image: " + image);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
@@ -100,40 +94,40 @@ class BitmapIndexed extends BitmapDescriptor {
|
||||
IndexColorModel createColorModel() {
|
||||
// NOTE: This is a hack to make room for transparent pixel for mask
|
||||
int bits = getBitCount();
|
||||
|
||||
|
||||
int colors = this.colors.length;
|
||||
int trans = -1;
|
||||
int transparent = -1;
|
||||
|
||||
// Try to avoid USHORT transfertype, as it results in BufferedImage TYPE_CUSTOM
|
||||
// NOTE: This code assumes icons are small, and is NOT optimized for performance...
|
||||
if (colors > (1 << getBitCount())) {
|
||||
int index = findTransIndexMaybeRemap(this.colors, this.bits);
|
||||
int index = findTransparentIndexMaybeRemap(this.colors, this.bits);
|
||||
|
||||
if (index == -1) {
|
||||
// No duplicate found, increase bitcount
|
||||
bits++;
|
||||
trans = this.colors.length - 1;
|
||||
transparent = this.colors.length - 1;
|
||||
}
|
||||
else {
|
||||
// Found a duplicate, use it as trans
|
||||
trans = index;
|
||||
// Found a duplicate, use it as transparent
|
||||
transparent = index;
|
||||
colors--;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Setting hasAlpha to true, makes things work on 1.2
|
||||
return new InverseColorMapIndexColorModel(
|
||||
bits, colors, this.colors, 0, true, trans,
|
||||
return new IndexColorModel(
|
||||
bits, colors, this.colors, 0, true, transparent,
|
||||
bits <= 8 ? DataBuffer.TYPE_BYTE : DataBuffer.TYPE_USHORT
|
||||
);
|
||||
}
|
||||
|
||||
private static int findTransIndexMaybeRemap(final int[] pColors, final int[] pBits) {
|
||||
private static int findTransparentIndexMaybeRemap(final int[] colors, final int[] bits) {
|
||||
// Look for unused colors, to use as transparent
|
||||
final boolean[] used = new boolean[pColors.length - 1];
|
||||
for (int pBit : pBits) {
|
||||
if (!used[pBit]) {
|
||||
used[pBit] = true;
|
||||
boolean[] used = new boolean[colors.length - 1];
|
||||
for (int bit : bits) {
|
||||
if (!used[bit]) {
|
||||
used[bit] = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,38 +138,35 @@ class BitmapIndexed extends BitmapDescriptor {
|
||||
}
|
||||
|
||||
// Try to find duplicates in colormap, and remap
|
||||
int trans = -1;
|
||||
int transparent = -1;
|
||||
int duplicate = -1;
|
||||
for (int i = 0; trans == -1 && i < pColors.length - 1; i++) {
|
||||
for (int j = i + 1; j < pColors.length - 1; j++) {
|
||||
if (pColors[i] == pColors[j]) {
|
||||
trans = j;
|
||||
for (int i = 0; transparent == -1 && i < colors.length - 1; i++) {
|
||||
for (int j = i + 1; j < colors.length - 1; j++) {
|
||||
if (colors[i] == colors[j]) {
|
||||
transparent = j;
|
||||
duplicate = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (trans != -1) {
|
||||
if (transparent != -1) {
|
||||
// Remap duplicate
|
||||
for (int i = 0; i < pBits.length; i++) {
|
||||
if (pBits[i] == trans) {
|
||||
pBits[i] = duplicate;
|
||||
for (int i = 0; i < bits.length; i++) {
|
||||
if (bits[i] == transparent) {
|
||||
bits[i] = duplicate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return trans;
|
||||
return transparent;
|
||||
}
|
||||
|
||||
public BufferedImage getImage() {
|
||||
if (image == null) {
|
||||
image = createImageIndexed();
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
public void setMask(final BitmapMask pMask) {
|
||||
mask = pMask;
|
||||
}
|
||||
}
|
||||
|
@@ -38,19 +38,19 @@ import java.awt.image.BufferedImage;
|
||||
* @version $Id: BitmapMask.java,v 1.0 25.feb.2006 00:29:44 haku Exp$
|
||||
*/
|
||||
class BitmapMask extends BitmapDescriptor {
|
||||
protected final BitmapIndexed mask;
|
||||
protected final BitmapIndexed bitMask;
|
||||
|
||||
public BitmapMask(final DirectoryEntry pParent, final DIBHeader pHeader) {
|
||||
super(pParent, pHeader);
|
||||
mask = new BitmapIndexed(pParent, pHeader);
|
||||
bitMask = new BitmapIndexed(pParent, pHeader);
|
||||
}
|
||||
|
||||
boolean isTransparent(final int pX, final int pY) {
|
||||
// NOTE: 1: Fully transparent, 0: Opaque...
|
||||
return mask.bits[pX + pY * getWidth()] != 0;
|
||||
return bitMask.bits[pX + pY * getWidth()] != 0;
|
||||
}
|
||||
|
||||
public BufferedImage getImage() {
|
||||
return mask.getImage();
|
||||
return bitMask.getImage();
|
||||
}
|
||||
}
|
||||
|
@@ -28,7 +28,9 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.WritableRaster;
|
||||
|
||||
/**
|
||||
* Describes an RGB/true color bitmap structure (16, 24 and 32 bits per pixel).
|
||||
@@ -43,6 +45,38 @@ class BitmapRGB extends BitmapDescriptor {
|
||||
}
|
||||
|
||||
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.
|
||||
if (mask != null) {
|
||||
image = createMaskedImage();
|
||||
mask = null;
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
private BufferedImage createMaskedImage() {
|
||||
BufferedImage masked = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
|
||||
|
||||
Graphics2D graphics = masked.createGraphics();
|
||||
try {
|
||||
graphics.drawImage(image, 0, 0, null);
|
||||
}
|
||||
finally {
|
||||
graphics.dispose();
|
||||
}
|
||||
|
||||
WritableRaster alphaRaster = masked.getAlphaRaster();
|
||||
|
||||
byte[] trans = {0x0};
|
||||
for (int y = 0; y < getHeight(); y++) {
|
||||
for (int x = 0; x < getWidth(); x++) {
|
||||
if (mask.isTransparent(x, y)) {
|
||||
alphaRaster.setDataElements(x, y, trans);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return masked;
|
||||
}
|
||||
}
|
||||
|
@@ -1,3 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 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.bmp;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
||||
|
@@ -66,14 +66,15 @@ import java.util.List;
|
||||
// TODO: Decide whether DirectoryEntry or DIBHeader should be primary source for color count/bit count
|
||||
// TODO: Support loading icons from DLLs, see
|
||||
// <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwui/html/msdn_icons.asp">MSDN</a>
|
||||
// Known issue: 256x256 PNG encoded icons does not have IndexColorModel even if stated in DirectoryEntry (seem impossible as the PNGs are all true color)
|
||||
// Known issue: 256x256 PNG encoded icons does not have IndexColorModel even if stated in DirectoryEntry
|
||||
// (seem impossible as the PNGs are all true color)
|
||||
abstract class DIBImageReader extends ImageReaderBase {
|
||||
// TODO: Consider moving the reading to inner classes (subclasses of BitmapDescriptor)
|
||||
private Directory directory;
|
||||
|
||||
// TODO: Review these, make sure we don't have a memory leak
|
||||
private Map<DirectoryEntry, DIBHeader> headers = new WeakHashMap<DirectoryEntry, DIBHeader>();
|
||||
private Map<DirectoryEntry, BitmapDescriptor> descriptors = new WeakWeakMap<DirectoryEntry, BitmapDescriptor>();
|
||||
private Map<DirectoryEntry, DIBHeader> headers = new WeakHashMap<>();
|
||||
private Map<DirectoryEntry, BitmapDescriptor> descriptors = new WeakWeakMap<>();
|
||||
|
||||
private ImageReader pngImageReader;
|
||||
|
||||
@@ -101,7 +102,7 @@ abstract class DIBImageReader extends ImageReaderBase {
|
||||
return getImageTypesPNG(entry);
|
||||
}
|
||||
|
||||
List<ImageTypeSpecifier> types = new ArrayList<ImageTypeSpecifier>();
|
||||
List<ImageTypeSpecifier> types = new ArrayList<>();
|
||||
DIBHeader header = getHeader(entry);
|
||||
|
||||
// Use data from header to create specifier
|
||||
@@ -121,10 +122,13 @@ abstract class DIBImageReader extends ImageReaderBase {
|
||||
specifier = ImageTypeSpecifiers.createFromIndexColorModel(indexed.createColorModel());
|
||||
break;
|
||||
case 16:
|
||||
// TODO: May have mask?!
|
||||
specifier = ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_USHORT_555_RGB);
|
||||
break;
|
||||
case 24:
|
||||
specifier = ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR);
|
||||
specifier = new BitmapRGB(entry, header).hasMask()
|
||||
? ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR)
|
||||
: ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR);
|
||||
break;
|
||||
case 32:
|
||||
specifier = ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_INT_ARGB);
|
||||
@@ -184,6 +188,7 @@ abstract class DIBImageReader extends ImageReaderBase {
|
||||
}
|
||||
else {
|
||||
Graphics2D g = destination.createGraphics();
|
||||
|
||||
try {
|
||||
g.setComposite(AlphaComposite.Src);
|
||||
g.drawImage(image, 0, 0, null);
|
||||
@@ -335,7 +340,7 @@ abstract class DIBImageReader extends ImageReaderBase {
|
||||
}
|
||||
|
||||
BitmapMask mask = new BitmapMask(pBitmap.entry, pBitmap.header);
|
||||
readBitmapIndexed1(mask.mask, true);
|
||||
readBitmapIndexed1(mask.bitMask, true);
|
||||
pBitmap.setMask(mask);
|
||||
}
|
||||
|
||||
@@ -370,7 +375,7 @@ abstract class DIBImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: If we are reading the mask, we don't abort or progress
|
||||
// NOTE: If we are reading the mask, we don't abort or report progress
|
||||
if (!pAsMask) {
|
||||
if (abortRequested()) {
|
||||
processReadAborted();
|
||||
@@ -455,7 +460,7 @@ abstract class DIBImageReader extends ImageReaderBase {
|
||||
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;
|
||||
// Will create TYPE_USHORT_555
|
||||
DirectColorModel cm = new DirectColorModel(16, 0x7C00, 0x03E0, 0x001F);
|
||||
DataBuffer buffer = new DataBufferShort(pixels, pixels.length);
|
||||
WritableRaster raster = Raster.createPackedRaster(
|
||||
@@ -480,6 +485,8 @@ abstract class DIBImageReader extends ImageReaderBase {
|
||||
|
||||
processImageProgress(100 * y / (float) pBitmap.getHeight());
|
||||
}
|
||||
|
||||
// TODO: Might be mask!?
|
||||
}
|
||||
|
||||
private void readBitmap24(final BitmapDescriptor pBitmap) throws IOException {
|
||||
@@ -494,16 +501,19 @@ abstract class DIBImageReader extends ImageReaderBase {
|
||||
cs, nBits, false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE
|
||||
);
|
||||
|
||||
int scanlineStride = pBitmap.getWidth() * 3;
|
||||
// BMP rows are padded to 4 byte boundary
|
||||
int rowSizeBytes = ((8 * scanlineStride + 31) / 32) * 4;
|
||||
|
||||
WritableRaster raster = Raster.createInterleavedRaster(
|
||||
buffer, pBitmap.getWidth(), pBitmap.getHeight(), pBitmap.getWidth(), 3, bOffs, null
|
||||
buffer, pBitmap.getWidth(), pBitmap.getHeight(), scanlineStride, 3, bOffs, null
|
||||
);
|
||||
pBitmap.image = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
|
||||
|
||||
for (int y = 0; y < pBitmap.getHeight(); y++) {
|
||||
int offset = (pBitmap.getHeight() - y - 1) * pBitmap.getWidth();
|
||||
imageInput.readFully(pixels, offset, pBitmap.getWidth() * 3);
|
||||
|
||||
// TODO: Possibly read padding byte here!
|
||||
for (int y = 0; y < pBitmap.getHeight(); y++) {
|
||||
int offset = (pBitmap.getHeight() - y - 1) * scanlineStride;
|
||||
imageInput.readFully(pixels, offset, scanlineStride);
|
||||
imageInput.skipBytes(rowSizeBytes - scanlineStride);
|
||||
|
||||
if (abortRequested()) {
|
||||
processReadAborted();
|
||||
@@ -512,6 +522,14 @@ abstract class DIBImageReader extends ImageReaderBase {
|
||||
|
||||
processImageProgress(100 * y / (float) pBitmap.getHeight());
|
||||
}
|
||||
|
||||
// 24 bit icons usually have a bit mask
|
||||
if (pBitmap.hasMask()) {
|
||||
BitmapMask mask = new BitmapMask(pBitmap.entry, pBitmap.header);
|
||||
readBitmapIndexed1(mask.bitMask, true);
|
||||
|
||||
pBitmap.setMask(mask);
|
||||
}
|
||||
}
|
||||
|
||||
private void readBitmap32(final BitmapDescriptor pBitmap) throws IOException {
|
||||
@@ -535,6 +553,9 @@ abstract class DIBImageReader extends ImageReaderBase {
|
||||
}
|
||||
processImageProgress(100 * y / (float) pBitmap.getHeight());
|
||||
}
|
||||
|
||||
// There might be a mask here as well, but we'll ignore it,
|
||||
// and use the 8 bit alpha channel in the ARGB pixel data
|
||||
}
|
||||
|
||||
private Directory getDirectory() throws IOException {
|
||||
|
@@ -1,3 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 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.bmp;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
||||
|
@@ -1,6 +1,6 @@
|
||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase;
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||
import org.junit.Test;
|
||||
import org.mockito.InOrder;
|
||||
import org.w3c.dom.Node;
|
||||
@@ -19,10 +19,12 @@ 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.*;
|
||||
@@ -34,7 +36,7 @@ import static org.mockito.Mockito.*;
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: BMPImageReaderTest.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$
|
||||
*/
|
||||
public class BMPImageReaderTest extends ImageReaderAbstractTestCase<BMPImageReader> {
|
||||
public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader> {
|
||||
protected List<TestData> getTestData() {
|
||||
return Arrays.asList(
|
||||
// BMP Suite "Good"
|
||||
@@ -90,7 +92,7 @@ public class BMPImageReaderTest extends ImageReaderAbstractTestCase<BMPImageRead
|
||||
new TestData(getClassLoaderResource("/os2/money-256-(os2).bmp"), new Dimension(455, 341)),
|
||||
new TestData(getClassLoaderResource("/os2/money-24bit-os2.bmp"), new Dimension(455, 341)),
|
||||
|
||||
// Vaious other samples
|
||||
// Various other samples
|
||||
new TestData(getClassLoaderResource("/bmp/Blue Lace 16.bmp"), new Dimension(48, 48)),
|
||||
new TestData(getClassLoaderResource("/bmp/blauesglas_mono.bmp"), new Dimension(301, 331)),
|
||||
new TestData(getClassLoaderResource("/bmp/blauesglas_4.bmp"), new Dimension(301, 331)),
|
||||
@@ -124,7 +126,7 @@ public class BMPImageReaderTest extends ImageReaderAbstractTestCase<BMPImageRead
|
||||
}
|
||||
|
||||
protected List<String> getFormatNames() {
|
||||
return Arrays.asList("bmp");
|
||||
return Collections.singletonList("bmp");
|
||||
}
|
||||
|
||||
protected List<String> getSuffixes() {
|
||||
@@ -132,7 +134,7 @@ public class BMPImageReaderTest extends ImageReaderAbstractTestCase<BMPImageRead
|
||||
}
|
||||
|
||||
protected List<String> getMIMETypes() {
|
||||
return Arrays.asList("image/bmp");
|
||||
return Collections.singletonList("image/bmp");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -234,7 +236,6 @@ public class BMPImageReaderTest extends ImageReaderAbstractTestCase<BMPImageRead
|
||||
|
||||
@Test
|
||||
public void testMetadataEqualsJRE() throws IOException, URISyntaxException {
|
||||
// Ignore this test if not on an Oracle JRE (com.sun...BMPImageReader not available)
|
||||
ImageReader jreReader;
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -243,8 +244,9 @@ public class BMPImageReaderTest extends ImageReaderAbstractTestCase<BMPImageRead
|
||||
jreReader = constructor.newInstance(new Object[] {null});
|
||||
}
|
||||
catch (Exception e) {
|
||||
System.err.println("WARNING: Skipping metadata tests: " + e);
|
||||
e.printStackTrace();
|
||||
// Ignore this test if not on an Oracle JRE (com.sun...BMPImageReader not available)
|
||||
assumeNoException(e);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,19 @@
|
||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfoTest;
|
||||
|
||||
/**
|
||||
* BMPProviderInfoTest.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: harald.kuhr$
|
||||
* @version $Id: BMPProviderInfoTest.java,v 1.0 02/06/16 harald.kuhr Exp$
|
||||
*/
|
||||
public class BMPProviderInfoTest extends ReaderWriterProviderInfoTest {
|
||||
|
||||
@Override
|
||||
protected ReaderWriterProviderInfo createProviderInfo() {
|
||||
return new BMPProviderInfo();
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase;
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -10,6 +10,7 @@ import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
@@ -21,7 +22,7 @@ import static org.junit.Assert.*;
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: CURImageReaderTest.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$
|
||||
*/
|
||||
public class CURImageReaderTest extends ImageReaderAbstractTestCase<CURImageReader> {
|
||||
public class CURImageReaderTest extends ImageReaderAbstractTest<CURImageReader> {
|
||||
protected List<TestData> getTestData() {
|
||||
return Arrays.asList(
|
||||
new TestData(getClassLoaderResource("/cur/hand.cur"), new Dimension(32, 32)),
|
||||
@@ -43,11 +44,11 @@ public class CURImageReaderTest extends ImageReaderAbstractTestCase<CURImageRead
|
||||
}
|
||||
|
||||
protected List<String> getFormatNames() {
|
||||
return Arrays.asList("cur");
|
||||
return Collections.singletonList("cur");
|
||||
}
|
||||
|
||||
protected List<String> getSuffixes() {
|
||||
return Arrays.asList("cur");
|
||||
return Collections.singletonList("cur");
|
||||
}
|
||||
|
||||
protected List<String> getMIMETypes() {
|
||||
|
@@ -0,0 +1,19 @@
|
||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfoTest;
|
||||
|
||||
/**
|
||||
* CURProviderInfoTest.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: harald.kuhr$
|
||||
* @version $Id: CURProviderInfoTest.java,v 1.0 02/06/16 harald.kuhr Exp$
|
||||
*/
|
||||
public class CURProviderInfoTest extends ReaderWriterProviderInfoTest {
|
||||
|
||||
@Override
|
||||
protected ReaderWriterProviderInfo createProviderInfo() {
|
||||
return new CURProviderInfo();
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase;
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -8,6 +8,7 @@ import javax.imageio.spi.ImageReaderSpi;
|
||||
import java.awt.*;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -17,7 +18,7 @@ import java.util.List;
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: ICOImageReaderTest.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$
|
||||
*/
|
||||
public class ICOImageReaderTest extends ImageReaderAbstractTestCase<ICOImageReader> {
|
||||
public class ICOImageReaderTest extends ImageReaderAbstractTest<ICOImageReader> {
|
||||
protected List<TestData> getTestData() {
|
||||
return Arrays.asList(
|
||||
new TestData(
|
||||
@@ -38,7 +39,9 @@ public class ICOImageReaderTest extends ImageReaderAbstractTestCase<ICOImageRead
|
||||
new Dimension(16, 16), new Dimension(16, 16), new Dimension(32, 32), new Dimension(32, 32),
|
||||
new Dimension(48, 48), new Dimension(48, 48), new Dimension(256, 256), new Dimension(256, 256),
|
||||
new Dimension(16, 16), new Dimension(32, 32), new Dimension(48, 48), new Dimension(256, 256)
|
||||
)
|
||||
),
|
||||
// Problematic icon that reports 24 bit in the descriptor, but has separate 1 bit ''mask (height 2 x icon height)!
|
||||
new TestData(getClassLoaderResource("/ico/rgb24bitmask.ico"), new Dimension(32, 32))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -56,11 +59,11 @@ public class ICOImageReaderTest extends ImageReaderAbstractTestCase<ICOImageRead
|
||||
}
|
||||
|
||||
protected List<String> getFormatNames() {
|
||||
return Arrays.asList("ico");
|
||||
return Collections.singletonList("ico");
|
||||
}
|
||||
|
||||
protected List<String> getSuffixes() {
|
||||
return Arrays.asList("ico");
|
||||
return Collections.singletonList("ico");
|
||||
}
|
||||
|
||||
protected List<String> getMIMETypes() {
|
||||
|
@@ -0,0 +1,19 @@
|
||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfoTest;
|
||||
|
||||
/**
|
||||
* ICOProviderInfoTest.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: harald.kuhr$
|
||||
* @version $Id: ICOProviderInfoTest.java,v 1.0 02/06/16 harald.kuhr Exp$
|
||||
*/
|
||||
public class ICOProviderInfoTest extends ReaderWriterProviderInfoTest {
|
||||
|
||||
@Override
|
||||
protected ReaderWriterProviderInfo createProviderInfo() {
|
||||
return new ICOProviderInfo();
|
||||
}
|
||||
}
|
BIN
imageio/imageio-bmp/src/test/resources/ico/rgb24bitmask.ico
Normal file
BIN
imageio/imageio-bmp/src/test/resources/ico/rgb24bitmask.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.2 KiB |
@@ -1,12 +1,10 @@
|
||||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.1-SNAPSHOT</version>
|
||||
<version>3.3-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-clippath</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: Photoshop Path Support</name>
|
||||
@@ -22,7 +20,7 @@
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
<classifier>tests</classifier>
|
||||
<type>test-jar</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
|
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.1-SNAPSHOT</version>
|
||||
<version>3.3-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: Core</name>
|
||||
|
@@ -26,7 +26,7 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.psd;
|
||||
package com.twelvemonkeys.imageio;
|
||||
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
@@ -42,13 +42,15 @@ import java.util.Arrays;
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: AbstractMetadata.java,v 1.0 Nov 13, 2009 1:02:12 AM haraldk Exp$
|
||||
*/
|
||||
abstract class AbstractMetadata extends IIOMetadata implements Cloneable {
|
||||
// TODO: Move to core...
|
||||
public abstract class AbstractMetadata extends IIOMetadata implements Cloneable {
|
||||
protected AbstractMetadata(final boolean standardFormatSupported,
|
||||
final String nativeFormatName, final String nativeFormatClassName,
|
||||
final String[] extraFormatNames, final String[] extraFormatClassNames) {
|
||||
super(standardFormatSupported, nativeFormatName, nativeFormatClassName, extraFormatNames, extraFormatClassNames);
|
||||
}
|
||||
|
||||
protected AbstractMetadata(final boolean pStandardFormatSupported,
|
||||
final String pNativeFormatName, final String pNativeFormatClassName,
|
||||
final String[] pExtraFormatNames, final String[] pExtraFormatClassNames) {
|
||||
super(pStandardFormatSupported, pNativeFormatName, pNativeFormatClassName, pExtraFormatNames, pExtraFormatClassNames);
|
||||
protected AbstractMetadata() {
|
||||
super(true, null, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,36 +65,38 @@ abstract class AbstractMetadata extends IIOMetadata implements Cloneable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getAsTree(final String pFormatName) {
|
||||
validateFormatName(pFormatName);
|
||||
public Node getAsTree(final String formatName) {
|
||||
validateFormatName(formatName);
|
||||
|
||||
if (pFormatName.equals(nativeMetadataFormatName)) {
|
||||
if (formatName.equals(nativeMetadataFormatName)) {
|
||||
return getNativeTree();
|
||||
}
|
||||
else if (pFormatName.equals(IIOMetadataFormatImpl.standardMetadataFormatName)) {
|
||||
else if (formatName.equals(IIOMetadataFormatImpl.standardMetadataFormatName)) {
|
||||
return getStandardTree();
|
||||
}
|
||||
|
||||
// TODO: What about extra formats??
|
||||
throw new AssertionError("Unreachable");
|
||||
// Subclasses that supports extra formats need to check for these formats themselves...
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation that throws {@code UnsupportedOperationException}.
|
||||
* Subclasses that supports formats other than standard metadata should override this method.
|
||||
*
|
||||
* @throws UnsupportedOperationException
|
||||
*/
|
||||
protected Node getNativeTree() {
|
||||
throw new UnsupportedOperationException("getNativeTree");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mergeTree(final String pFormatName, final Node pRoot) throws IIOInvalidTreeException {
|
||||
public void mergeTree(final String formatName, final Node root) throws IIOInvalidTreeException {
|
||||
assertMutable();
|
||||
|
||||
validateFormatName(pFormatName);
|
||||
validateFormatName(formatName);
|
||||
|
||||
if (!pRoot.getNodeName().equals(nativeMetadataFormatName)) {
|
||||
throw new IIOInvalidTreeException("Root must be " + nativeMetadataFormatName, pRoot);
|
||||
}
|
||||
|
||||
Node node = pRoot.getFirstChild();
|
||||
while (node != null) {
|
||||
// TODO: Merge values from node into this
|
||||
|
||||
// Move to the next sibling
|
||||
node = node.getNextSibling();
|
||||
if (!root.getNodeName().equals(formatName)) {
|
||||
throw new IIOInvalidTreeException("Root must be " + formatName, root);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,21 +116,19 @@ abstract class AbstractMetadata extends IIOMetadata implements Cloneable {
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract Node getNativeTree();
|
||||
|
||||
protected final void validateFormatName(final String pFormatName) {
|
||||
protected final void validateFormatName(final String formatName) {
|
||||
String[] metadataFormatNames = getMetadataFormatNames();
|
||||
|
||||
if (metadataFormatNames != null) {
|
||||
for (String metadataFormatName : metadataFormatNames) {
|
||||
if (metadataFormatName.equals(pFormatName)) {
|
||||
if (metadataFormatName.equals(formatName)) {
|
||||
return; // Found, we're ok!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Bad format name: \"%s\". Expected one of %s", pFormatName, Arrays.toString(metadataFormatNames))
|
||||
String.format("Bad format name: \"%s\". Expected one of %s", formatName, Arrays.toString(metadataFormatNames))
|
||||
);
|
||||
}
|
||||
|
@@ -29,6 +29,7 @@
|
||||
package com.twelvemonkeys.imageio;
|
||||
|
||||
import com.twelvemonkeys.image.BufferedImageIcon;
|
||||
import com.twelvemonkeys.image.ImageUtil;
|
||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||
|
||||
import javax.imageio.*;
|
||||
@@ -37,6 +38,9 @@ import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.datatransfer.Transferable;
|
||||
import java.awt.datatransfer.UnsupportedFlavorException;
|
||||
import java.awt.event.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.IndexColorModel;
|
||||
@@ -424,6 +428,8 @@ public abstract class ImageReaderBase extends ImageReader {
|
||||
static final String ZOOM_OUT = "zoom-out";
|
||||
static final String ZOOM_ACTUAL = "zoom-actual";
|
||||
|
||||
private BufferedImage image;
|
||||
|
||||
Paint backgroundPaint;
|
||||
|
||||
final Paint checkeredBG;
|
||||
@@ -434,6 +440,7 @@ public abstract class ImageReaderBase extends ImageReader {
|
||||
setOpaque(false);
|
||||
setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
|
||||
|
||||
image = pImage;
|
||||
checkeredBG = createTexture();
|
||||
|
||||
// For indexed color, default to the color of the transparent pixel, if any
|
||||
@@ -441,7 +448,7 @@ public abstract class ImageReaderBase extends ImageReader {
|
||||
|
||||
backgroundPaint = defaultBG != null ? defaultBG : checkeredBG;
|
||||
|
||||
setupActions(pImage);
|
||||
setupActions();
|
||||
setComponentPopupMenu(createPopupMenu());
|
||||
addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
@@ -451,16 +458,59 @@ public abstract class ImageReaderBase extends ImageReader {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
setTransferHandler(new TransferHandler() {
|
||||
@Override
|
||||
public int getSourceActions(JComponent c) {
|
||||
return COPY;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Transferable createTransferable(JComponent c) {
|
||||
return new ImageTransferable(image);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean importData(JComponent comp, Transferable t) {
|
||||
if (canImport(comp, t.getTransferDataFlavors())) {
|
||||
try {
|
||||
Image transferData = (Image) t.getTransferData(DataFlavor.imageFlavor);
|
||||
image = ImageUtil.toBuffered(transferData);
|
||||
setIcon(new BufferedImageIcon(image));
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (UnsupportedFlavorException | IOException ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canImport(JComponent comp, DataFlavor[] transferFlavors) {
|
||||
for (DataFlavor flavor : transferFlavors) {
|
||||
if (flavor.equals(DataFlavor.imageFlavor)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setupActions(final BufferedImage pImage) {
|
||||
private void setupActions() {
|
||||
// Mac weirdness... VK_MINUS/VK_PLUS seems to map to english key map always...
|
||||
bindAction(new ZoomAction("Zoom in", pImage, 2), ZOOM_IN, KeyStroke.getKeyStroke('+'), KeyStroke.getKeyStroke(KeyEvent.VK_ADD, 0));
|
||||
bindAction(new ZoomAction("Zoom out", pImage, .5), ZOOM_OUT, KeyStroke.getKeyStroke('-'), KeyStroke.getKeyStroke(KeyEvent.VK_SUBTRACT, 0));
|
||||
bindAction(new ZoomAction("Zoom actual", pImage), 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_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(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()));
|
||||
}
|
||||
|
||||
private void bindAction(final AbstractAction action, final String key, final KeyStroke... keyStrokes) {
|
||||
private void bindAction(final Action action, final String key, final KeyStroke... keyStrokes) {
|
||||
for (KeyStroke keyStroke : keyStrokes) {
|
||||
getInputMap(WHEN_IN_FOCUSED_WINDOW).put(keyStroke, key);
|
||||
}
|
||||
@@ -588,20 +638,18 @@ public abstract class ImageReaderBase extends ImageReader {
|
||||
}
|
||||
|
||||
private class ZoomAction extends AbstractAction {
|
||||
private final BufferedImage image;
|
||||
private final double zoomFactor;
|
||||
|
||||
public ZoomAction(final String name, final BufferedImage image, final double zoomFactor) {
|
||||
public ZoomAction(final String name, final double zoomFactor) {
|
||||
super(name);
|
||||
this.image = image;
|
||||
this.zoomFactor = zoomFactor;
|
||||
}
|
||||
|
||||
public ZoomAction(final String name, final BufferedImage image) {
|
||||
this(name, image, 0);
|
||||
public ZoomAction(final String name) {
|
||||
this(name, 0);
|
||||
}
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
if (zoomFactor <= 0) {
|
||||
setIcon(new BufferedImageIcon(image));
|
||||
}
|
||||
@@ -614,6 +662,33 @@ public abstract class ImageReaderBase extends ImageReader {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class ImageTransferable implements Transferable {
|
||||
private final BufferedImage image;
|
||||
|
||||
public ImageTransferable(final BufferedImage image) {
|
||||
this.image = image;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataFlavor[] getTransferDataFlavors() {
|
||||
return new DataFlavor[] {DataFlavor.imageFlavor};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDataFlavorSupported(final DataFlavor flavor) {
|
||||
return DataFlavor.imageFlavor.equals(flavor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getTransferData(final DataFlavor flavor) throws UnsupportedFlavorException, IOException {
|
||||
if (isDataFlavorSupported(flavor)) {
|
||||
return image;
|
||||
}
|
||||
|
||||
throw new UnsupportedFlavorException(flavor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class ExitIfNoWindowPresentHandler extends WindowAdapter {
|
||||
|
@@ -0,0 +1,151 @@
|
||||
package com.twelvemonkeys.imageio.color;
|
||||
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
|
||||
/**
|
||||
* Converts between CIE L*a*b* and sRGB color spaces.
|
||||
*/
|
||||
// Code adapted from ImageJ's Color_Space_Converter.java (Public Domain):
|
||||
// http://rsb.info.nih.gov/ij/plugins/download/Color_Space_Converter.java
|
||||
public final class CIELabColorConverter {
|
||||
// TODO: Create interface in the color package?
|
||||
// TODO: Make YCbCr/YCCK -> RGB/CMYK implement same interface?
|
||||
|
||||
public enum Illuminant {
|
||||
D50(new float[] {96.4212f, 100.0f, 82.5188f}),
|
||||
D65(new float[] {95.0429f, 100.0f, 108.8900f});
|
||||
|
||||
private final float[] whitePoint;
|
||||
|
||||
Illuminant(final float[] wp) {
|
||||
whitePoint = Validate.isTrue(wp != null && wp.length == 3, wp, "Bad white point definition: %s");
|
||||
}
|
||||
|
||||
public float[] getWhitePoint() {
|
||||
return whitePoint;
|
||||
}
|
||||
}
|
||||
|
||||
private final float[] whitePoint;
|
||||
|
||||
public CIELabColorConverter(final Illuminant illuminant) {
|
||||
whitePoint = Validate.notNull(illuminant, "illuminant").getWhitePoint();
|
||||
}
|
||||
|
||||
private float clamp(float x) {
|
||||
if (x < 0.0f) {
|
||||
return 0.0f;
|
||||
}
|
||||
else if (x > 255.0f) {
|
||||
return 255.0f;
|
||||
}
|
||||
else {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
public void toRGB(float l, float a, float b, float[] rgbResult) {
|
||||
XYZtoRGB(LABtoXYZ(l, a, b, rgbResult), rgbResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert LAB to XYZ.
|
||||
* @param L
|
||||
* @param a
|
||||
* @param b
|
||||
* @return XYZ values
|
||||
*/
|
||||
private float[] LABtoXYZ(float L, float a, float b, float[] xyzResult) {
|
||||
// Significant speedup: Removing Math.pow
|
||||
float y = (L + 16.0f) / 116.0f;
|
||||
float y3 = y * y * y; // Math.pow(y, 3.0);
|
||||
float x = (a / 500.0f) + y;
|
||||
float x3 = x * x * x; // Math.pow(x, 3.0);
|
||||
float z = y - (b / 200.0f);
|
||||
float z3 = z * z * z; // Math.pow(z, 3.0);
|
||||
|
||||
if (y3 > 0.008856f) {
|
||||
y = y3;
|
||||
}
|
||||
else {
|
||||
y = (y - (16.0f / 116.0f)) / 7.787f;
|
||||
}
|
||||
|
||||
if (x3 > 0.008856f) {
|
||||
x = x3;
|
||||
}
|
||||
else {
|
||||
x = (x - (16.0f / 116.0f)) / 7.787f;
|
||||
}
|
||||
|
||||
if (z3 > 0.008856f) {
|
||||
z = z3;
|
||||
}
|
||||
else {
|
||||
z = (z - (16.0f / 116.0f)) / 7.787f;
|
||||
}
|
||||
|
||||
xyzResult[0] = x * whitePoint[0];
|
||||
xyzResult[1] = y * whitePoint[1];
|
||||
xyzResult[2] = z * whitePoint[2];
|
||||
|
||||
return xyzResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert XYZ to RGB
|
||||
* @param xyz
|
||||
* @return RGB values
|
||||
*/
|
||||
private float[] XYZtoRGB(final float[] xyz, final float[] rgbResult) {
|
||||
return XYZtoRGB(xyz[0], xyz[1], xyz[2], rgbResult);
|
||||
}
|
||||
|
||||
private float[] XYZtoRGB(final float X, final float Y, final float Z, float[] rgbResult) {
|
||||
float x = X / 100.0f;
|
||||
float y = Y / 100.0f;
|
||||
float z = Z / 100.0f;
|
||||
|
||||
float r = x * 3.2406f + y * -1.5372f + z * -0.4986f;
|
||||
float g = x * -0.9689f + y * 1.8758f + z * 0.0415f;
|
||||
float b = x * 0.0557f + y * -0.2040f + z * 1.0570f;
|
||||
|
||||
// assume sRGB
|
||||
if (r > 0.0031308f) {
|
||||
r = ((1.055f * (float) pow(r, 1.0 / 2.4)) - 0.055f);
|
||||
}
|
||||
else {
|
||||
r = (r * 12.92f);
|
||||
}
|
||||
|
||||
if (g > 0.0031308f) {
|
||||
g = ((1.055f * (float) pow(g, 1.0 / 2.4)) - 0.055f);
|
||||
}
|
||||
else {
|
||||
g = (g * 12.92f);
|
||||
}
|
||||
|
||||
if (b > 0.0031308f) {
|
||||
b = ((1.055f * (float) pow(b, 1.0 / 2.4)) - 0.055f);
|
||||
}
|
||||
else {
|
||||
b = (b * 12.92f);
|
||||
}
|
||||
|
||||
// convert 0..1 into 0..255
|
||||
rgbResult[0] = clamp(r * 255);
|
||||
rgbResult[1] = clamp(g * 255);
|
||||
rgbResult[2] = clamp(b * 255);
|
||||
|
||||
return rgbResult;
|
||||
}
|
||||
|
||||
// TODO: Test, to figure out if accuracy is good enough.
|
||||
// Visual inspection looks good! The author claims 5-12% error, worst case up to 25%...
|
||||
// http://martin.ankerl.com/2007/10/04/optimized-pow-approximation-for-java-and-c-c/
|
||||
static double pow(final double a, final double b) {
|
||||
long tmp = Double.doubleToLongBits(a);
|
||||
long tmp2 = (long) (b * (tmp - 4606921280493453312L)) + 4606921280493453312L;
|
||||
return Double.longBitsToDouble(tmp2);
|
||||
}
|
||||
}
|
@@ -52,7 +52,7 @@ import java.util.Properties;
|
||||
* <p />
|
||||
* Color profiles may be configured by placing a property-file
|
||||
* {@code com/twelvemonkeys/imageio/color/icc_profiles.properties}
|
||||
* on the classpath, specifying the full path to the profile.
|
||||
* on the classpath, specifying the full path to the profiles.
|
||||
* ICC color profiles are probably already present on your system, or
|
||||
* can be downloaded from
|
||||
* <a href="http://www.color.org/profiles2.xalter">ICC</a>,
|
||||
@@ -84,11 +84,11 @@ public final class ColorSpaces {
|
||||
public static final int CS_GENERIC_CMYK = 5001;
|
||||
|
||||
// Weak references to hold the color spaces while cached
|
||||
private static WeakReference<ICC_Profile> adobeRGB1998 = new WeakReference<ICC_Profile>(null);
|
||||
private static WeakReference<ICC_Profile> genericCMYK = new WeakReference<ICC_Profile>(null);
|
||||
private static WeakReference<ICC_Profile> adobeRGB1998 = new WeakReference<>(null);
|
||||
private static WeakReference<ICC_Profile> genericCMYK = new WeakReference<>(null);
|
||||
|
||||
// Cache for the latest used color spaces
|
||||
private static final Map<Key, ICC_ColorSpace> cache = new LRUHashMap<Key, ICC_ColorSpace>(10);
|
||||
private static final Map<Key, ICC_ColorSpace> cache = new LRUHashMap<>(10);
|
||||
|
||||
private ColorSpaces() {}
|
||||
|
||||
@@ -100,7 +100,8 @@ public final class ColorSpaces {
|
||||
*
|
||||
* @param profile the ICC color profile. May not be {@code null}.
|
||||
* @return an ICC color space
|
||||
* @throws IllegalArgumentException if {@code profile} is {@code null}
|
||||
* @throws IllegalArgumentException if {@code profile} is {@code null}.
|
||||
* @throws java.awt.color.CMMException if {@code profile} is invalid.
|
||||
*/
|
||||
public static ICC_ColorSpace createColorSpace(final ICC_Profile profile) {
|
||||
Validate.notNull(profile, "profile");
|
||||
@@ -161,6 +162,11 @@ public final class ColorSpaces {
|
||||
|
||||
if (cs == null) {
|
||||
cs = new ICC_ColorSpace(profile);
|
||||
|
||||
// Validate the color space, to avoid caching bad color spaces
|
||||
// Will throw IllegalArgumentException or CMMException if the profile is bad
|
||||
cs.fromRGB(new float[] {1f, 0f, 0f});
|
||||
|
||||
cache.put(key, cs);
|
||||
}
|
||||
|
||||
@@ -195,7 +201,7 @@ public final class ColorSpaces {
|
||||
* @return {@code true} if known to be offending, {@code false} otherwise
|
||||
* @throws IllegalArgumentException if {@code profile} is {@code null}
|
||||
*/
|
||||
public static boolean isOffendingColorProfile(final ICC_Profile profile) {
|
||||
static boolean isOffendingColorProfile(final ICC_Profile profile) {
|
||||
Validate.notNull(profile, "profile");
|
||||
|
||||
// NOTE:
|
||||
@@ -213,6 +219,26 @@ public final class ColorSpaces {
|
||||
return data[ICC_Profile.icHdrRenderingIntent] != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether an ICC color profile is valid.
|
||||
* Invalid profiles are known to cause problems for {@link java.awt.image.ColorConvertOp}.
|
||||
* <p />
|
||||
* <em>
|
||||
* Note that this method only tests if a color conversion using this profile is known to fail.
|
||||
* There's no guarantee that the color conversion will succeed even if this method returns {@code false}.
|
||||
* </em>
|
||||
*
|
||||
* @param profile the ICC color profile. May not be {@code null}.
|
||||
* @return {@code profile} if valid.
|
||||
* @throws IllegalArgumentException if {@code profile} is {@code null}
|
||||
* @throws java.awt.color.CMMException if {@code profile} is invalid.
|
||||
*/
|
||||
public static ICC_Profile validateProfile(final ICC_Profile profile) {
|
||||
createColorSpace(profile); // Creating a color space will fail if the profile is bad
|
||||
|
||||
return profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the color space specified by the given color space constant.
|
||||
* <p />
|
||||
@@ -249,7 +275,7 @@ public final class ColorSpaces {
|
||||
}
|
||||
}
|
||||
|
||||
adobeRGB1998 = new WeakReference<ICC_Profile>(profile);
|
||||
adobeRGB1998 = new WeakReference<>(profile);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,7 +298,7 @@ public final class ColorSpaces {
|
||||
return CMYKColorSpace.getInstance();
|
||||
}
|
||||
|
||||
genericCMYK = new WeakReference<ICC_Profile>(profile);
|
||||
genericCMYK = new WeakReference<>(profile);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,7 +343,7 @@ public final class ColorSpaces {
|
||||
try {
|
||||
return ICC_Profile.getInstance(profilePath);
|
||||
}
|
||||
catch (IOException ignore) {
|
||||
catch (SecurityException | IOException ignore) {
|
||||
if (DEBUG) {
|
||||
ignore.printStackTrace();
|
||||
}
|
||||
@@ -367,15 +393,18 @@ public final class ColorSpaces {
|
||||
}
|
||||
|
||||
private static class Profiles {
|
||||
private static final Properties PROFILES = loadProfiles(Platform.os());
|
||||
private static final Properties PROFILES = loadProfiles();
|
||||
|
||||
private static Properties loadProfiles(final Platform.OperatingSystem os) {
|
||||
private static Properties loadProfiles() {
|
||||
Properties systemDefaults;
|
||||
|
||||
try {
|
||||
systemDefaults = SystemUtil.loadProperties(ColorSpaces.class, "com/twelvemonkeys/imageio/color/icc_profiles_" + os.id());
|
||||
systemDefaults = SystemUtil.loadProperties(
|
||||
ColorSpaces.class,
|
||||
"com/twelvemonkeys/imageio/color/icc_profiles_" + Platform.os().id()
|
||||
);
|
||||
}
|
||||
catch (IOException ignore) {
|
||||
catch (SecurityException | IOException ignore) {
|
||||
System.err.printf(
|
||||
"Warning: Could not load system default ICC profile locations from %s, will use bundled fallback profiles.\n",
|
||||
ignore.getMessage()
|
||||
@@ -392,10 +421,14 @@ public final class ColorSpaces {
|
||||
Properties profiles = new Properties(systemDefaults);
|
||||
|
||||
try {
|
||||
Properties userOverrides = SystemUtil.loadProperties(ColorSpaces.class, "com/twelvemonkeys/imageio/color/icc_profiles");
|
||||
Properties userOverrides = SystemUtil.loadProperties(
|
||||
ColorSpaces.class,
|
||||
"com/twelvemonkeys/imageio/color/icc_profiles"
|
||||
);
|
||||
profiles.putAll(userOverrides);
|
||||
}
|
||||
catch (IOException ignore) {
|
||||
catch (SecurityException | IOException ignore) {
|
||||
// Most likely, this file won't be there
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
|
@@ -14,7 +14,7 @@ import java.awt.color.ICC_Profile;
|
||||
interface ICCProfileSanitizer {
|
||||
void fixProfile(ICC_Profile profile, byte[] profileHeader);
|
||||
|
||||
static class Factory {
|
||||
class Factory {
|
||||
static ICCProfileSanitizer get() {
|
||||
// Strategy pattern:
|
||||
// - KCMSSanitizerStrategy - Current behaviour, default for Java 1.6 and Oracle JRE 1.7
|
||||
|
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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.color;
|
||||
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.ComponentColorModel;
|
||||
import java.awt.image.DataBuffer;
|
||||
|
||||
/**
|
||||
* ComponentColorModel subclass that correctly handles full 16 bit {@code TYPE_SHORT} signed integral samples.
|
||||
**
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: UInt32ColorModel.java,v 1.0 24.01.11 17.51 haraldk Exp$
|
||||
*/
|
||||
public final class Int16ComponentColorModel extends ComponentColorModel {
|
||||
private final ComponentColorModel delegate;
|
||||
|
||||
public Int16ComponentColorModel(final ColorSpace cs, final boolean hasAlpha, boolean isAlphaPremultiplied) {
|
||||
super(cs, hasAlpha, isAlphaPremultiplied, hasAlpha ? TRANSLUCENT : OPAQUE, DataBuffer.TYPE_SHORT);
|
||||
|
||||
delegate = new ComponentColorModel(cs, hasAlpha, isAlphaPremultiplied, hasAlpha ? TRANSLUCENT : OPAQUE, DataBuffer.TYPE_USHORT);
|
||||
}
|
||||
|
||||
private void remap(final short[] s, final int i) {
|
||||
// MIN ... -1 -> 0 ... MAX
|
||||
// 0 ... MAX -> MIN ... -1
|
||||
short sample = s[i];
|
||||
|
||||
if (sample < 0) {
|
||||
s[i] = (short) (sample - Short.MIN_VALUE);
|
||||
}
|
||||
else {
|
||||
s[i] = (short) (sample + Short.MIN_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRed(final Object inData) {
|
||||
remap((short[]) inData, 0);
|
||||
|
||||
return delegate.getRed(inData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGreen(final Object inData) {
|
||||
remap((short[]) inData, 1);
|
||||
|
||||
return delegate.getGreen(inData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlue(final Object inData) {
|
||||
remap((short[]) inData, 2);
|
||||
|
||||
return delegate.getBlue(inData);
|
||||
}
|
||||
}
|
@@ -0,0 +1,81 @@
|
||||
package com.twelvemonkeys.imageio.color;
|
||||
|
||||
/**
|
||||
* Fast YCbCr to RGB conversion.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author Original code by Werner Randelshofer (used by permission).
|
||||
*/
|
||||
public final class YCbCrConverter {
|
||||
/**
|
||||
* Define tables for YCC->RGB color space conversion.
|
||||
*/
|
||||
private final static int SCALEBITS = 16;
|
||||
private final static int MAXJSAMPLE = 255;
|
||||
private final static int CENTERJSAMPLE = 128;
|
||||
private final static int ONE_HALF = 1 << (SCALEBITS - 1);
|
||||
|
||||
private final static int[] Cr_R_LUT = new int[MAXJSAMPLE + 1];
|
||||
private final static int[] Cb_B_LUT = new int[MAXJSAMPLE + 1];
|
||||
private final static int[] Cr_G_LUT = new int[MAXJSAMPLE + 1];
|
||||
private final static int[] Cb_G_LUT = new int[MAXJSAMPLE + 1];
|
||||
|
||||
/**
|
||||
* Initializes tables for YCC->RGB color space conversion.
|
||||
*/
|
||||
private static void buildYCCtoRGBtable() {
|
||||
if (ColorSpaces.DEBUG) {
|
||||
System.err.println("Building YCC conversion table");
|
||||
}
|
||||
|
||||
for (int i = 0, x = -CENTERJSAMPLE; i <= MAXJSAMPLE; i++, x++) {
|
||||
// i is the actual input pixel value, in the range 0..MAXJSAMPLE
|
||||
// The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE
|
||||
// Cr=>R value is nearest int to 1.40200 * x
|
||||
Cr_R_LUT[i] = (int) ((1.40200 * (1 << SCALEBITS) + 0.5) * x + ONE_HALF) >> SCALEBITS;
|
||||
// Cb=>B value is nearest int to 1.77200 * x
|
||||
Cb_B_LUT[i] = (int) ((1.77200 * (1 << SCALEBITS) + 0.5) * x + ONE_HALF) >> SCALEBITS;
|
||||
// Cr=>G value is scaled-up -0.71414 * x
|
||||
Cr_G_LUT[i] = -(int) (0.71414 * (1 << SCALEBITS) + 0.5) * x;
|
||||
// Cb=>G value is scaled-up -0.34414 * x
|
||||
// We also add in ONE_HALF so that need not do it in inner loop
|
||||
Cb_G_LUT[i] = -(int) ((0.34414) * (1 << SCALEBITS) + 0.5) * x + ONE_HALF;
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
buildYCCtoRGBtable();
|
||||
}
|
||||
|
||||
public static void convertYCbCr2RGB(final byte[] yCbCr, final byte[] rgb, final double[] coefficients, final int offset) {
|
||||
double y = (yCbCr[offset] & 0xff);
|
||||
double cb = (yCbCr[offset + 1] & 0xff) - 128;
|
||||
double cr = (yCbCr[offset + 2] & 0xff) - 128;
|
||||
|
||||
double lumaRed = coefficients[0];
|
||||
double lumaGreen = coefficients[1];
|
||||
double lumaBlue = coefficients[2];
|
||||
|
||||
int red = (int) Math.round(cr * (2 - 2 * lumaRed) + y);
|
||||
int blue = (int) Math.round(cb * (2 - 2 * lumaBlue) + y);
|
||||
int green = (int) Math.round((y - lumaRed * red - lumaBlue * blue) / lumaGreen);
|
||||
|
||||
rgb[offset] = clamp(red);
|
||||
rgb[offset + 2] = clamp(blue);
|
||||
rgb[offset + 1] = clamp(green);
|
||||
}
|
||||
|
||||
public static void convertYCbCr2RGB(final byte[] yCbCr, final byte[] rgb, final int offset) {
|
||||
int y = yCbCr[offset] & 0xff;
|
||||
int cr = yCbCr[offset + 2] & 0xff;
|
||||
int cb = yCbCr[offset + 1] & 0xff;
|
||||
|
||||
rgb[offset] = clamp(y + Cr_R_LUT[cr]);
|
||||
rgb[offset + 1] = clamp(y + (Cb_G_LUT[cb] + Cr_G_LUT[cr] >> SCALEBITS));
|
||||
rgb[offset + 2] = clamp(y + Cb_B_LUT[cb]);
|
||||
}
|
||||
|
||||
private static byte clamp(int val) {
|
||||
return (byte) Math.max(0, Math.min(255, val));
|
||||
}
|
||||
}
|
@@ -73,7 +73,7 @@ public class ProviderInfo {
|
||||
return name.startsWith("com.twelvemonkeys") ? "TwelveMonkeys" : name;
|
||||
}
|
||||
|
||||
private String fakeVersion(Package pPackage) {
|
||||
private String fakeVersion(final Package pPackage) {
|
||||
String name = pPackage.getName();
|
||||
return name.startsWith("com.twelvemonkeys") ? "DEV" : "Unspecified";
|
||||
}
|
||||
|
@@ -1,3 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 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.spi;
|
||||
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
@@ -24,7 +52,7 @@ public abstract class ReaderWriterProviderInfo extends ProviderInfo {
|
||||
private final String[] writerSpiClassNames;
|
||||
private final Class[] outputTypes = new Class[] {ImageOutputStream.class};
|
||||
private final boolean supportsStandardStreamMetadata;
|
||||
private final String nativeStreameMetadataFormatName;
|
||||
private final String nativeStreamMetadataFormatName;
|
||||
private final String nativeStreamMetadataFormatClassName;
|
||||
private final String[] extraStreamMetadataFormatNames;
|
||||
private final String[] extraStreamMetadataFormatClassNames;
|
||||
@@ -38,7 +66,8 @@ public abstract class ReaderWriterProviderInfo extends ProviderInfo {
|
||||
* Creates a provider information instance based on the given class.
|
||||
*
|
||||
* @param infoClass the class to get provider information from.
|
||||
* The provider info will be taken from the class' package. @throws IllegalArgumentException if {@code pPackage == null}
|
||||
* The provider info will be taken from the class' package.
|
||||
* @throws IllegalArgumentException if {@code pPackage == null}
|
||||
*/
|
||||
protected ReaderWriterProviderInfo(final Class<? extends ReaderWriterProviderInfo> infoClass,
|
||||
final String[] formatNames,
|
||||
@@ -68,7 +97,7 @@ public abstract class ReaderWriterProviderInfo extends ProviderInfo {
|
||||
this.writerClassName = writerClassName;
|
||||
this.writerSpiClassNames = writerSpiClassNames;
|
||||
this.supportsStandardStreamMetadata = supportsStandardStreamMetadata;
|
||||
this.nativeStreameMetadataFormatName = nativeStreameMetadataFormatName;
|
||||
this.nativeStreamMetadataFormatName = nativeStreameMetadataFormatName;
|
||||
this.nativeStreamMetadataFormatClassName = nativeStreamMetadataFormatClassName;
|
||||
this.extraStreamMetadataFormatNames = extraStreamMetadataFormatNames;
|
||||
this.extraStreamMetadataFormatClassNames = extraStreamMetadataFormatClassNames;
|
||||
@@ -120,7 +149,7 @@ public abstract class ReaderWriterProviderInfo extends ProviderInfo {
|
||||
}
|
||||
|
||||
public String nativeStreamMetadataFormatName() {
|
||||
return nativeStreameMetadataFormatName;
|
||||
return nativeStreamMetadataFormatName;
|
||||
}
|
||||
|
||||
public String nativeStreamMetadataFormatClassName() {
|
||||
|
@@ -0,0 +1,55 @@
|
||||
package com.twelvemonkeys.imageio.stream;
|
||||
|
||||
import javax.imageio.stream.ImageOutputStream;
|
||||
import javax.imageio.stream.ImageOutputStreamImpl;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* SubImageOutputStream.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: harald.kuhr$
|
||||
* @version $Id: SubImageOutputStream.java,v 1.0 30/03/15 harald.kuhr Exp$
|
||||
*/
|
||||
public class SubImageOutputStream extends ImageOutputStreamImpl {
|
||||
private final ImageOutputStream stream;
|
||||
|
||||
public SubImageOutputStream(final ImageOutputStream stream) {
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
stream.write(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
stream.write(b, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
return stream.read();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
return stream.read(b, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCached() {
|
||||
return stream.isCached();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCachedMemory() {
|
||||
return stream.isCachedMemory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCachedFile() {
|
||||
return stream.isCachedFile();
|
||||
}
|
||||
}
|
@@ -28,14 +28,19 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.util;
|
||||
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.awt.image.IndexColorModel;
|
||||
import java.awt.image.*;
|
||||
|
||||
import static com.twelvemonkeys.lang.Validate.isTrue;
|
||||
import static com.twelvemonkeys.lang.Validate.notNull;
|
||||
|
||||
/**
|
||||
* Factory class for creating {@code ImageTypeSpecifier}s.
|
||||
* In most cases, this class will delegate to the corresponding methods in {@link ImageTypeSpecifier}.
|
||||
* Fixes some subtle bugs in {@code ImageTypeSpecifier}'s factory methods, but
|
||||
* in most cases, this class will delegate to the corresponding methods in {@link ImageTypeSpecifier}.
|
||||
*
|
||||
* @see javax.imageio.ImageTypeSpecifier
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
@@ -54,6 +59,20 @@ public final class ImageTypeSpecifiers {
|
||||
final int redMask, final int greenMask,
|
||||
final int blueMask, final int alphaMask,
|
||||
final int transferType, boolean isAlphaPremultiplied) {
|
||||
if (transferType == DataBuffer.TYPE_BYTE || transferType == DataBuffer.TYPE_USHORT) {
|
||||
// ImageTypeSpecifier unconditionally uses bits == 32, we'll use a workaround for BYTE/USHORT types
|
||||
notNull(colorSpace, "colorSpace");
|
||||
isTrue(colorSpace.getType() == ColorSpace.TYPE_RGB, colorSpace, "ColorSpace must be TYPE_RGB");
|
||||
isTrue(redMask != 0 || greenMask != 0 || blueMask != 0 || alphaMask != 0, "No mask has at least 1 bit set");
|
||||
|
||||
int bits = transferType == DataBuffer.TYPE_BYTE ? 8 : 16;
|
||||
|
||||
ColorModel colorModel = new DirectColorModel(colorSpace, bits, redMask, greenMask, blueMask, alphaMask,
|
||||
isAlphaPremultiplied, transferType);
|
||||
|
||||
return new ImageTypeSpecifier(colorModel, colorModel.createCompatibleSampleModel(1, 1));
|
||||
}
|
||||
|
||||
return ImageTypeSpecifier.createPacked(colorSpace, redMask, greenMask, blueMask, alphaMask, transferType, isAlphaPremultiplied);
|
||||
}
|
||||
|
||||
@@ -79,7 +98,11 @@ public final class ImageTypeSpecifiers {
|
||||
}
|
||||
|
||||
public static ImageTypeSpecifier createGrayscale(final int bits, final int dataType) {
|
||||
if (bits == 32 && dataType == DataBuffer.TYPE_INT) {
|
||||
if (bits == 16 && dataType == DataBuffer.TYPE_SHORT) {
|
||||
// As the ComponentColorModel is broken for 16 bit signed int, we'll use our own version
|
||||
return new Int16ImageTypeSpecifier(ColorSpace.getInstance(ColorSpace.CS_GRAY), new int[] {0}, false, false);
|
||||
}
|
||||
else if (bits == 32 && dataType == DataBuffer.TYPE_INT) {
|
||||
// As the ComponentColorModel is broken for 32 bit unsigned int, we'll use our own version
|
||||
return new UInt32ImageTypeSpecifier(ColorSpace.getInstance(ColorSpace.CS_GRAY), new int[] {0}, false, false);
|
||||
}
|
||||
@@ -89,7 +112,11 @@ public final class ImageTypeSpecifiers {
|
||||
}
|
||||
|
||||
public static ImageTypeSpecifier createGrayscale(final int bits, final int dataType, final boolean isAlphaPremultiplied) {
|
||||
if (bits == 32 && dataType == DataBuffer.TYPE_INT) {
|
||||
if (bits == 16 && dataType == DataBuffer.TYPE_SHORT) {
|
||||
// As the ComponentColorModel is broken for 16 bit signed int, we'll use our own version
|
||||
return new Int16ImageTypeSpecifier(ColorSpace.getInstance(ColorSpace.CS_GRAY), new int[] {0, 1}, true, isAlphaPremultiplied);
|
||||
}
|
||||
else if (bits == 32 && dataType == DataBuffer.TYPE_INT) {
|
||||
// As the ComponentColorModel is broken for 32 bit unsigned int, we'll use our own version
|
||||
return new UInt32ImageTypeSpecifier(ColorSpace.getInstance(ColorSpace.CS_GRAY), new int[] {0, 1}, true, isAlphaPremultiplied);
|
||||
}
|
||||
@@ -98,6 +125,34 @@ public final class ImageTypeSpecifiers {
|
||||
return ImageTypeSpecifier.createGrayscale(bits, dataType, false, isAlphaPremultiplied);
|
||||
}
|
||||
|
||||
public static ImageTypeSpecifier createPackedGrayscale(final ColorSpace colorSpace, final int bits, final int dataType) {
|
||||
notNull(colorSpace, "colorSpace");
|
||||
isTrue(colorSpace.getType() == ColorSpace.TYPE_GRAY, colorSpace, "ColorSpace must be TYPE_GRAY");
|
||||
isTrue(bits == 1 || bits == 2 || bits == 4, bits, "bits must be 1, 2, or 4: %s");
|
||||
isTrue(dataType == DataBuffer.TYPE_BYTE, dataType, "dataType must be TYPE_BYTE: %s");
|
||||
|
||||
int numEntries = 1 << bits;
|
||||
|
||||
byte[] arr = new byte[numEntries];
|
||||
byte[] arg = new byte[numEntries];
|
||||
byte[] arb = 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);
|
||||
|
||||
arr[i] = (byte) (rgb[0] * 255);
|
||||
arg[i] = (byte) (rgb[1] * 255);
|
||||
arb[i] = (byte) (rgb[2]* 255);
|
||||
}
|
||||
|
||||
ColorModel colorModel = new IndexColorModel(bits, numEntries, arr, arg, arb);
|
||||
SampleModel sampleModel = new MultiPixelPackedSampleModel(dataType, 1, 1, bits);
|
||||
|
||||
return new ImageTypeSpecifier(colorModel, sampleModel);
|
||||
}
|
||||
|
||||
public static ImageTypeSpecifier createIndexed(final byte[] redLUT, final byte[] greenLUT,
|
||||
final byte[] blueLUT, final byte[] alphaLUT,
|
||||
final int bits, final int dataType) {
|
||||
|
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import com.twelvemonkeys.imageio.color.Int16ComponentColorModel;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.awt.image.PixelInterleavedSampleModel;
|
||||
|
||||
/**
|
||||
* ImageTypeSpecifier for interleaved 16 bit signed integral samples.
|
||||
*
|
||||
* @see com.twelvemonkeys.imageio.color.Int16ColorModel
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: Int16ImageTypeSpecifier.java,v 1.0 24.01.11 17.51 haraldk Exp$
|
||||
*/
|
||||
final class Int16ImageTypeSpecifier extends ImageTypeSpecifier {
|
||||
Int16ImageTypeSpecifier(final ColorSpace cs, int[] bandOffsets, final boolean hasAlpha, final boolean isAlphaPremultiplied) {
|
||||
super(
|
||||
new Int16ComponentColorModel(cs, hasAlpha, isAlphaPremultiplied),
|
||||
new PixelInterleavedSampleModel(
|
||||
DataBuffer.TYPE_SHORT, 1, 1,
|
||||
cs.getNumComponents() + (hasAlpha ? 1 : 0),
|
||||
cs.getNumComponents() + (hasAlpha ? 1 : 0),
|
||||
bandOffsets
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
@@ -0,0 +1,68 @@
|
||||
package com.twelvemonkeys.imageio.color;
|
||||
|
||||
import com.twelvemonkeys.imageio.color.CIELabColorConverter.Illuminant;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
||||
/**
|
||||
* CIELabColorConverterTest.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: harald.kuhr$
|
||||
* @version $Id: CIELabColorConverterTest.java,v 1.0 22/10/15 harald.kuhr Exp$
|
||||
*/
|
||||
public class CIELabColorConverterTest {
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testNoIllumninant() {
|
||||
new CIELabColorConverter(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testD50() {
|
||||
CIELabColorConverter converter = new CIELabColorConverter(Illuminant.D50);
|
||||
float[] rgb = new float[3];
|
||||
|
||||
converter.toRGB(100, -128, -128, rgb);
|
||||
assertArrayEquals(new float[] {0, 255, 255}, rgb, 1);
|
||||
|
||||
converter.toRGB(100, 0, 0, rgb);
|
||||
assertArrayEquals(new float[] {255, 252, 220}, rgb, 5);
|
||||
|
||||
converter.toRGB(0, 0, 0, rgb);
|
||||
assertArrayEquals(new float[] {0, 0, 0}, rgb, 1);
|
||||
|
||||
converter.toRGB(100, 0, 127, rgb);
|
||||
assertArrayEquals(new float[] {255, 249, 0}, rgb, 5);
|
||||
|
||||
converter.toRGB(50, -128, 127, rgb);
|
||||
assertArrayEquals(new float[] {0, 152, 0}, rgb, 2);
|
||||
|
||||
converter.toRGB(50, 127, -128, rgb);
|
||||
assertArrayEquals(new float[] {222, 0, 255}, rgb, 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testD65() {
|
||||
CIELabColorConverter converter = new CIELabColorConverter(Illuminant.D65);
|
||||
float[] rgb = new float[3];
|
||||
|
||||
converter.toRGB(100, -128, -128, rgb);
|
||||
assertArrayEquals(new float[] {0, 255, 255}, rgb, 1);
|
||||
|
||||
converter.toRGB(100, 0, 0, rgb);
|
||||
assertArrayEquals(new float[] {255, 252, 255}, rgb, 5);
|
||||
|
||||
converter.toRGB(0, 0, 0, rgb);
|
||||
assertArrayEquals(new float[] {0, 0, 0}, rgb, 1);
|
||||
|
||||
converter.toRGB(100, 0, 127, rgb);
|
||||
assertArrayEquals(new float[] {255, 250, 0}, rgb, 5);
|
||||
|
||||
converter.toRGB(50, -128, 127, rgb);
|
||||
assertArrayEquals(new float[] {0, 152, 0}, rgb, 3);
|
||||
|
||||
converter.toRGB(50, 127, -128, rgb);
|
||||
assertArrayEquals(new float[] {184, 0, 255}, rgb, 5);
|
||||
}
|
||||
}
|
@@ -0,0 +1,160 @@
|
||||
package com.twelvemonkeys.imageio.spi;
|
||||
|
||||
import org.hamcrest.Description;
|
||||
import org.junit.Test;
|
||||
import org.junit.internal.matchers.TypeSafeMatcher;
|
||||
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.ImageWriter;
|
||||
import javax.imageio.metadata.IIOMetadataFormat;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.spi.ImageWriterSpi;
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* ReaderWriterProviderInfoTest.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: harald.kuhr$
|
||||
* @version $Id: ReaderWriterProviderInfoTest.java,v 1.0 02/06/16 harald.kuhr Exp$
|
||||
*/
|
||||
public abstract class ReaderWriterProviderInfoTest {
|
||||
|
||||
private final ReaderWriterProviderInfo providerInfo = createProviderInfo();
|
||||
|
||||
protected abstract ReaderWriterProviderInfo createProviderInfo();
|
||||
|
||||
protected final ReaderWriterProviderInfo getProviderInfo() {
|
||||
return providerInfo;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readerClassName() throws Exception {
|
||||
assertClassExists(providerInfo.readerClassName(), ImageReader.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readerSpiClassNames() throws Exception {
|
||||
assertClassesExist(providerInfo.readerSpiClassNames(), ImageReaderSpi.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void inputTypes() throws Exception {
|
||||
assertNotNull(providerInfo.inputTypes());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writerClassName() throws Exception {
|
||||
assertClassExists(providerInfo.writerClassName(), ImageWriter.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writerSpiClassNames() throws Exception {
|
||||
assertClassesExist(providerInfo.writerSpiClassNames(), ImageWriterSpi.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outputTypes() throws Exception {
|
||||
assertNotNull(providerInfo.outputTypes());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nativeStreamMetadataFormatClassName() throws Exception {
|
||||
assertClassExists(providerInfo.nativeStreamMetadataFormatClassName(), IIOMetadataFormat.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extraStreamMetadataFormatClassNames() throws Exception {
|
||||
assertClassesExist(providerInfo.extraStreamMetadataFormatClassNames(), IIOMetadataFormat.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nativeImageMetadataFormatClassName() throws Exception {
|
||||
assertClassExists(providerInfo.nativeImageMetadataFormatClassName(), IIOMetadataFormat.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extraImageMetadataFormatClassNames() throws Exception {
|
||||
assertClassesExist(providerInfo.extraImageMetadataFormatClassNames(), IIOMetadataFormat.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void formatNames() {
|
||||
String[] names = providerInfo.formatNames();
|
||||
assertNotNull(names);
|
||||
assertFalse(names.length == 0);
|
||||
|
||||
List<String> list = asList(names);
|
||||
|
||||
for (String name : list) {
|
||||
assertNotNull(name);
|
||||
assertFalse(name.isEmpty());
|
||||
|
||||
assertTrue(list.contains(name.toLowerCase()));
|
||||
assertTrue(list.contains(name.toUpperCase()));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void suffixes() {
|
||||
String[] suffixes = providerInfo.suffixes();
|
||||
assertNotNull(suffixes);
|
||||
assertFalse(suffixes.length == 0);
|
||||
|
||||
for (String suffix : suffixes) {
|
||||
assertNotNull(suffix);
|
||||
assertFalse(suffix.isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mimeTypes() {
|
||||
String[] mimeTypes = providerInfo.mimeTypes();
|
||||
assertNotNull(mimeTypes);
|
||||
assertFalse(mimeTypes.length == 0);
|
||||
|
||||
for (String mimeType : mimeTypes) {
|
||||
assertNotNull(mimeType);
|
||||
assertFalse(mimeType.isEmpty());
|
||||
|
||||
assertTrue(mimeType.length() > 1);
|
||||
assertTrue(mimeType.indexOf('/') > 0);
|
||||
assertTrue(mimeType.indexOf('/') < mimeType.length() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> void assertClassExists(final String className, final Class<T> type) {
|
||||
if (className != null) {
|
||||
try {
|
||||
final Class<?> cl = Class.forName(className);
|
||||
|
||||
assertThat(cl, new TypeSafeMatcher<Class<?>>() {
|
||||
@Override
|
||||
public boolean matchesSafely(Class<?> item) {
|
||||
return type.isAssignableFrom(cl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
description.appendText("is subclass of ").appendValue(type);
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
fail("Class not found: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> void assertClassesExist(final String[] classNames, final Class<T> type) {
|
||||
if (classNames != null) {
|
||||
for (String className : classNames) {
|
||||
assertClassExists(className, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -57,13 +57,13 @@ import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* ImageReaderAbstractTestCase
|
||||
* ImageReaderAbstractTest
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: ImageReaderAbstractTestCase.java,v 1.0 Apr 1, 2008 10:36:46 PM haraldk Exp$
|
||||
* @version $Id: ImageReaderAbstractTest.java,v 1.0 Apr 1, 2008 10:36:46 PM haraldk Exp$
|
||||
*/
|
||||
public abstract class ImageReaderAbstractTestCase<T extends ImageReader> {
|
||||
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...
|
||||
@@ -83,10 +83,7 @@ public abstract class ImageReaderAbstractTestCase<T extends ImageReader> {
|
||||
try {
|
||||
return getReaderClass().newInstance();
|
||||
}
|
||||
catch (InstantiationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
catch (IllegalAccessException e) {
|
||||
catch (InstantiationException | IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
@@ -1002,7 +999,7 @@ public abstract class ImageReaderAbstractTestCase<T extends ImageReader> {
|
||||
/*aspectRatio =*/ reader.getAspectRatio(-1);
|
||||
//assertEquals("Wrong aspect ratio", data.getDimension().width / (float) data.getDimension().height, aspectRatio, 0f);
|
||||
}
|
||||
catch (IndexOutOfBoundsException expected) {
|
||||
catch (IndexOutOfBoundsException ignore) {
|
||||
}
|
||||
catch (IOException e) {
|
||||
fail("Could not read image aspect ratio" + e);
|
||||
@@ -1391,7 +1388,7 @@ public abstract class ImageReaderAbstractTestCase<T extends ImageReader> {
|
||||
reader.read(0, param);
|
||||
fail("Expected to throw exception with illegal type specifier");
|
||||
}
|
||||
catch (IIOException expected) {
|
||||
catch (IIOException | IllegalArgumentException expected) {
|
||||
// TODO: This is thrown by ImageReader.getDestination. But are we happy with that?
|
||||
String message = expected.getMessage().toLowerCase();
|
||||
if (!(message.contains("destination") && message.contains("type"))) {
|
||||
@@ -1399,23 +1396,16 @@ public abstract class ImageReaderAbstractTestCase<T extends ImageReader> {
|
||||
throw expected;
|
||||
}
|
||||
}
|
||||
catch (IllegalArgumentException expected) {
|
||||
String message = expected.getMessage().toLowerCase();
|
||||
if (!(message.contains("destination") && message.contains("type"))) {
|
||||
// Allow this to bubble up, du to a bug in the Sun PNGImageReader
|
||||
throw expected;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<ImageTypeSpecifier> createIllegalTypes(Iterator<ImageTypeSpecifier> pValidTypes) {
|
||||
List<ImageTypeSpecifier> allTypes = new ArrayList<ImageTypeSpecifier>();
|
||||
List<ImageTypeSpecifier> allTypes = new ArrayList<>();
|
||||
for (int i = BufferedImage.TYPE_INT_RGB; i < BufferedImage.TYPE_BYTE_INDEXED; i++) {
|
||||
allTypes.add(ImageTypeSpecifier.createFromBufferedImageType(i));
|
||||
}
|
||||
|
||||
List<ImageTypeSpecifier> illegalTypes = new ArrayList<ImageTypeSpecifier>(allTypes);
|
||||
List<ImageTypeSpecifier> illegalTypes = new ArrayList<>(allTypes);
|
||||
while (pValidTypes.hasNext()) {
|
||||
ImageTypeSpecifier valid = pValidTypes.next();
|
||||
boolean removed = illegalTypes.remove(valid);
|
||||
@@ -1454,6 +1444,7 @@ public abstract class ImageReaderAbstractTestCase<T extends ImageReader> {
|
||||
assertEquals(reader.getHeight(0) + point.y, image.getHeight());
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Test
|
||||
public void testSetDestinationOffsetNull() throws IOException {
|
||||
final ImageReader reader = createReader();
|
||||
@@ -1629,12 +1620,12 @@ public abstract class ImageReaderAbstractTestCase<T extends ImageReader> {
|
||||
throw new IllegalArgumentException("input == null");
|
||||
}
|
||||
|
||||
sizes = new ArrayList<Dimension>();
|
||||
images = new ArrayList<BufferedImage>();
|
||||
sizes = new ArrayList<>();
|
||||
images = new ArrayList<>();
|
||||
|
||||
List<Dimension> sizes = pSizes;
|
||||
if (sizes == null) {
|
||||
sizes = new ArrayList<Dimension>();
|
||||
sizes = new ArrayList<>();
|
||||
if (pImages != null) {
|
||||
for (BufferedImage image : pImages) {
|
||||
sizes.add(new Dimension(image.getWidth(), image.getHeight()));
|
||||
@@ -1690,6 +1681,7 @@ public abstract class ImageReaderAbstractTestCase<T extends ImageReader> {
|
||||
return sizes.get(pIndex);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public BufferedImage getImage(final int pIndex) {
|
||||
return images.get(pIndex);
|
||||
}
|
@@ -1,12 +1,11 @@
|
||||
package com.twelvemonkeys.imageio.util;
|
||||
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.awt.image.IndexColorModel;
|
||||
import java.awt.image.*;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@@ -40,7 +39,7 @@ public class ImageTypeSpecifiersTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatePacked() {
|
||||
public void testCreatePacked32() {
|
||||
// TYPE_INT_RGB
|
||||
assertEquals(
|
||||
ImageTypeSpecifier.createPacked(sRGB, DCM_RED_MASK, DCM_GREEN_MASK, DCM_BLUE_MASK, 0, DataBuffer.TYPE_INT, false),
|
||||
@@ -61,31 +60,70 @@ public class ImageTypeSpecifiersTest {
|
||||
ImageTypeSpecifier.createPacked(sRGB, DCM_BGR_RED_MASK, DCM_BGR_GRN_MASK, DCM_BGR_BLU_MASK, 0, DataBuffer.TYPE_INT, false),
|
||||
ImageTypeSpecifiers.createPacked(sRGB, DCM_BGR_RED_MASK, DCM_BGR_GRN_MASK, DCM_BGR_BLU_MASK, 0, DataBuffer.TYPE_INT, false)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatePacked16() {
|
||||
// TYPE_USHORT_555_RGB
|
||||
assertEquals(
|
||||
ImageTypeSpecifier.createPacked(sRGB, DCM_555_RED_MASK, DCM_555_GRN_MASK, DCM_555_BLU_MASK, 0, DataBuffer.TYPE_USHORT, false),
|
||||
createPacked(sRGB, DCM_555_RED_MASK, DCM_555_GRN_MASK, DCM_555_BLU_MASK, 0, DataBuffer.TYPE_USHORT, false),
|
||||
ImageTypeSpecifiers.createPacked(sRGB, DCM_555_RED_MASK, DCM_555_GRN_MASK, DCM_555_BLU_MASK, 0, DataBuffer.TYPE_USHORT, false)
|
||||
);
|
||||
// "SHORT 555 RGB" (impossible for some reason)
|
||||
// assertEquals(
|
||||
// ImageTypeSpecifier.createPacked(sRGB, DCM_555_RED_MASK, DCM_555_GRN_MASK, DCM_555_BLU_MASK, 0, DataBuffer.TYPE_SHORT, false),
|
||||
// ImageTypeSpecifiers.createPacked(sRGB, DCM_555_RED_MASK, DCM_555_GRN_MASK, DCM_555_BLU_MASK, 0, DataBuffer.TYPE_SHORT, false)
|
||||
// );
|
||||
// "SHORT 555 RGB" (impossible, only BYTE, USHORT, INT supported)
|
||||
|
||||
// TYPE_USHORT_565_RGB
|
||||
assertEquals(
|
||||
ImageTypeSpecifier.createPacked(sRGB, DCM_565_RED_MASK, DCM_565_GRN_MASK, DCM_565_BLU_MASK, 0, DataBuffer.TYPE_USHORT, false),
|
||||
createPacked(sRGB, DCM_565_RED_MASK, DCM_565_GRN_MASK, DCM_565_BLU_MASK, 0, DataBuffer.TYPE_USHORT, false),
|
||||
ImageTypeSpecifiers.createPacked(sRGB, DCM_565_RED_MASK, DCM_565_GRN_MASK, DCM_565_BLU_MASK, 0, DataBuffer.TYPE_USHORT, false)
|
||||
);
|
||||
// "USHORT 4444 ARGB"
|
||||
assertEquals(
|
||||
ImageTypeSpecifier.createPacked(sRGB, 0xf00, 0xf0, 0xf, 0xf000, DataBuffer.TYPE_USHORT, false),
|
||||
createPacked(sRGB, 0xf00, 0xf0, 0xf, 0xf000, DataBuffer.TYPE_USHORT, false),
|
||||
ImageTypeSpecifiers.createPacked(sRGB, 0xf00, 0xf0, 0xf, 0xf000, DataBuffer.TYPE_USHORT, false)
|
||||
);
|
||||
// "USHORT 4444 ARGB PRE"
|
||||
assertEquals(
|
||||
ImageTypeSpecifier.createPacked(sRGB, 0xf00, 0xf0, 0xf, 0xf000, DataBuffer.TYPE_USHORT, true),
|
||||
createPacked(sRGB, 0xf00, 0xf0, 0xf, 0xf000, DataBuffer.TYPE_USHORT, true),
|
||||
ImageTypeSpecifiers.createPacked(sRGB, 0xf00, 0xf0, 0xf, 0xf000, DataBuffer.TYPE_USHORT, true)
|
||||
);
|
||||
|
||||
// Extra: Make sure color models bits is actually 16 (ImageTypeSpecifier equivalent returns 32)
|
||||
assertEquals(16, ImageTypeSpecifiers.createPacked(sRGB, DCM_565_RED_MASK, DCM_565_GRN_MASK, DCM_565_BLU_MASK, 0, DataBuffer.TYPE_USHORT, false).getColorModel().getPixelSize());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatePacked8() {
|
||||
// "BYTE 332 RGB"
|
||||
assertEquals(
|
||||
createPacked(sRGB, 0xe0, 0x1c, 0x03, 0x0, DataBuffer.TYPE_BYTE, false),
|
||||
ImageTypeSpecifiers.createPacked(sRGB, 0xe0, 0x1c, 0x3, 0x0, DataBuffer.TYPE_BYTE, false)
|
||||
);
|
||||
// "BYTE 2222 ARGB"
|
||||
assertEquals(
|
||||
createPacked(sRGB, 0xc0, 0x30, 0x0c, 0x03, DataBuffer.TYPE_BYTE, false),
|
||||
ImageTypeSpecifiers.createPacked(sRGB, 0xc0, 0x30, 0x0c, 0x03, DataBuffer.TYPE_BYTE, false)
|
||||
);
|
||||
// "BYTE 2222 ARGB PRE"
|
||||
assertEquals(
|
||||
createPacked(sRGB, 0xc0, 0x30, 0x0c, 0x03, DataBuffer.TYPE_BYTE, true),
|
||||
ImageTypeSpecifiers.createPacked(sRGB, 0xc0, 0x30, 0x0c, 0x03, DataBuffer.TYPE_BYTE, true)
|
||||
);
|
||||
|
||||
// Extra: Make sure color models bits is actually 8 (ImageTypeSpecifiers equivalent returns 32)
|
||||
assertEquals(8, ImageTypeSpecifiers.createPacked(sRGB, 0xc0, 0x30, 0x0c, 0x03, DataBuffer.TYPE_BYTE, false).getColorModel().getPixelSize());
|
||||
}
|
||||
|
||||
private ImageTypeSpecifier createPacked(final ColorSpace colorSpace,
|
||||
final int redMask, final int greenMask, final int blueMask, final int alphaMask,
|
||||
final int transferType, final boolean isAlphaPremultiplied) {
|
||||
Validate.isTrue(transferType == DataBuffer.TYPE_BYTE || transferType == DataBuffer.TYPE_USHORT, transferType, "transferType: %s");
|
||||
|
||||
int bits = transferType == DataBuffer.TYPE_BYTE ? 8 : 16;
|
||||
|
||||
ColorModel colorModel =
|
||||
new DirectColorModel(colorSpace, bits, redMask, greenMask, blueMask, alphaMask, isAlphaPremultiplied, transferType);
|
||||
|
||||
return new ImageTypeSpecifier(colorModel, colorModel.createCompatibleSampleModel(1, 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -337,11 +375,7 @@ public class ImageTypeSpecifiersTest {
|
||||
);
|
||||
|
||||
assertEquals(
|
||||
ImageTypeSpecifier.createGrayscale(16, DataBuffer.TYPE_SHORT, false), // NOTE: Unsigned TYPE_SHORT makes no sense...
|
||||
ImageTypeSpecifiers.createGrayscale(16, DataBuffer.TYPE_SHORT)
|
||||
);
|
||||
assertEquals(
|
||||
ImageTypeSpecifier.createGrayscale(16, DataBuffer.TYPE_SHORT, true),
|
||||
new Int16ImageTypeSpecifier(ColorSpace.getInstance(ColorSpace.CS_GRAY), new int[] {0}, false, false),
|
||||
ImageTypeSpecifiers.createGrayscale(16, DataBuffer.TYPE_SHORT)
|
||||
);
|
||||
}
|
||||
@@ -400,19 +434,11 @@ public class ImageTypeSpecifiersTest {
|
||||
);
|
||||
|
||||
assertEquals(
|
||||
ImageTypeSpecifier.createGrayscale(16, DataBuffer.TYPE_SHORT, false, false),
|
||||
new Int16ImageTypeSpecifier(ColorSpace.getInstance(ColorSpace.CS_GRAY), new int[] {0, 1}, true, false),
|
||||
ImageTypeSpecifiers.createGrayscale(16, DataBuffer.TYPE_SHORT, false)
|
||||
);
|
||||
assertEquals(
|
||||
ImageTypeSpecifier.createGrayscale(16, DataBuffer.TYPE_SHORT, false, true),
|
||||
ImageTypeSpecifiers.createGrayscale(16, DataBuffer.TYPE_SHORT, true)
|
||||
);
|
||||
assertEquals(
|
||||
ImageTypeSpecifier.createGrayscale(16, DataBuffer.TYPE_SHORT, true, false),
|
||||
ImageTypeSpecifiers.createGrayscale(16, DataBuffer.TYPE_SHORT, false)
|
||||
);
|
||||
assertEquals(
|
||||
ImageTypeSpecifier.createGrayscale(16, DataBuffer.TYPE_SHORT, true, true),
|
||||
new Int16ImageTypeSpecifier(ColorSpace.getInstance(ColorSpace.CS_GRAY), new int[] {0, 1}, true, true),
|
||||
ImageTypeSpecifiers.createGrayscale(16, DataBuffer.TYPE_SHORT, true)
|
||||
);
|
||||
}
|
||||
@@ -437,6 +463,30 @@ public class ImageTypeSpecifiersTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatePackedGrayscale1() {
|
||||
assertEquals(
|
||||
ImageTypeSpecifier.createGrayscale(1, DataBuffer.TYPE_BYTE, false),
|
||||
ImageTypeSpecifiers.createPackedGrayscale(GRAY, 1, DataBuffer.TYPE_BYTE)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatePackedGrayscale2() {
|
||||
assertEquals(
|
||||
ImageTypeSpecifier.createGrayscale(2, DataBuffer.TYPE_BYTE, false),
|
||||
ImageTypeSpecifiers.createPackedGrayscale(GRAY, 2, DataBuffer.TYPE_BYTE)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatePackedGrayscale4() {
|
||||
assertEquals(
|
||||
ImageTypeSpecifier.createGrayscale(4, DataBuffer.TYPE_BYTE, false),
|
||||
ImageTypeSpecifiers.createPackedGrayscale(GRAY, 4, DataBuffer.TYPE_BYTE)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateIndexedByteArrays1to8() {
|
||||
for (int bits = 1; bits <= 8; bits <<= 1) {
|
||||
@@ -562,7 +612,6 @@ public class ImageTypeSpecifiersTest {
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static byte[] createByteLut(final int count) {
|
||||
byte[] lut = new byte[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
|
@@ -63,6 +63,7 @@ public abstract class ImageWriterAbstractTestCase {
|
||||
|
||||
static {
|
||||
IIORegistry.getDefaultInstance().registerServiceProvider(new URLImageInputStreamSpi());
|
||||
ImageIO.setUseCache(false);
|
||||
}
|
||||
|
||||
protected abstract ImageWriter createImageWriter();
|
||||
@@ -120,23 +121,20 @@ public abstract class ImageWriterAbstractTestCase {
|
||||
|
||||
for (RenderedImage testData : getTestData()) {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
ImageOutputStream stream = ImageIO.createImageOutputStream(buffer);
|
||||
writer.setOutput(stream);
|
||||
|
||||
try {
|
||||
try (ImageOutputStream stream = ImageIO.createImageOutputStream(buffer)) {
|
||||
writer.setOutput(stream);
|
||||
writer.write(drawSomething((BufferedImage) testData));
|
||||
}
|
||||
catch (IOException e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
finally {
|
||||
stream.close(); // Force data to be written
|
||||
}
|
||||
|
||||
assertTrue("No image data written", buffer.size() > 0);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Test
|
||||
public void testWriteNull() throws IOException {
|
||||
ImageWriter writer = createImageWriter();
|
||||
|
@@ -1,8 +1,7 @@
|
||||
- Rename to imageio-common?
|
||||
- Separate modules for more for more plugins
|
||||
- The BMP reader supports some special formats not supported by Sun reader
|
||||
- PNM package is pretty complete, but useless, as it's provided by Sun? License?
|
||||
- WBMP?
|
||||
- XBM?
|
||||
- Add stream support for NIO and NIO2 stuff? Path, ByteChannel
|
||||
- Faster RAF support (need to buffer the slow readShort/readInt/readLong/readUnsignedShort/readUnsignedInt)
|
||||
- See if it's possible to use FileImageInputStream for FileChannels and FileInputStream to avoid the caching
|
||||
- And the above for output streams too...
|
||||
DONE:
|
||||
- Split up into separate plugins (modules), to allow easier configuration
|
30
imageio/imageio-hdr/pom.xml
Normal file
30
imageio/imageio-hdr/pom.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.3-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-hdr</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: HDR plugin</name>
|
||||
<description>
|
||||
ImageIO plugin for Radiance RGBE High Dynaimc Range format (HDR).
|
||||
</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>
|
||||
</dependencies>
|
||||
</project>
|
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 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.hdr;
|
||||
|
||||
/**
|
||||
* HDR.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: harald.kuhr$
|
||||
* @version $Id: HDR.java,v 1.0 27/07/15 harald.kuhr Exp$
|
||||
*/
|
||||
interface HDR {
|
||||
byte[] RADIANCE_MAGIC = new byte[] {'#', '?', 'R', 'A', 'D', 'I', 'A', 'N', 'C', 'E'};
|
||||
byte[] RGBE_MAGIC = new byte[] {'#', '?', 'R', 'G', 'B', 'E'};
|
||||
}
|
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 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.hdr;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* HDRHeader.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: harald.kuhr$
|
||||
* @version $Id: HDRHeader.java,v 1.0 27/07/15 harald.kuhr Exp$
|
||||
*/
|
||||
final class HDRHeader {
|
||||
private static final String KEY_FORMAT = "FORMAT=";
|
||||
private static final String KEY_PRIMARIES = "PRIMARIES=";
|
||||
private static final String KEY_EXPOSURE = "EXPOSURE=";
|
||||
private static final String KEY_GAMMA = "GAMMA=";
|
||||
private static final String KEY_SOFTWARE = "SOFTWARE=";
|
||||
|
||||
private int width;
|
||||
private int height;
|
||||
|
||||
private String software;
|
||||
|
||||
public static HDRHeader read(final ImageInputStream stream) throws IOException {
|
||||
HDRHeader header = new HDRHeader();
|
||||
|
||||
while (true) {
|
||||
String line = stream.readLine().trim();
|
||||
|
||||
if (line.isEmpty()) {
|
||||
// This is the last line before the dimensions
|
||||
break;
|
||||
}
|
||||
|
||||
if (line.startsWith("#?")) {
|
||||
// Program specifier, don't need that...
|
||||
}
|
||||
else if (line.startsWith("#")) {
|
||||
// Comment (ignore)
|
||||
}
|
||||
else if (line.startsWith(KEY_FORMAT)) {
|
||||
String format = line.substring(KEY_FORMAT.length()).trim();
|
||||
|
||||
if (!format.equals("32-bit_rle_rgbe")) {
|
||||
throw new IIOException("Unsupported format \"" + format + "\"(expected \"32-bit_rle_rgbe\")");
|
||||
}
|
||||
// TODO: Support the 32-bit_rle_xyze format
|
||||
}
|
||||
else if (line.startsWith(KEY_PRIMARIES)) {
|
||||
// TODO: We are going to need these values...
|
||||
// Should contain 8 (RGB + white point) coordinates
|
||||
}
|
||||
else if (line.startsWith(KEY_EXPOSURE)) {
|
||||
// TODO: We are going to need these values...
|
||||
}
|
||||
else if (line.startsWith(KEY_GAMMA)) {
|
||||
// TODO: We are going to need these values...
|
||||
}
|
||||
else if (line.startsWith(KEY_SOFTWARE)) {
|
||||
header.software = line.substring(KEY_SOFTWARE.length()).trim();
|
||||
}
|
||||
else {
|
||||
// ...ignore
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Proper parsing of width/height and orientation!
|
||||
String dimensionsLine = stream.readLine().trim();
|
||||
String[] dims = dimensionsLine.split("\\s");
|
||||
|
||||
if (dims[0].equals("-Y") && dims[2].equals("+X")) {
|
||||
header.height = Integer.parseInt(dims[1]);
|
||||
header.width = Integer.parseInt(dims[3]);
|
||||
|
||||
return header;
|
||||
}
|
||||
else {
|
||||
throw new IIOException("Unsupported RGBE orientation (expected \"-Y ... +X ...\")");
|
||||
}
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public String getSoftware() {
|
||||
return software;
|
||||
}
|
||||
}
|
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 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.hdr;
|
||||
|
||||
import com.twelvemonkeys.imageio.plugins.hdr.tonemap.DefaultToneMapper;
|
||||
import com.twelvemonkeys.imageio.plugins.hdr.tonemap.ToneMapper;
|
||||
|
||||
import javax.imageio.ImageReadParam;
|
||||
|
||||
/**
|
||||
* HDRImageReadParam.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: harald.kuhr$
|
||||
* @version $Id: HDRImageReadParam.java,v 1.0 28/07/15 harald.kuhr Exp$
|
||||
*/
|
||||
public final class HDRImageReadParam extends ImageReadParam {
|
||||
static final ToneMapper DEFAULT_TONE_MAPPER = new DefaultToneMapper(.1f);
|
||||
|
||||
private ToneMapper toneMapper = DEFAULT_TONE_MAPPER;
|
||||
|
||||
public ToneMapper getToneMapper() {
|
||||
return toneMapper;
|
||||
}
|
||||
|
||||
public void setToneMapper(final ToneMapper toneMapper) {
|
||||
this.toneMapper = toneMapper != null ? toneMapper : DEFAULT_TONE_MAPPER;
|
||||
}
|
||||
}
|
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 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.hdr;
|
||||
|
||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||
import com.twelvemonkeys.imageio.plugins.hdr.tonemap.ToneMapper;
|
||||
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import java.awt.*;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.awt.image.Raster;
|
||||
import java.awt.image.WritableRaster;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* HDRImageReader.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: harald.kuhr$
|
||||
* @version $Id: HDRImageReader.java,v 1.0 27/07/15 harald.kuhr Exp$
|
||||
*/
|
||||
public final class HDRImageReader extends ImageReaderBase {
|
||||
// Specs: http://radsite.lbl.gov/radiance/refer/filefmts.pdf
|
||||
|
||||
private HDRHeader header;
|
||||
|
||||
protected HDRImageReader(final ImageReaderSpi provider) {
|
||||
super(provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resetMembers() {
|
||||
header = null;
|
||||
}
|
||||
|
||||
private void readHeader() throws IOException {
|
||||
if (header == null) {
|
||||
header = HDRHeader.read(imageInput);
|
||||
|
||||
imageInput.flushBefore(imageInput.getStreamPosition());
|
||||
}
|
||||
|
||||
imageInput.seek(imageInput.getFlushedPosition());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth(int imageIndex) throws IOException {
|
||||
checkBounds(imageIndex);
|
||||
readHeader();
|
||||
|
||||
return header.getWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight(int imageIndex) throws IOException {
|
||||
checkBounds(imageIndex);
|
||||
readHeader();
|
||||
|
||||
return header.getHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IOException {
|
||||
checkBounds(imageIndex);
|
||||
readHeader();
|
||||
|
||||
ColorSpace sRGB = ColorSpace.getInstance(ColorSpace.CS_sRGB);
|
||||
return Collections.singletonList(ImageTypeSpecifiers.createInterleaved(sRGB, new int[] {0, 1, 2}, DataBuffer.TYPE_FLOAT, false, false)).iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedImage read(final int imageIndex, final ImageReadParam param) throws IOException {
|
||||
checkBounds(imageIndex);
|
||||
readHeader();
|
||||
|
||||
int width = getWidth(imageIndex);
|
||||
int height = getHeight(imageIndex);
|
||||
|
||||
BufferedImage destination = getDestination(param, getImageTypes(imageIndex), width, height);
|
||||
|
||||
Rectangle srcRegion = new Rectangle();
|
||||
Rectangle destRegion = new Rectangle();
|
||||
computeRegions(param, width, height, destination, srcRegion, destRegion);
|
||||
|
||||
WritableRaster raster = destination.getRaster()
|
||||
.createWritableChild(destRegion.x, destRegion.y, destRegion.width, destRegion.height, 0, 0, null);
|
||||
|
||||
int xSub = param != null ? param.getSourceXSubsampling() : 1;
|
||||
int ySub = param != null ? param.getSourceYSubsampling() : 1;
|
||||
|
||||
// Allow pluggable tone mapper via ImageReadParam
|
||||
ToneMapper toneMapper = param instanceof HDRImageReadParam
|
||||
? ((HDRImageReadParam) param).getToneMapper()
|
||||
: HDRImageReadParam.DEFAULT_TONE_MAPPER;
|
||||
|
||||
byte[] rowRGBE = new byte[width * 4];
|
||||
float[] rgb = new float[3];
|
||||
|
||||
processImageStarted(imageIndex);
|
||||
|
||||
// Process one scanline of RGBE data at a time
|
||||
for (int srcY = 0; srcY < height; srcY++) {
|
||||
int dstY = ((srcY - srcRegion.y) / ySub) + destRegion.y;
|
||||
if (dstY >= destRegion.height) {
|
||||
break;
|
||||
}
|
||||
|
||||
RGBE.readPixelsRawRLE(imageInput, rowRGBE, 0, width, 1);
|
||||
|
||||
if (srcY % ySub == 0 && dstY >= destRegion.y) {
|
||||
for (int srcX = srcRegion.x; srcX < srcRegion.x + srcRegion.width; srcX += xSub) {
|
||||
int dstX = ((srcX - srcRegion.x) / xSub) + destRegion.x;
|
||||
if (dstX >= destRegion.width) {
|
||||
break;
|
||||
}
|
||||
|
||||
RGBE.rgbe2float(rgb, rowRGBE, srcX * 4);
|
||||
|
||||
// Map/clamp RGB values into visible range, normally [0...1]
|
||||
toneMapper.map(rgb);
|
||||
|
||||
raster.setDataElements(dstX, dstY, rgb);
|
||||
}
|
||||
}
|
||||
|
||||
processImageProgress(srcY * 100f / height);
|
||||
|
||||
if (abortRequested()) {
|
||||
processReadAborted();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
processImageComplete();
|
||||
|
||||
return destination;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canReadRaster() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Raster readRaster(final int imageIndex, final ImageReadParam param) throws IOException {
|
||||
checkBounds(imageIndex);
|
||||
readHeader();
|
||||
|
||||
int width = getWidth(imageIndex);
|
||||
int height = getHeight(imageIndex);
|
||||
|
||||
Rectangle srcRegion = new Rectangle();
|
||||
Rectangle destRegion = new Rectangle();
|
||||
computeRegions(param, width, height, null, srcRegion, destRegion);
|
||||
destRegion = srcRegion; // We don't really care about destination for raster
|
||||
|
||||
BufferedImage destination = new BufferedImage(srcRegion.width, srcRegion.height, BufferedImage.TYPE_4BYTE_ABGR);
|
||||
WritableRaster raster = destination.getRaster();
|
||||
|
||||
int xSub = param != null ? param.getSourceXSubsampling() : 1;
|
||||
int ySub = param != null ? param.getSourceYSubsampling() : 1;
|
||||
|
||||
byte[] rowRGBE = new byte[width * 4];
|
||||
byte[] pixelRGBE = new byte[width];
|
||||
|
||||
processImageStarted(imageIndex);
|
||||
|
||||
// Process one scanline of RGBE data at a time
|
||||
for (int srcY = 0; srcY < height; srcY++) {
|
||||
int dstY = ((srcY - srcRegion.y) / ySub) + destRegion.y;
|
||||
if (dstY >= destRegion.height) {
|
||||
break;
|
||||
}
|
||||
|
||||
RGBE.readPixelsRawRLE(imageInput, rowRGBE, 0, width, 1);
|
||||
|
||||
if (srcY % ySub == 0 && dstY >= destRegion.y) {
|
||||
for (int srcX = srcRegion.x; srcX < srcRegion.x + srcRegion.width; srcX += xSub) {
|
||||
int dstX = ((srcX - srcRegion.x) / xSub) + destRegion.x;
|
||||
if (dstX >= destRegion.width) {
|
||||
break;
|
||||
}
|
||||
|
||||
System.arraycopy(rowRGBE, srcX * 4, pixelRGBE, 0, 4);
|
||||
raster.setDataElements(dstX, dstY, pixelRGBE);
|
||||
}
|
||||
}
|
||||
|
||||
processImageProgress(srcY * 100f / height);
|
||||
|
||||
if (abortRequested()) {
|
||||
processReadAborted();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
processImageComplete();
|
||||
|
||||
return destination.getRaster();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageReadParam getDefaultReadParam() {
|
||||
return new HDRImageReadParam();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IIOMetadata getImageMetadata(int imageIndex) throws IOException {
|
||||
checkBounds(imageIndex);
|
||||
readHeader();
|
||||
|
||||
return new HDRMetadata(header);
|
||||
}
|
||||
|
||||
public static void main(final String[] args) throws IOException {
|
||||
File file = new File(args[0]);
|
||||
|
||||
BufferedImage image = ImageIO.read(file);
|
||||
|
||||
showIt(image, file.getName());
|
||||
}
|
||||
}
|
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 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.hdr;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
|
||||
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* HDRImageReaderSpi.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: harald.kuhr$
|
||||
* @version $Id: HDRImageReaderSpi.java,v 1.0 27/07/15 harald.kuhr Exp$
|
||||
*/
|
||||
public final class HDRImageReaderSpi extends ImageReaderSpiBase {
|
||||
public HDRImageReaderSpi() {
|
||||
super(new HDRProviderInfo());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canDecodeInput(final Object source) throws IOException {
|
||||
if (!(source instanceof ImageInputStream)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ImageInputStream stream = (ImageInputStream) source;
|
||||
|
||||
stream.mark();
|
||||
|
||||
try {
|
||||
// NOTE: All images I have found starts with #?RADIANCE (or has no #? line at all),
|
||||
// although some sources claim that #?RGBE is also used.
|
||||
byte[] magic = new byte[HDR.RADIANCE_MAGIC.length];
|
||||
stream.readFully(magic);
|
||||
|
||||
return Arrays.equals(HDR.RADIANCE_MAGIC, magic)
|
||||
|| Arrays.equals(HDR.RGBE_MAGIC, Arrays.copyOf(magic, 6));
|
||||
}
|
||||
finally {
|
||||
stream.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageReader createReaderInstance(Object extension) throws IOException {
|
||||
return new HDRImageReader(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription(final Locale locale) {
|
||||
return "Radiance RGBE High Dynaimc Range (HDR) image reader";
|
||||
}
|
||||
}
|
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 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.hdr;
|
||||
|
||||
import com.twelvemonkeys.imageio.AbstractMetadata;
|
||||
|
||||
import javax.imageio.metadata.IIOMetadataNode;
|
||||
|
||||
final class HDRMetadata extends AbstractMetadata {
|
||||
private final HDRHeader header;
|
||||
|
||||
HDRMetadata(final HDRHeader header) {
|
||||
this.header = header;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardChromaNode() {
|
||||
IIOMetadataNode chroma = new IIOMetadataNode("Chroma");
|
||||
|
||||
IIOMetadataNode csType = new IIOMetadataNode("ColorSpaceType");
|
||||
chroma.appendChild(csType);
|
||||
csType.setAttribute("name", "RGB");
|
||||
// TODO: Support XYZ
|
||||
|
||||
IIOMetadataNode numChannels = new IIOMetadataNode("NumChannels");
|
||||
numChannels.setAttribute("value", "3");
|
||||
chroma.appendChild(numChannels);
|
||||
|
||||
IIOMetadataNode blackIsZero = new IIOMetadataNode("BlackIsZero");
|
||||
blackIsZero.setAttribute("value", "TRUE");
|
||||
chroma.appendChild(blackIsZero);
|
||||
|
||||
return chroma;
|
||||
}
|
||||
|
||||
// No compression
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardCompressionNode() {
|
||||
IIOMetadataNode node = new IIOMetadataNode("Compression");
|
||||
|
||||
IIOMetadataNode compressionTypeName = new IIOMetadataNode("CompressionTypeName");
|
||||
compressionTypeName.setAttribute("value", "RLE");
|
||||
node.appendChild(compressionTypeName);
|
||||
|
||||
IIOMetadataNode lossless = new IIOMetadataNode("Lossless");
|
||||
lossless.setAttribute("value", "TRUE");
|
||||
node.appendChild(lossless);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardDataNode() {
|
||||
IIOMetadataNode node = new IIOMetadataNode("Data");
|
||||
|
||||
IIOMetadataNode sampleFormat = new IIOMetadataNode("SampleFormat");
|
||||
sampleFormat.setAttribute("value", "UnsignedIntegral");
|
||||
node.appendChild(sampleFormat);
|
||||
|
||||
IIOMetadataNode bitsPerSample = new IIOMetadataNode("BitsPerSample");
|
||||
bitsPerSample.setAttribute("value", "8 8 8 8");
|
||||
node.appendChild(bitsPerSample);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardDimensionNode() {
|
||||
IIOMetadataNode dimension = new IIOMetadataNode("Dimension");
|
||||
|
||||
// TODO: Support other orientations
|
||||
IIOMetadataNode imageOrientation = new IIOMetadataNode("ImageOrientation");
|
||||
imageOrientation.setAttribute("value", "Normal");
|
||||
dimension.appendChild(imageOrientation);
|
||||
|
||||
return dimension;
|
||||
}
|
||||
|
||||
// No document node
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardTextNode() {
|
||||
if (header.getSoftware() != null) {
|
||||
IIOMetadataNode text = new IIOMetadataNode("Text");
|
||||
|
||||
IIOMetadataNode textEntry = new IIOMetadataNode("TextEntry");
|
||||
textEntry.setAttribute("keyword", "Software");
|
||||
textEntry.setAttribute("value", header.getSoftware());
|
||||
text.appendChild(textEntry);
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// No tiling
|
||||
|
||||
// No transparency
|
||||
}
|
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 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.hdr;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
||||
|
||||
/**
|
||||
* HDRProviderInfo.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: harald.kuhr$
|
||||
* @version $Id: HDRProviderInfo.java,v 1.0 27/07/15 harald.kuhr Exp$
|
||||
*/
|
||||
final class HDRProviderInfo extends ReaderWriterProviderInfo {
|
||||
protected HDRProviderInfo() {
|
||||
super(
|
||||
HDRProviderInfo.class,
|
||||
new String[] {"HDR", "hdr", "RGBE", "rgbe"},
|
||||
new String[] {"hdr", "rgbe", "xyze", "pic"},
|
||||
new String[] {"image/vnd.radiance"},
|
||||
"com.twelvemonkeys.imageio.plugins.hdr.HDRImageReader",
|
||||
new String[]{"com.twelvemonkeys.imageio.plugins.hdr.HDRImageReaderSpi"},
|
||||
null,
|
||||
null,
|
||||
false, null, null, null, null,
|
||||
true, null, null, null, null
|
||||
);
|
||||
}
|
||||
}
|
@@ -0,0 +1,494 @@
|
||||
package com.twelvemonkeys.imageio.plugins.hdr;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* This file contains code to read and write four byte rgbe file format
|
||||
* developed by Greg Ward. It handles the conversions between rgbe and
|
||||
* pixels consisting of floats. The data is assumed to be an array of floats.
|
||||
* By default there are three floats per pixel in the order red, green, blue.
|
||||
* (RGBE_DATA_??? values control this.) Only the mimimal header reading and
|
||||
* writing is implemented. Each routine does error checking and will return
|
||||
* a status value as defined below. This code is intended as a skeleton so
|
||||
* feel free to modify it to suit your needs. <P>
|
||||
* <p/>
|
||||
* Ported to Java and restructured by Kenneth Russell. <BR>
|
||||
* posted to http://www.graphics.cornell.edu/~bjw/ <BR>
|
||||
* written by Bruce Walter (bjw@graphics.cornell.edu) 5/26/95 <BR>
|
||||
* based on code written by Greg Ward <BR>
|
||||
* <p/>
|
||||
* Source: https://java.net/projects/jogl-demos/sources/svn/content/trunk/src/demos/hdr/RGBE.java
|
||||
*/
|
||||
final class RGBE {
|
||||
// Flags indicating which fields in a Header are valid
|
||||
private static final int VALID_PROGRAMTYPE = 0x01;
|
||||
private static final int VALID_GAMMA = 0x02;
|
||||
private static final int VALID_EXPOSURE = 0x04;
|
||||
|
||||
private static final String gammaString = "GAMMA=";
|
||||
private static final String exposureString = "EXPOSURE=";
|
||||
|
||||
private static final Pattern widthHeightPattern = Pattern.compile("-Y (\\d+) \\+X (\\d+)");
|
||||
|
||||
public static class Header {
|
||||
// Indicates which fields are valid
|
||||
private int valid;
|
||||
|
||||
// Listed at beginning of file to identify it after "#?".
|
||||
// Defaults to "RGBE"
|
||||
private String programType;
|
||||
|
||||
// Image has already been gamma corrected with given gamma.
|
||||
// Defaults to 1.0 (no correction)
|
||||
private float gamma;
|
||||
|
||||
// A value of 1.0 in an image corresponds to <exposure>
|
||||
// watts/steradian/m^2. Defaults to 1.0.
|
||||
private float exposure;
|
||||
|
||||
// Width and height of image
|
||||
private int width;
|
||||
private int height;
|
||||
|
||||
private Header(int valid,
|
||||
String programType,
|
||||
float gamma,
|
||||
float exposure,
|
||||
int width,
|
||||
int height) {
|
||||
this.valid = valid;
|
||||
this.programType = programType;
|
||||
this.gamma = gamma;
|
||||
this.exposure = exposure;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public boolean isProgramTypeValid() {
|
||||
return ((valid & VALID_PROGRAMTYPE) != 0);
|
||||
}
|
||||
|
||||
public boolean isGammaValid() {
|
||||
return ((valid & VALID_GAMMA) != 0);
|
||||
}
|
||||
|
||||
public boolean isExposureValid() {
|
||||
return ((valid & VALID_EXPOSURE) != 0);
|
||||
}
|
||||
|
||||
public String getProgramType() {
|
||||
return programType;
|
||||
}
|
||||
|
||||
public float getGamma() {
|
||||
return gamma;
|
||||
}
|
||||
|
||||
public float getExposure() {
|
||||
return exposure;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
if (isProgramTypeValid()) {
|
||||
buf.append(" Program type: ");
|
||||
buf.append(getProgramType());
|
||||
}
|
||||
buf.append(" Gamma");
|
||||
if (isGammaValid()) {
|
||||
buf.append(" [valid]");
|
||||
}
|
||||
buf.append(": ");
|
||||
buf.append(getGamma());
|
||||
buf.append(" Exposure");
|
||||
if (isExposureValid()) {
|
||||
buf.append(" [valid]");
|
||||
}
|
||||
buf.append(": ");
|
||||
buf.append(getExposure());
|
||||
buf.append(" Width: ");
|
||||
buf.append(getWidth());
|
||||
buf.append(" Height: ");
|
||||
buf.append(getHeight());
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public static Header readHeader(final DataInput in) throws IOException {
|
||||
int valid = 0;
|
||||
String programType = null;
|
||||
float gamma = 1.0f;
|
||||
float exposure = 1.0f;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
|
||||
String buf = in.readLine();
|
||||
if (buf == null) {
|
||||
throw new IOException("Unexpected EOF reading magic token");
|
||||
}
|
||||
if (buf.charAt(0) == '#' && buf.charAt(1) == '?') {
|
||||
valid |= VALID_PROGRAMTYPE;
|
||||
programType = buf.substring(2);
|
||||
buf = in.readLine();
|
||||
if (buf == null) {
|
||||
throw new IOException("Unexpected EOF reading line after magic token");
|
||||
}
|
||||
}
|
||||
|
||||
boolean foundFormat = false;
|
||||
boolean done = false;
|
||||
while (!done) {
|
||||
if (buf.equals("FORMAT=32-bit_rle_rgbe")) {
|
||||
foundFormat = true;
|
||||
}
|
||||
else if (buf.startsWith(gammaString)) {
|
||||
valid |= VALID_GAMMA;
|
||||
gamma = Float.parseFloat(buf.substring(gammaString.length()));
|
||||
}
|
||||
else if (buf.startsWith(exposureString)) {
|
||||
valid |= VALID_EXPOSURE;
|
||||
exposure = Float.parseFloat(buf.substring(exposureString.length()));
|
||||
}
|
||||
else {
|
||||
Matcher m = widthHeightPattern.matcher(buf);
|
||||
if (m.matches()) {
|
||||
width = Integer.parseInt(m.group(2));
|
||||
height = Integer.parseInt(m.group(1));
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!done) {
|
||||
buf = in.readLine();
|
||||
if (buf == null) {
|
||||
throw new IOException("Unexpected EOF reading header");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundFormat) {
|
||||
throw new IOException("No FORMAT specifier found");
|
||||
}
|
||||
|
||||
return new Header(valid, programType, gamma, exposure, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple read routine. Will not correctly handle run length encoding.
|
||||
*/
|
||||
public static void readPixels(DataInput in, float[] data, int numpixels) throws IOException {
|
||||
byte[] rgbe = new byte[4];
|
||||
float[] rgb = new float[3];
|
||||
int offset = 0;
|
||||
|
||||
while (numpixels-- > 0) {
|
||||
in.readFully(rgbe);
|
||||
|
||||
rgbe2float(rgb, rgbe, 0);
|
||||
|
||||
data[offset++] = rgb[0];
|
||||
data[offset++] = rgb[1];
|
||||
data[offset++] = rgb[2];
|
||||
}
|
||||
}
|
||||
|
||||
public static void readPixelsRaw(DataInput in, byte[] data, int offset, int numpixels) throws IOException {
|
||||
int numExpected = 4 * numpixels;
|
||||
in.readFully(data, offset, numExpected);
|
||||
}
|
||||
|
||||
public static void readPixelsRawRLE(DataInput in, byte[] data, int offset,
|
||||
int scanline_width, int num_scanlines) throws IOException {
|
||||
byte[] rgbe = new byte[4];
|
||||
byte[] scanline_buffer = null;
|
||||
int ptr, ptr_end;
|
||||
int count;
|
||||
byte[] buf = new byte[2];
|
||||
|
||||
if ((scanline_width < 8) || (scanline_width > 0x7fff)) {
|
||||
// run length encoding is not allowed so read flat
|
||||
readPixelsRaw(in, data, offset, scanline_width * num_scanlines);
|
||||
}
|
||||
|
||||
// read in each successive scanline
|
||||
while (num_scanlines > 0) {
|
||||
in.readFully(rgbe);
|
||||
|
||||
if ((rgbe[0] != 2) || (rgbe[1] != 2) || ((rgbe[2] & 0x80) != 0)) {
|
||||
// this file is not run length encoded
|
||||
data[offset++] = rgbe[0];
|
||||
data[offset++] = rgbe[1];
|
||||
data[offset++] = rgbe[2];
|
||||
data[offset++] = rgbe[3];
|
||||
readPixelsRaw(in, data, offset, scanline_width * num_scanlines - 1);
|
||||
}
|
||||
|
||||
if ((((rgbe[2] & 0xFF) << 8) | (rgbe[3] & 0xFF)) != scanline_width) {
|
||||
throw new IOException("Wrong scanline width " +
|
||||
(((rgbe[2] & 0xFF) << 8) | (rgbe[3] & 0xFF)) +
|
||||
", expected " + scanline_width);
|
||||
}
|
||||
|
||||
if (scanline_buffer == null) {
|
||||
scanline_buffer = new byte[4 * scanline_width];
|
||||
}
|
||||
|
||||
ptr = 0;
|
||||
// read each of the four channels for the scanline into the buffer
|
||||
for (int i = 0; i < 4; i++) {
|
||||
ptr_end = (i + 1) * scanline_width;
|
||||
while (ptr < ptr_end) {
|
||||
in.readFully(buf);
|
||||
|
||||
if ((buf[0] & 0xFF) > 128) {
|
||||
// a run of the same value
|
||||
count = (buf[0] & 0xFF) - 128;
|
||||
if ((count == 0) || (count > ptr_end - ptr)) {
|
||||
throw new IOException("Bad scanline data");
|
||||
}
|
||||
while (count-- > 0) {
|
||||
scanline_buffer[ptr++] = buf[1];
|
||||
}
|
||||
}
|
||||
else {
|
||||
// a non-run
|
||||
count = buf[0] & 0xFF;
|
||||
if ((count == 0) || (count > ptr_end - ptr)) {
|
||||
throw new IOException("Bad scanline data");
|
||||
}
|
||||
scanline_buffer[ptr++] = buf[1];
|
||||
if (--count > 0) {
|
||||
in.readFully(scanline_buffer, ptr, count);
|
||||
ptr += count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// copy byte data to output
|
||||
for (int i = 0; i < scanline_width; i++) {
|
||||
data[offset++] = scanline_buffer[i];
|
||||
data[offset++] = scanline_buffer[i + scanline_width];
|
||||
data[offset++] = scanline_buffer[i + 2 * scanline_width];
|
||||
data[offset++] = scanline_buffer[i + 3 * scanline_width];
|
||||
}
|
||||
num_scanlines--;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard conversion from float pixels to rgbe pixels.
|
||||
*/
|
||||
public static void float2rgbe(byte[] rgbe, float red, float green, float blue) {
|
||||
float v;
|
||||
int e;
|
||||
|
||||
v = red;
|
||||
if (green > v) {
|
||||
v = green;
|
||||
}
|
||||
if (blue > v) {
|
||||
v = blue;
|
||||
}
|
||||
if (v < 1e-32f) {
|
||||
rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;
|
||||
}
|
||||
else {
|
||||
FracExp fe = frexp(v);
|
||||
v = (float) (fe.getFraction() * 256.0 / v);
|
||||
rgbe[0] = (byte) (red * v);
|
||||
rgbe[1] = (byte) (green * v);
|
||||
rgbe[2] = (byte) (blue * v);
|
||||
rgbe[3] = (byte) (fe.getExponent() + 128);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard conversion from rgbe to float pixels. Note: Ward uses
|
||||
* ldexp(col+0.5,exp-(128+8)). However we wanted pixels in the
|
||||
* range [0,1] to map back into the range [0,1].
|
||||
*/
|
||||
public static void rgbe2float(float[] rgb, byte[] rgbe, int startRGBEOffset) {
|
||||
float f;
|
||||
|
||||
if (rgbe[startRGBEOffset + 3] != 0) { // nonzero pixel
|
||||
f = (float) ldexp(1.0, (rgbe[startRGBEOffset + 3] & 0xFF) - (128 + 8));
|
||||
rgb[0] = (rgbe[startRGBEOffset + 0] & 0xFF) * f;
|
||||
rgb[1] = (rgbe[startRGBEOffset + 1] & 0xFF) * f;
|
||||
rgb[2] = (rgbe[startRGBEOffset + 2] & 0xFF) * f;
|
||||
}
|
||||
else {
|
||||
rgb[0] = 0;
|
||||
rgb[1] = 0;
|
||||
rgb[2] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static double ldexp(double value, int exp) {
|
||||
if (!finite(value) || value == 0.0) {
|
||||
return value;
|
||||
}
|
||||
value = scalbn(value, exp);
|
||||
// No good way to indicate errno (want to avoid throwing
|
||||
// exceptions because don't know about stability of calculations)
|
||||
// if(!finite(value)||value==0.0) errno = ERANGE;
|
||||
return value;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Internals only below this point
|
||||
//
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Math routines, some fdlibm-derived
|
||||
//
|
||||
|
||||
static class FracExp {
|
||||
private double fraction;
|
||||
private int exponent;
|
||||
|
||||
public FracExp(double fraction, int exponent) {
|
||||
this.fraction = fraction;
|
||||
this.exponent = exponent;
|
||||
}
|
||||
|
||||
public double getFraction() {
|
||||
return fraction;
|
||||
}
|
||||
|
||||
public int getExponent() {
|
||||
return exponent;
|
||||
}
|
||||
}
|
||||
|
||||
private static final double two54 = 1.80143985094819840000e+16; // 43500000 00000000
|
||||
private static final double twom54 = 5.55111512312578270212e-17; // 0x3C900000 0x00000000
|
||||
private static final double huge = 1.0e+300;
|
||||
private static final double tiny = 1.0e-300;
|
||||
|
||||
private static int hi(double x) {
|
||||
long bits = Double.doubleToRawLongBits(x);
|
||||
return (int) (bits >>> 32);
|
||||
}
|
||||
|
||||
private static int lo(double x) {
|
||||
long bits = Double.doubleToRawLongBits(x);
|
||||
return (int) bits;
|
||||
}
|
||||
|
||||
private static double fromhilo(int hi, int lo) {
|
||||
return Double.longBitsToDouble((((long) hi) << 32) |
|
||||
(((long) lo) & 0xFFFFFFFFL));
|
||||
}
|
||||
|
||||
private static FracExp frexp(double x) {
|
||||
int hx = hi(x);
|
||||
int ix = 0x7fffffff & hx;
|
||||
int lx = lo(x);
|
||||
int e = 0;
|
||||
if (ix >= 0x7ff00000 || ((ix | lx) == 0)) {
|
||||
return new FracExp(x, e); // 0,inf,nan
|
||||
}
|
||||
if (ix < 0x00100000) { // subnormal
|
||||
x *= two54;
|
||||
hx = hi(x);
|
||||
ix = hx & 0x7fffffff;
|
||||
e = -54;
|
||||
}
|
||||
e += (ix >> 20) - 1022;
|
||||
hx = (hx & 0x800fffff) | 0x3fe00000;
|
||||
lx = lo(x);
|
||||
return new FracExp(fromhilo(hx, lx), e);
|
||||
}
|
||||
|
||||
private static boolean finite(double x) {
|
||||
int hx;
|
||||
hx = hi(x);
|
||||
return (((hx & 0x7fffffff) - 0x7ff00000) >> 31) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* copysign(double x, double y) <BR>
|
||||
* copysign(x,y) returns a value with the magnitude of x and
|
||||
* with the sign bit of y.
|
||||
*/
|
||||
private static double copysign(double x, double y) {
|
||||
return fromhilo((hi(x) & 0x7fffffff) | (hi(y) & 0x80000000), lo(x));
|
||||
}
|
||||
|
||||
/**
|
||||
* scalbn (double x, int n) <BR>
|
||||
* scalbn(x,n) returns x* 2**n computed by exponent
|
||||
* manipulation rather than by actually performing an
|
||||
* exponentiation or a multiplication.
|
||||
*/
|
||||
private static double scalbn(double x, int n) {
|
||||
int hx = hi(x);
|
||||
int lx = lo(x);
|
||||
int k = (hx & 0x7ff00000) >> 20; // extract exponent
|
||||
if (k == 0) { // 0 or subnormal x
|
||||
if ((lx | (hx & 0x7fffffff)) == 0) {
|
||||
return x; // +-0
|
||||
}
|
||||
x *= two54;
|
||||
hx = hi(x);
|
||||
k = ((hx & 0x7ff00000) >> 20) - 54;
|
||||
if (n < -50000) {
|
||||
return tiny * x; // underflow
|
||||
}
|
||||
}
|
||||
if (k == 0x7ff) {
|
||||
return x + x; // NaN or Inf
|
||||
}
|
||||
k = k + n;
|
||||
if (k > 0x7fe) {
|
||||
return huge * copysign(huge, x); // overflow
|
||||
}
|
||||
if (k > 0) {
|
||||
// normal result
|
||||
return fromhilo((hx & 0x800fffff) | (k << 20), lo(x));
|
||||
}
|
||||
if (k <= -54) {
|
||||
if (n > 50000) {
|
||||
// in case integer overflow in n+k
|
||||
return huge * copysign(huge, x); // overflow
|
||||
}
|
||||
else {
|
||||
return tiny * copysign(tiny, x); // underflow
|
||||
}
|
||||
}
|
||||
k += 54; // subnormal result
|
||||
x = fromhilo((hx & 0x800fffff) | (k << 20), lo(x));
|
||||
return x * twom54;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Test harness
|
||||
//
|
||||
|
||||
public static void main(String[] args) {
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
try {
|
||||
DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream(args[i])));
|
||||
Header header = RGBE.readHeader(in);
|
||||
System.err.println("Header for file \"" + args[i] + "\":");
|
||||
System.err.println(" " + header);
|
||||
byte[] data = new byte[header.getWidth() * header.getHeight() * 4];
|
||||
readPixelsRawRLE(in, data, 0, header.getWidth(), header.getHeight());
|
||||
in.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 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.hdr.tonemap;
|
||||
|
||||
/**
|
||||
* DefaultToneMapper.
|
||||
* <p/>
|
||||
* Normalizes values to range [0...1] using:
|
||||
*
|
||||
* <p><em>V<sub>out</sub> = V<sub>in</sub> / (V<sub>in</sub> + C)</em></p>
|
||||
*
|
||||
* Where <em>C</em> is constant.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: harald.kuhr$
|
||||
* @version $Id: DefaultToneMapper.java,v 1.0 28/07/15 harald.kuhr Exp$
|
||||
*/
|
||||
public final class DefaultToneMapper implements ToneMapper {
|
||||
|
||||
private final float constant;
|
||||
|
||||
public DefaultToneMapper() {
|
||||
this(1);
|
||||
}
|
||||
|
||||
public DefaultToneMapper(final float constant) {
|
||||
this.constant = constant;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void map(final float[] rgb) {
|
||||
// Default Vo = Vi / (Vi + 1)
|
||||
for (int i = 0; i < rgb.length; i++) {
|
||||
rgb[i] = rgb[i] / (rgb[i] + constant);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 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.hdr.tonemap;
|
||||
|
||||
/**
|
||||
* GammaToneMapper.
|
||||
* <p/>
|
||||
* Normalizes values to range [0...1] using:
|
||||
*
|
||||
* <p><em>V<sub>out</sub> = A V<sub>in</sub><sup>\u03b3</sup></em></p>
|
||||
*
|
||||
* Where <em>A</em> is constant and <em>\u03b3</em> is the gamma.
|
||||
* Values > 1 are clamped.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: harald.kuhr$
|
||||
* @version $Id: GammaToneMapper.java,v 1.0 28/07/15 harald.kuhr Exp$
|
||||
*/
|
||||
public final class GammaToneMapper implements ToneMapper {
|
||||
|
||||
private final float constant;
|
||||
private final float gamma;
|
||||
|
||||
public GammaToneMapper() {
|
||||
this(0.5f, .25f);
|
||||
}
|
||||
|
||||
public GammaToneMapper(final float constant, final float gamma) {
|
||||
this.constant = constant;
|
||||
this.gamma = gamma;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void map(final float[] rgb) {
|
||||
// Gamma Vo = A * Vi^y
|
||||
for (int i = 0; i < rgb.length; i++) {
|
||||
rgb[i] = Math.min(1f, (float) (constant * Math.pow(rgb[i], gamma)));
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 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.hdr.tonemap;
|
||||
|
||||
/**
|
||||
* NullToneMapper.
|
||||
* <p/>
|
||||
* This {@code ToneMapper} does *not* normalize or clamp values
|
||||
* to range [0...1], but leaves the values as-is.
|
||||
* Useful for applications that implements custom tone mapping.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: harald.kuhr$
|
||||
* @version $Id: NullToneMapper.java,v 1.0 28/07/15 harald.kuhr Exp$
|
||||
*/
|
||||
public final class NullToneMapper implements ToneMapper {
|
||||
@Override
|
||||
public void map(float[] rgb) {
|
||||
}
|
||||
}
|
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 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.hdr.tonemap;
|
||||
|
||||
/**
|
||||
* ToneMapper.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: harald.kuhr$
|
||||
* @version $Id: ToneMapper.java,v 1.0 28/07/15 harald.kuhr Exp$
|
||||
*/
|
||||
public interface ToneMapper {
|
||||
void map(float[] rgb);
|
||||
}
|
@@ -0,0 +1 @@
|
||||
com.twelvemonkeys.imageio.plugins.hdr.HDRImageReaderSpi
|
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 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.hdr;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import java.awt.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* TGAImageReaderTest
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: TGAImageReaderTest.java,v 1.0 03.07.14 22:28 haraldk Exp$
|
||||
*/
|
||||
public class HDRImageReaderTest extends ImageReaderAbstractTest<HDRImageReader> {
|
||||
@Override
|
||||
protected List<TestData> getTestData() {
|
||||
return Arrays.asList(
|
||||
new TestData(getClassLoaderResource("/hdr/memorial_o876.hdr"), new Dimension(512, 768))
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ImageReaderSpi createProvider() {
|
||||
return new HDRImageReaderSpi();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<HDRImageReader> getReaderClass() {
|
||||
return HDRImageReader.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HDRImageReader createReader() {
|
||||
return new HDRImageReader(createProvider());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getFormatNames() {
|
||||
return Arrays.asList("HDR", "hdr", "RGBE", "rgbe");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getSuffixes() {
|
||||
return Arrays.asList("hdr", "rgbe", "xyze");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getMIMETypes() {
|
||||
return Collections.singletonList(
|
||||
"image/vnd.radiance"
|
||||
);
|
||||
}
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
package com.twelvemonkeys.imageio.plugins.hdr;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfoTest;
|
||||
|
||||
/**
|
||||
* HDRProviderInfoTest.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: harald.kuhr$
|
||||
* @version $Id: HDRProviderInfoTest.java,v 1.0 02/06/16 harald.kuhr Exp$
|
||||
*/
|
||||
public class HDRProviderInfoTest extends ReaderWriterProviderInfoTest {
|
||||
|
||||
@Override
|
||||
protected ReaderWriterProviderInfo createProviderInfo() {
|
||||
return new HDRProviderInfo();
|
||||
}
|
||||
}
|
BIN
imageio/imageio-hdr/src/test/resources/hdr/memorial_o876.hdr
Normal file
BIN
imageio/imageio-hdr/src/test/resources/hdr/memorial_o876.hdr
Normal file
Binary file not shown.
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.1-SNAPSHOT</version>
|
||||
<version>3.3-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-icns</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: ICNS plugin</name>
|
||||
@@ -18,7 +18,7 @@
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
<classifier>tests</classifier>
|
||||
<type>test-jar</type>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
@@ -1,3 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 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.icns;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
||||
@@ -19,7 +47,7 @@ final class ICNSProviderInfo extends ReaderWriterProviderInfo {
|
||||
"image/x-apple-icons", // Common extension MIME
|
||||
},
|
||||
"com.twelvemonkeys.imageio.plugins.icns.ICNSImageReader",
|
||||
new String[] {"com.twelvemonkeys.imageio.plugins.ics.ICNImageReaderSpi"},
|
||||
new String[] {"com.twelvemonkeys.imageio.plugins.icns.ICNSImageReaderSpi"},
|
||||
null, null,
|
||||
false, null, null, null, null,
|
||||
true, null, null, null, null
|
||||
|
@@ -28,7 +28,7 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.icns;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase;
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -37,6 +37,7 @@ import javax.imageio.spi.ImageReaderSpi;
|
||||
import java.awt.*;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -46,7 +47,7 @@ import java.util.List;
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: ICNSImageReaderTest.java,v 1.0 25.10.11 18:44 haraldk Exp$
|
||||
*/
|
||||
public class ICNSImageReaderTest extends ImageReaderAbstractTestCase {
|
||||
public class ICNSImageReaderTest extends ImageReaderAbstractTest {
|
||||
@Override
|
||||
protected List<TestData> getTestData() {
|
||||
return Arrays.asList(
|
||||
@@ -119,17 +120,17 @@ public class ICNSImageReaderTest extends ImageReaderAbstractTestCase {
|
||||
|
||||
@Override
|
||||
protected List<String> getFormatNames() {
|
||||
return Arrays.asList("icns");
|
||||
return Collections.singletonList("icns");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getSuffixes() {
|
||||
return Arrays.asList("icns");
|
||||
return Collections.singletonList("icns");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getMIMETypes() {
|
||||
return Arrays.asList("image/x-apple-icons");
|
||||
return Collections.singletonList("image/x-apple-icons");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@@ -0,0 +1,19 @@
|
||||
package com.twelvemonkeys.imageio.plugins.icns;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfoTest;
|
||||
|
||||
/**
|
||||
* ICNSProviderInfoTest.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: harald.kuhr$
|
||||
* @version $Id: ICNSProviderInfoTest.java,v 1.0 02/06/16 harald.kuhr Exp$
|
||||
*/
|
||||
public class ICNSProviderInfoTest extends ReaderWriterProviderInfoTest {
|
||||
|
||||
@Override
|
||||
protected ReaderWriterProviderInfo createProviderInfo() {
|
||||
return new ICNSProviderInfo();
|
||||
}
|
||||
}
|
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.1-SNAPSHOT</version>
|
||||
<version>3.3-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-iff</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: IFF plugin</name>
|
||||
@@ -21,7 +21,7 @@
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
<classifier>tests</classifier>
|
||||
<type>test-jar</type>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
@@ -1,3 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 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.iff;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
||||
|
@@ -28,7 +28,7 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.iff;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase;
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
@@ -39,11 +39,10 @@ import java.awt.image.ColorModel;
|
||||
import java.awt.image.IndexColorModel;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* IFFImageReaderTestCase
|
||||
@@ -52,7 +51,7 @@ import static org.junit.Assert.assertTrue;
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: IFFImageReaderTestCase.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$
|
||||
*/
|
||||
public class IFFImageReaderTest extends ImageReaderAbstractTestCase<IFFImageReader> {
|
||||
public class IFFImageReaderTest extends ImageReaderAbstractTest<IFFImageReader> {
|
||||
protected List<TestData> getTestData() {
|
||||
return Arrays.asList(
|
||||
// 32 bit - Ok
|
||||
@@ -93,7 +92,7 @@ public class IFFImageReaderTest extends ImageReaderAbstractTestCase<IFFImageRead
|
||||
}
|
||||
|
||||
protected List<String> getFormatNames() {
|
||||
return Arrays.asList("iff");
|
||||
return Collections.singletonList("iff");
|
||||
}
|
||||
|
||||
protected List<String> getSuffixes() {
|
||||
|
@@ -0,0 +1,19 @@
|
||||
package com.twelvemonkeys.imageio.plugins.iff;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfoTest;
|
||||
|
||||
/**
|
||||
* IFFProviderInfoTest.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: harald.kuhr$
|
||||
* @version $Id: IFFProviderInfoTest.java,v 1.0 02/06/16 harald.kuhr Exp$
|
||||
*/
|
||||
public class IFFProviderInfoTest extends ReaderWriterProviderInfoTest {
|
||||
|
||||
@Override
|
||||
protected ReaderWriterProviderInfo createProviderInfo() {
|
||||
return new IFFProviderInfo();
|
||||
}
|
||||
}
|
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.1-SNAPSHOT</version>
|
||||
<version>3.3-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-jpeg</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: JPEG plugin</name>
|
||||
@@ -20,7 +20,7 @@
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
<classifier>tests</classifier>
|
||||
<type>test-jar</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
|
@@ -28,6 +28,7 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.jpeg;
|
||||
|
||||
import com.twelvemonkeys.imageio.color.YCbCrConverter;
|
||||
import com.twelvemonkeys.imageio.metadata.Directory;
|
||||
import com.twelvemonkeys.imageio.metadata.Entry;
|
||||
import com.twelvemonkeys.imageio.metadata.exif.TIFF;
|
||||
@@ -102,7 +103,7 @@ final class EXIFThumbnailReader extends ThumbnailReader {
|
||||
thumbnail = readJPEG();
|
||||
}
|
||||
|
||||
cachedThumbnail = pixelsExposed ? null : new SoftReference<BufferedImage>(thumbnail);
|
||||
cachedThumbnail = pixelsExposed ? null : new SoftReference<>(thumbnail);
|
||||
|
||||
return thumbnail;
|
||||
}
|
||||
@@ -132,14 +133,10 @@ final class EXIFThumbnailReader extends ThumbnailReader {
|
||||
input = new SequenceInputStream(new ByteArrayInputStream(fakeEmptyExif), input);
|
||||
|
||||
try {
|
||||
MemoryCacheImageInputStream stream = new MemoryCacheImageInputStream(input);
|
||||
|
||||
try {
|
||||
try (MemoryCacheImageInputStream stream = new MemoryCacheImageInputStream(input)) {
|
||||
return readJPEGThumbnail(reader, stream);
|
||||
}
|
||||
finally {
|
||||
stream.close();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
input.close();
|
||||
@@ -195,15 +192,15 @@ final class EXIFThumbnailReader extends ThumbnailReader {
|
||||
break;
|
||||
case 6:
|
||||
// YCbCr
|
||||
for (int i = 0, thumbDataLength = thumbData.length; i < thumbDataLength; i += 3) {
|
||||
JPEGImageReader.YCbCrConverter.convertYCbCr2RGB(thumbData, thumbData, i);
|
||||
for (int i = 0; i < thumbSize; i += 3) {
|
||||
YCbCrConverter.convertYCbCr2RGB(thumbData, thumbData, i);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IIOException("Unknown PhotometricInterpretation value for uncompressed EXIF thumbnail (expected 2 or 6): " + interpretation);
|
||||
}
|
||||
|
||||
return ThumbnailReader.readRawThumbnail(thumbData, thumbData.length, 0, w, h);
|
||||
return ThumbnailReader.readRawThumbnail(thumbData, thumbSize, 0, w, h);
|
||||
}
|
||||
|
||||
throw new IIOException("Missing StripOffsets tag for uncompressed EXIF thumbnail");
|
||||
|
@@ -28,9 +28,9 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.jpeg;
|
||||
|
||||
import com.twelvemonkeys.image.ImageUtil;
|
||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||
import com.twelvemonkeys.imageio.color.ColorSpaces;
|
||||
import com.twelvemonkeys.imageio.color.YCbCrConverter;
|
||||
import com.twelvemonkeys.imageio.metadata.CompoundDirectory;
|
||||
import com.twelvemonkeys.imageio.metadata.Directory;
|
||||
import com.twelvemonkeys.imageio.metadata.Entry;
|
||||
@@ -112,7 +112,7 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
private static final Map<Integer, List<String>> SEGMENT_IDENTIFIERS = createSegmentIds();
|
||||
|
||||
private static Map<Integer, List<String>> createSegmentIds() {
|
||||
Map<Integer, List<String>> map = new LinkedHashMap<Integer, List<String>>();
|
||||
Map<Integer, List<String>> map = new LinkedHashMap<>();
|
||||
|
||||
// Need all APP markers to be able to re-generate proper metadata later
|
||||
for (int appMarker = JPEG.APP0; appMarker <= JPEG.APP15; appMarker++) {
|
||||
@@ -152,10 +152,10 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
/** Cached list of JPEG segments we filter from the underlying stream */
|
||||
private List<JPEGSegment> segments;
|
||||
|
||||
JPEGImageReader(final ImageReaderSpi provider, final ImageReader delegate) {
|
||||
protected JPEGImageReader(final ImageReaderSpi provider, final ImageReader delegate) {
|
||||
super(provider);
|
||||
this.delegate = Validate.notNull(delegate);
|
||||
|
||||
this.delegate = Validate.notNull(delegate);
|
||||
progressDelegator = new ProgressDelegator();
|
||||
}
|
||||
|
||||
@@ -224,7 +224,7 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
JPEGColorSpace csType = getSourceCSType(getJFIF(), getAdobeDCT(), getSOF());
|
||||
|
||||
if (types == null || !types.hasNext() || csType == JPEGColorSpace.CMYK || csType == JPEGColorSpace.YCCK) {
|
||||
ArrayList<ImageTypeSpecifier> typeList = new ArrayList<ImageTypeSpecifier>();
|
||||
ArrayList<ImageTypeSpecifier> typeList = new ArrayList<>();
|
||||
// Add the standard types, we can always convert to these
|
||||
typeList.add(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR));
|
||||
typeList.add(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB));
|
||||
@@ -268,10 +268,15 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
@Override
|
||||
public ImageTypeSpecifier getRawImageType(int imageIndex) throws IOException {
|
||||
// If delegate can determine the spec, we'll just go with that
|
||||
ImageTypeSpecifier rawType = delegate.getRawImageType(imageIndex);
|
||||
try {
|
||||
ImageTypeSpecifier rawType = delegate.getRawImageType(imageIndex);
|
||||
|
||||
if (rawType != null) {
|
||||
return rawType;
|
||||
if (rawType != null) {
|
||||
return rawType;
|
||||
}
|
||||
}
|
||||
catch (NullPointerException ignore) {
|
||||
// Fall through
|
||||
}
|
||||
|
||||
// Otherwise, consult the image metadata
|
||||
@@ -298,7 +303,9 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
super.setInput(input, seekForwardOnly, ignoreMetadata);
|
||||
|
||||
// JPEGSegmentImageInputStream that filters out/skips bad/unnecessary segments
|
||||
delegate.setInput(imageInput != null ? new JPEGSegmentImageInputStream(imageInput) : null, seekForwardOnly, ignoreMetadata);
|
||||
delegate.setInput(imageInput != null
|
||||
? new JPEGSegmentImageInputStream(imageInput)
|
||||
: null, seekForwardOnly, ignoreMetadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -311,22 +318,10 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
assertInput();
|
||||
checkBounds(imageIndex);
|
||||
|
||||
// CompoundDirectory exif = getExif();
|
||||
// if (exif != null) {
|
||||
// System.err.println("exif: " + exif);
|
||||
// System.err.println("Orientation: " + exif.getEntryById(TIFF.TAG_ORIENTATION));
|
||||
// Entry exifIFDEntry = exif.getEntryById(TIFF.TAG_EXIF_IFD);
|
||||
//
|
||||
// if (exifIFDEntry != null) {
|
||||
// Directory exifIFD = (Directory) exifIFDEntry.getValue();
|
||||
// System.err.println("PixelXDimension: " + exifIFD.getEntryById(EXIF.TAG_PIXEL_X_DIMENSION));
|
||||
// System.err.println("PixelYDimension: " + exifIFD.getEntryById(EXIF.TAG_PIXEL_Y_DIMENSION));
|
||||
// }
|
||||
// }
|
||||
|
||||
SOFSegment sof = getSOF();
|
||||
ICC_Profile profile = getEmbeddedICCProfile(false);
|
||||
AdobeDCTSegment adobeDCT = getAdobeDCT();
|
||||
boolean bogusAdobeDCT = false;
|
||||
|
||||
if (adobeDCT != null && (adobeDCT.getTransform() == AdobeDCTSegment.YCC && sof.componentsInFrame() != 3 ||
|
||||
adobeDCT.getTransform() == AdobeDCTSegment.YCCK && sof.componentsInFrame() != 4)) {
|
||||
@@ -337,6 +332,7 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
sof.marker & 0xf, sof.componentsInFrame()
|
||||
));
|
||||
|
||||
bogusAdobeDCT = true;
|
||||
adobeDCT = null;
|
||||
}
|
||||
|
||||
@@ -345,11 +341,11 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
// We need to apply ICC profile unless the profile is sRGB/default gray (whatever that is)
|
||||
// - or only filter out the bad ICC profiles in the JPEGSegmentImageInputStream.
|
||||
if (delegate.canReadRaster() && (
|
||||
bogusAdobeDCT ||
|
||||
sourceCSType == JPEGColorSpace.CMYK ||
|
||||
sourceCSType == JPEGColorSpace.YCCK ||
|
||||
adobeDCT != null && adobeDCT.getTransform() == AdobeDCTSegment.YCCK ||
|
||||
profile != null && !ColorSpaces.isCS_sRGB(profile)) ||
|
||||
sourceCSType == JPEGColorSpace.YCbCr && getRawImageType(imageIndex) != null) { // TODO: Issue warning?
|
||||
profile != null && !ColorSpaces.isCS_sRGB(profile) ||
|
||||
sourceCSType == JPEGColorSpace.YCbCr && getRawImageType(imageIndex) != null)) { // TODO: Issue warning?
|
||||
if (DEBUG) {
|
||||
System.out.println("Reading using raster and extra conversion");
|
||||
System.out.println("ICC color profile: " + profile);
|
||||
@@ -371,6 +367,10 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
int origHeight = getHeight(imageIndex);
|
||||
|
||||
Iterator<ImageTypeSpecifier> imageTypes = getImageTypes(imageIndex);
|
||||
// TODO: Avoid creating destination here, if possible (as it saves time and memory)
|
||||
// If YCbCr or RGB, we could instead create a BufferedImage around the converted raster directly.
|
||||
// If YCCK or CMYK, we could instead create a BufferedImage around the converted raster,
|
||||
// leaving the fourth band as alpha (or pretend it's not there, by creating a child raster).
|
||||
BufferedImage image = getDestination(param, imageTypes, origWidth, origHeight);
|
||||
WritableRaster destination = image.getRaster();
|
||||
|
||||
@@ -404,6 +404,7 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
if (DEBUG) {
|
||||
System.err.println("Converting from " + intendedCS + " to " + (image.getColorModel().getColorSpace().isCS_sRGB() ? "sRGB" : image.getColorModel().getColorSpace()));
|
||||
}
|
||||
|
||||
convert = new ColorConvertOp(intendedCS, image.getColorModel().getColorSpace(), null);
|
||||
}
|
||||
// Else, pass through with no conversion
|
||||
@@ -462,10 +463,13 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
|
||||
// Apply source color conversion from implicit color space
|
||||
if (csType == JPEGColorSpace.YCbCr || csType == JPEGColorSpace.YCbCrA) {
|
||||
YCbCrConverter.convertYCbCr2RGB(raster);
|
||||
convertYCbCr2RGB(raster);
|
||||
}
|
||||
else if (csType == JPEGColorSpace.YCCK) {
|
||||
YCbCrConverter.convertYCCK2CMYK(raster);
|
||||
// TODO: Need to rethink this (non-) inversion, see #147
|
||||
// TODO: Allow param to specify inversion, or possibly the PDF decode array
|
||||
// flag0 bit 15, blend = 1 see http://graphicdesign.stackexchange.com/questions/12894/cmyk-jpegs-extracted-from-pdf-appear-inverted
|
||||
convertYCCK2CMYK(raster);
|
||||
}
|
||||
else if (csType == JPEGColorSpace.CMYK) {
|
||||
invertCMYK(raster);
|
||||
@@ -620,7 +624,7 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
private ICC_Profile ensureDisplayProfile(final ICC_Profile profile) {
|
||||
protected ICC_Profile ensureDisplayProfile(final ICC_Profile profile) {
|
||||
// NOTE: This is probably not the right way to do it... :-P
|
||||
// TODO: Consider moving method to ColorSpaces class or new class in imageio.color package
|
||||
|
||||
@@ -670,16 +674,11 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
|
||||
segments = JPEGSegmentUtil.readSegments(imageInput, SEGMENT_IDENTIFIERS);
|
||||
}
|
||||
catch (IIOException ignore) {
|
||||
catch (IIOException | IllegalArgumentException ignore) {
|
||||
if (DEBUG) {
|
||||
ignore.printStackTrace();
|
||||
}
|
||||
}
|
||||
catch (IllegalArgumentException foo) {
|
||||
if (DEBUG) {
|
||||
foo.printStackTrace();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
imageInput.reset();
|
||||
}
|
||||
@@ -699,7 +698,7 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
if ((marker == ALL_APP_MARKERS && segment.marker() >= JPEG.APP0 && segment.marker() <= JPEG.APP15 || segment.marker() == marker)
|
||||
&& (identifier == null || identifier.equals(segment.identifier()))) {
|
||||
if (appSegments == Collections.EMPTY_LIST) {
|
||||
appSegments = new ArrayList<JPEGSegment>(segments.size());
|
||||
appSegments = new ArrayList<>(segments.size());
|
||||
}
|
||||
|
||||
appSegments.add(segment);
|
||||
@@ -831,7 +830,7 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
return data;
|
||||
}
|
||||
|
||||
ICC_Profile getEmbeddedICCProfile(final boolean allowBadIndexes) throws IOException {
|
||||
protected ICC_Profile getEmbeddedICCProfile(final boolean allowBadIndexes) throws IOException {
|
||||
// ICC v 1.42 (2006) annex B:
|
||||
// APP2 marker (0xFFE2) + 2 byte length + ASCII 'ICC_PROFILE' + 0 (termination)
|
||||
// + 1 byte chunk number + 1 byte chunk count (allows ICC profiles chunked in multiple APP2 segments)
|
||||
@@ -853,7 +852,7 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
return null;
|
||||
}
|
||||
|
||||
return readICCProfileSafe(stream);
|
||||
return readICCProfileSafe(stream, allowBadIndexes);
|
||||
}
|
||||
else if (!segments.isEmpty()) {
|
||||
// NOTE: This is probably over-complicated, as I've never encountered ICC_PROFILE chunks out of order...
|
||||
@@ -900,15 +899,17 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
streams[badICC ? i : chunkNumber - 1] = stream;
|
||||
}
|
||||
|
||||
return readICCProfileSafe(new SequenceInputStream(Collections.enumeration(Arrays.asList(streams))));
|
||||
return readICCProfileSafe(new SequenceInputStream(Collections.enumeration(Arrays.asList(streams))), allowBadIndexes);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private ICC_Profile readICCProfileSafe(final InputStream stream) throws IOException {
|
||||
private ICC_Profile readICCProfileSafe(final InputStream stream, final boolean allowBadProfile) throws IOException {
|
||||
try {
|
||||
return ICC_Profile.getInstance(stream);
|
||||
ICC_Profile profile = ICC_Profile.getInstance(stream);
|
||||
|
||||
return allowBadProfile ? profile : ColorSpaces.validateProfile(profile);
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
// NOTE: Throws either IllegalArgumentException or CMMException, depending on platform.
|
||||
@@ -940,6 +941,11 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
delegate.abort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageReadParam getDefaultReadParam() {
|
||||
return delegate.getDefaultReadParam();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean readerSupportsThumbnails() {
|
||||
return true; // We support EXIF, JFIF and JFXX style thumbnails
|
||||
@@ -949,7 +955,7 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
checkBounds(imageIndex);
|
||||
|
||||
if (thumbnails == null) {
|
||||
thumbnails = new ArrayList<ThumbnailReader>();
|
||||
thumbnails = new ArrayList<>();
|
||||
ThumbnailReadProgressListener thumbnailProgressDelegator = new ThumbnailProgressDelegate();
|
||||
|
||||
// Read JFIF thumbnails if present
|
||||
@@ -1101,105 +1107,32 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Static inner class for lazy-loading of conversion tables.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author Original code by Werner Randelshofer
|
||||
*/
|
||||
static final class YCbCrConverter {
|
||||
/** Define tables for YCC->RGB color space conversion. */
|
||||
private final static int SCALEBITS = 16;
|
||||
private final static int MAXJSAMPLE = 255;
|
||||
private final static int CENTERJSAMPLE = 128;
|
||||
private final static int ONE_HALF = 1 << (SCALEBITS - 1);
|
||||
public static void convertYCbCr2RGB(final Raster raster) {
|
||||
final int height = raster.getHeight();
|
||||
final int width = raster.getWidth();
|
||||
final byte[] data = ((DataBufferByte) raster.getDataBuffer()).getData();
|
||||
|
||||
private final static int[] Cr_R_LUT = new int[MAXJSAMPLE + 1];
|
||||
private final static int[] Cb_B_LUT = new int[MAXJSAMPLE + 1];
|
||||
private final static int[] Cr_G_LUT = new int[MAXJSAMPLE + 1];
|
||||
private final static int[] Cb_G_LUT = new int[MAXJSAMPLE + 1];
|
||||
|
||||
/**
|
||||
* Initializes tables for YCC->RGB color space conversion.
|
||||
*/
|
||||
private static void buildYCCtoRGBtable() {
|
||||
if (DEBUG) {
|
||||
System.err.println("Building YCC conversion table");
|
||||
}
|
||||
|
||||
for (int i = 0, x = -CENTERJSAMPLE; i <= MAXJSAMPLE; i++, x++) {
|
||||
// i is the actual input pixel value, in the range 0..MAXJSAMPLE
|
||||
// The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE
|
||||
// Cr=>R value is nearest int to 1.40200 * x
|
||||
Cr_R_LUT[i] = (int) ((1.40200 * (1 << SCALEBITS) + 0.5) * x + ONE_HALF) >> SCALEBITS;
|
||||
// Cb=>B value is nearest int to 1.77200 * x
|
||||
Cb_B_LUT[i] = (int) ((1.77200 * (1 << SCALEBITS) + 0.5) * x + ONE_HALF) >> SCALEBITS;
|
||||
// Cr=>G value is scaled-up -0.71414 * x
|
||||
Cr_G_LUT[i] = -(int) (0.71414 * (1 << SCALEBITS) + 0.5) * x;
|
||||
// Cb=>G value is scaled-up -0.34414 * x
|
||||
// We also add in ONE_HALF so that need not do it in inner loop
|
||||
Cb_G_LUT[i] = -(int) ((0.34414) * (1 << SCALEBITS) + 0.5) * x + ONE_HALF;
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
YCbCrConverter.convertYCbCr2RGB(data, data, (x + y * width) * 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
buildYCCtoRGBtable();
|
||||
}
|
||||
public static void convertYCCK2CMYK(final Raster raster) {
|
||||
final int height = raster.getHeight();
|
||||
final int width = raster.getWidth();
|
||||
final byte[] data = ((DataBufferByte) raster.getDataBuffer()).getData();
|
||||
|
||||
static void convertYCbCr2RGB(final Raster raster) {
|
||||
final int height = raster.getHeight();
|
||||
final int width = raster.getWidth();
|
||||
final byte[] data = ((DataBufferByte) raster.getDataBuffer()).getData();
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
convertYCbCr2RGB(data, data, (x + y * width) * 3);
|
||||
}
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
int offset = (x + y * width) * 4;
|
||||
// YCC -> CMY
|
||||
YCbCrConverter.convertYCbCr2RGB(data, data, offset);
|
||||
// Inverse K
|
||||
data[offset + 3] = (byte) (0xff - data[offset + 3] & 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
static void convertYCbCr2RGB(final byte[] yCbCr, final byte[] rgb, final int offset) {
|
||||
int y = yCbCr[offset ] & 0xff;
|
||||
int cr = yCbCr[offset + 2] & 0xff;
|
||||
int cb = yCbCr[offset + 1] & 0xff;
|
||||
|
||||
rgb[offset ] = clamp(y + Cr_R_LUT[cr]);
|
||||
rgb[offset + 1] = clamp(y + (Cb_G_LUT[cb] + Cr_G_LUT[cr] >> SCALEBITS));
|
||||
rgb[offset + 2] = clamp(y + Cb_B_LUT[cb]);
|
||||
}
|
||||
|
||||
static void convertYCCK2CMYK(final Raster raster) {
|
||||
final int height = raster.getHeight();
|
||||
final int width = raster.getWidth();
|
||||
final byte[] data = ((DataBufferByte) raster.getDataBuffer()).getData();
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
convertYCCK2CMYK(data, data, (x + y * width) * 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void convertYCCK2CMYK(byte[] ycck, byte[] cmyk, int offset) {
|
||||
// Inverted
|
||||
int y = 255 - ycck[offset ] & 0xff;
|
||||
int cb = 255 - ycck[offset + 1] & 0xff;
|
||||
int cr = 255 - ycck[offset + 2] & 0xff;
|
||||
int k = 255 - ycck[offset + 3] & 0xff;
|
||||
|
||||
int cmykC = MAXJSAMPLE - (y + Cr_R_LUT[cr]);
|
||||
int cmykM = MAXJSAMPLE - (y + (Cb_G_LUT[cb] + Cr_G_LUT[cr] >> SCALEBITS));
|
||||
int cmykY = MAXJSAMPLE - (y + Cb_B_LUT[cb]);
|
||||
|
||||
cmyk[offset ] = clamp(cmykC);
|
||||
cmyk[offset + 1] = clamp(cmykM);
|
||||
cmyk[offset + 2] = clamp(cmykY);
|
||||
cmyk[offset + 3] = (byte) k; // K passes through unchanged
|
||||
}
|
||||
|
||||
private static byte clamp(int val) {
|
||||
return (byte) Math.max(0, Math.min(255, val));
|
||||
}
|
||||
}
|
||||
|
||||
private class ProgressDelegator extends ProgressListenerBase implements IIOReadUpdateListener, IIOReadWarningListener {
|
||||
@@ -1297,7 +1230,68 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
}
|
||||
|
||||
public static void main(final String[] args) throws IOException {
|
||||
for (final String arg : args) {
|
||||
ImageIO.setUseCache(false);
|
||||
|
||||
int subX = 1;
|
||||
int subY = 1;
|
||||
int xOff = 0;
|
||||
int yOff = 0;
|
||||
Rectangle roi = null;
|
||||
boolean metadata = false;
|
||||
boolean thumbnails = false;
|
||||
|
||||
for (int argIdx = 0; argIdx < args.length; argIdx++) {
|
||||
final String arg = args[argIdx];
|
||||
|
||||
if (arg.charAt(0) == '-') {
|
||||
if (arg.equals("-s") || arg.equals("--subsample") && args.length > argIdx) {
|
||||
String[] sub = args[++argIdx].split(",");
|
||||
|
||||
try {
|
||||
if (sub.length >= 4) {
|
||||
subX = Integer.parseInt(sub[0]);
|
||||
subY = Integer.parseInt(sub[1]);
|
||||
xOff = Integer.parseInt(sub[2]);
|
||||
yOff = Integer.parseInt(sub[3]);
|
||||
}
|
||||
else {
|
||||
subX = Integer.parseInt(sub[0]);
|
||||
subY = sub.length > 1 ? Integer.parseInt(sub[1]) : subX;
|
||||
}
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
System.err.println("Bad sub sampling (x,y): '" + args[argIdx] + "'");
|
||||
}
|
||||
}
|
||||
else if (arg.equals("-r") || arg.equals("--roi") && args.length > argIdx) {
|
||||
String[] region = args[++argIdx].split(",");
|
||||
|
||||
try {
|
||||
if (region.length >= 4) {
|
||||
roi = new Rectangle(Integer.parseInt(region[0]), Integer.parseInt(region[2]), Integer.parseInt(region[2]), Integer.parseInt(region[3]));
|
||||
}
|
||||
else {
|
||||
roi = new Rectangle(Integer.parseInt(region[0]), Integer.parseInt(region[2]));
|
||||
}
|
||||
}
|
||||
catch (IndexOutOfBoundsException | NumberFormatException e) {
|
||||
System.err.println("Bad source region ([x,y,]w, h): '" + args[argIdx] + "'");
|
||||
}
|
||||
}
|
||||
else if (arg.equals("-m") || arg.equals("--metadata")) {
|
||||
metadata = true;
|
||||
}
|
||||
else if (arg.equals("-t") || arg.equals("--thumbnails")) {
|
||||
thumbnails = true;
|
||||
}
|
||||
else {
|
||||
System.err.println("Unknown argument: '" + arg + "'");
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
File file = new File(arg);
|
||||
|
||||
ImageInputStream input = ImageIO.createImageInputStream(file);
|
||||
@@ -1313,15 +1307,15 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
continue;
|
||||
}
|
||||
|
||||
ImageReader reader = readers.next();
|
||||
// System.err.println("Reading using: " + reader);
|
||||
final ImageReader reader = readers.next();
|
||||
System.err.println("Reading using: " + reader);
|
||||
|
||||
reader.addIIOReadWarningListener(new IIOReadWarningListener() {
|
||||
public void warningOccurred(ImageReader source, String warning) {
|
||||
System.err.println("Warning: " + arg + ": " + warning);
|
||||
}
|
||||
});
|
||||
reader.addIIOReadProgressListener(new ProgressListenerBase() {
|
||||
final ProgressListenerBase listener = new ProgressListenerBase() {
|
||||
private static final int MAX_W = 78;
|
||||
int lastProgress = 0;
|
||||
|
||||
@@ -1350,29 +1344,35 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
|
||||
System.out.println("]");
|
||||
}
|
||||
});
|
||||
};
|
||||
reader.addIIOReadProgressListener(listener);
|
||||
|
||||
reader.setInput(input);
|
||||
|
||||
// For a tables-only image, we can't read image, but we should get metadata.
|
||||
if (reader.getNumImages(true) == 0) {
|
||||
IIOMetadata streamMetadata = reader.getStreamMetadata();
|
||||
IIOMetadataNode streamNativeTree = (IIOMetadataNode) streamMetadata.getAsTree(streamMetadata.getNativeMetadataFormatName());
|
||||
new XMLSerializer(System.out, System.getProperty("file.encoding")).serialize(streamNativeTree, false);
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
// For a tables-only image, we can't read image, but we should get metadata.
|
||||
if (reader.getNumImages(true) == 0) {
|
||||
IIOMetadata streamMetadata = reader.getStreamMetadata();
|
||||
IIOMetadataNode streamNativeTree = (IIOMetadataNode) streamMetadata.getAsTree(streamMetadata.getNativeMetadataFormatName());
|
||||
new XMLSerializer(System.out, System.getProperty("file.encoding")).serialize(streamNativeTree, false);
|
||||
continue;
|
||||
}
|
||||
|
||||
BufferedImage image;
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
// if (args.length > 1) {
|
||||
// int sub = Integer.parseInt(args[1]);
|
||||
// int sub = 4;
|
||||
// param.setSourceSubsampling(sub, sub, 0, 0);
|
||||
// }
|
||||
BufferedImage image = reader.getImageTypes(0).next().createBufferedImage(reader.getWidth(0), reader.getHeight(0));
|
||||
if (subX > 1 || subY > 1 || roi != null) {
|
||||
param.setSourceSubsampling(subX, subY, xOff, yOff);
|
||||
param.setSourceRegion(roi);
|
||||
|
||||
image = reader.getImageTypes(0).next().createBufferedImage((reader.getWidth(0) + subX - 1)/ subX, (reader.getHeight(0) + subY - 1) / subY);
|
||||
}
|
||||
else {
|
||||
image = reader.getImageTypes(0).next().createBufferedImage(reader.getWidth(0), reader.getHeight(0));
|
||||
}
|
||||
param.setDestination(image);
|
||||
|
||||
// long start = System.currentTimeMillis();
|
||||
long start = DEBUG ? System.currentTimeMillis() : 0;
|
||||
|
||||
try {
|
||||
image = reader.read(0, param);
|
||||
}
|
||||
@@ -1383,12 +1383,13 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// System.err.println("Read time: " + (System.currentTimeMillis() - start) + " ms");
|
||||
// System.err.println("image: " + image);
|
||||
|
||||
if (DEBUG) {
|
||||
System.err.println("Read time: " + (System.currentTimeMillis() - start) + " ms");
|
||||
System.err.println("image: " + image);
|
||||
}
|
||||
|
||||
// image = new ResampleOp(reader.getWidth(0) / 4, reader.getHeight(0) / 4, ResampleOp.FILTER_LANCZOS).filter(image, null);
|
||||
|
||||
/*
|
||||
int maxW = 1280;
|
||||
int maxH = 800;
|
||||
if (image.getWidth() > maxW || image.getHeight() > maxH) {
|
||||
@@ -1402,28 +1403,45 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
}
|
||||
// System.err.println("Scale time: " + (System.currentTimeMillis() - start) + " ms");
|
||||
}
|
||||
*/
|
||||
|
||||
showIt(image, String.format("Image: %s [%d x %d]", file.getName(), reader.getWidth(0), reader.getHeight(0)));
|
||||
|
||||
try {
|
||||
IIOMetadata imageMetadata = reader.getImageMetadata(0);
|
||||
System.out.println("Metadata for File: " + file.getName());
|
||||
System.out.println("Native:");
|
||||
new XMLSerializer(System.out, System.getProperty("file.encoding")).serialize(imageMetadata.getAsTree(imageMetadata.getNativeMetadataFormatName()), false);
|
||||
System.out.println("Standard:");
|
||||
new XMLSerializer(System.out, System.getProperty("file.encoding")).serialize(imageMetadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName), false);
|
||||
System.out.println();
|
||||
if (metadata) {
|
||||
try {
|
||||
IIOMetadata imageMetadata = reader.getImageMetadata(0);
|
||||
System.out.println("Metadata for File: " + file.getName());
|
||||
|
||||
int numThumbnails = reader.getNumThumbnails(0);
|
||||
for (int i = 0; i < numThumbnails; i++) {
|
||||
BufferedImage thumbnail = reader.readThumbnail(0, i);
|
||||
// System.err.println("thumbnail: " + thumbnail);
|
||||
showIt(thumbnail, String.format("Thumbnail: %s [%d x %d]", file.getName(), thumbnail.getWidth(), thumbnail.getHeight()));
|
||||
if (imageMetadata.getNativeMetadataFormatName() != null) {
|
||||
System.out.println("Native:");
|
||||
new XMLSerializer(System.out, System.getProperty("file.encoding")).serialize(imageMetadata.getAsTree(imageMetadata.getNativeMetadataFormatName()), false);
|
||||
}
|
||||
if (imageMetadata.isStandardMetadataFormatSupported()) {
|
||||
System.out.println("Standard:");
|
||||
new XMLSerializer(System.out, System.getProperty("file.encoding")).serialize(imageMetadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName), false);
|
||||
}
|
||||
|
||||
System.out.println();
|
||||
}
|
||||
catch (IIOException e) {
|
||||
System.err.println("Could not read thumbnails: " + arg + ": " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
catch (IIOException e) {
|
||||
System.err.println("Could not read thumbnails: " + arg + ": " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
|
||||
if (thumbnails) {
|
||||
try {
|
||||
int numThumbnails = reader.getNumThumbnails(0);
|
||||
for (int i = 0; i < numThumbnails; i++) {
|
||||
BufferedImage thumbnail = reader.readThumbnail(0, i);
|
||||
// System.err.println("thumbnail: " + thumbnail);
|
||||
showIt(thumbnail, String.format("Thumbnail: %s [%d x %d]", file.getName(), thumbnail.getWidth(), thumbnail.getHeight()));
|
||||
}
|
||||
}
|
||||
catch (IIOException e) {
|
||||
System.err.println("Could not read thumbnails: " + arg + ": " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Throwable t) {
|
||||
|
@@ -29,6 +29,7 @@
|
||||
package com.twelvemonkeys.imageio.plugins.jpeg;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
|
||||
@@ -48,14 +49,14 @@ import java.util.Locale;
|
||||
* @version $Id: JPEGImageReaderSpi.java,v 1.0 24.01.11 22.12 haraldk Exp$
|
||||
*/
|
||||
public class JPEGImageReaderSpi extends ImageReaderSpiBase {
|
||||
private ImageReaderSpi delegateProvider;
|
||||
protected ImageReaderSpi delegateProvider;
|
||||
|
||||
/**
|
||||
* Constructor for use by {@link javax.imageio.spi.IIORegistry} only.
|
||||
* The instance created will not work without being properly registered.
|
||||
*/
|
||||
public JPEGImageReaderSpi() {
|
||||
super(new JPEGProviderInfo());
|
||||
this(new JPEGProviderInfo());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,6 +70,15 @@ public class JPEGImageReaderSpi extends ImageReaderSpiBase {
|
||||
this.delegateProvider = Validate.notNull(delegateProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for subclasses.
|
||||
*
|
||||
* @param info
|
||||
*/
|
||||
protected JPEGImageReaderSpi(final ReaderWriterProviderInfo info) {
|
||||
super(info);
|
||||
}
|
||||
|
||||
static ImageReaderSpi lookupDelegateProvider(final ServiceRegistry registry) {
|
||||
Iterator<ImageReaderSpi> providers = registry.getServiceProviders(ImageReaderSpi.class, true);
|
||||
|
||||
@@ -83,7 +93,7 @@ public class JPEGImageReaderSpi extends ImageReaderSpiBase {
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
@SuppressWarnings({"unchecked", "deprecation"})
|
||||
@Override
|
||||
public void onRegistration(final ServiceRegistry registry, final Class<?> category) {
|
||||
if (delegateProvider == null) {
|
||||
|
@@ -1,3 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 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.jpeg;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
||||
|
@@ -240,16 +240,21 @@ final class JPEGSegmentImageInputStream extends ImageInputStreamImpl {
|
||||
private void streamInit() throws IOException {
|
||||
stream.seek(0);
|
||||
|
||||
int soi = stream.readUnsignedShort();
|
||||
if (soi != JPEG.SOI) {
|
||||
throw new IIOException(String.format("Not a JPEG stream (starts with: 0x%04x, expected SOI: 0x%04x)", soi, JPEG.SOI));
|
||||
}
|
||||
else {
|
||||
try {
|
||||
int soi = stream.readUnsignedShort();
|
||||
|
||||
if (soi != JPEG.SOI) {
|
||||
throw new IIOException(String.format("Not a JPEG stream (starts with: 0x%04x, expected SOI: 0x%04x)", soi, JPEG.SOI));
|
||||
}
|
||||
|
||||
segment = new Segment(soi, 0, 0, 2);
|
||||
|
||||
segments.add(segment);
|
||||
currentSegment = segments.size() - 1; // 0
|
||||
}
|
||||
catch (EOFException eof) {
|
||||
throw new IIOException(String.format("Not a JPEG stream (short stream. expected SOI: 0x%04x)", JPEG.SOI), eof);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isAppSegmentMarker(final int marker) {
|
||||
|
@@ -28,7 +28,7 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.jpeg;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase;
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||
import org.hamcrest.core.IsInstanceOf;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
@@ -59,10 +59,11 @@ import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assume.assumeNoException;
|
||||
import static org.junit.Assume.assumeNotNull;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* JPEGImageReaderTest
|
||||
@@ -71,11 +72,11 @@ import static org.mockito.Mockito.verify;
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: JPEGImageReaderTest.java,v 1.0 24.01.11 22.04 haraldk Exp$
|
||||
*/
|
||||
public class JPEGImageReaderTest extends ImageReaderAbstractTestCase<JPEGImageReader> {
|
||||
public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader> {
|
||||
|
||||
private static final JPEGImageReaderSpi SPI = new JPEGImageReaderSpi(lookupDelegateProvider());
|
||||
protected static final JPEGImageReaderSpi SPI = new JPEGImageReaderSpi(lookupDelegateProvider());
|
||||
|
||||
private static ImageReaderSpi lookupDelegateProvider() {
|
||||
protected static ImageReaderSpi lookupDelegateProvider() {
|
||||
return JPEGImageReaderSpi.lookupDelegateProvider(IIORegistry.getDefaultInstance());
|
||||
}
|
||||
|
||||
@@ -85,6 +86,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTestCase<JPEGImageRe
|
||||
return Arrays.asList(
|
||||
new TestData(getClassLoaderResource("/jpeg/cmm-exception-adobe-rgb.jpg"), new Dimension(626, 76)),
|
||||
new TestData(getClassLoaderResource("/jpeg/cmm-exception-srgb.jpg"), new Dimension(1800, 1200)),
|
||||
new TestData(getClassLoaderResource("/jpeg/corrupted-icc-srgb.jpg"), new Dimension(1024, 685)),
|
||||
new TestData(getClassLoaderResource("/jpeg/gray-sample.jpg"), new Dimension(386, 396)),
|
||||
new TestData(getClassLoaderResource("/jpeg/cmyk-sample.jpg"), new Dimension(160, 227)),
|
||||
new TestData(getClassLoaderResource("/jpeg/cmyk-sample-multiple-chunk-icc.jpg"), new Dimension(2707, 3804)),
|
||||
@@ -149,7 +151,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTestCase<JPEGImageRe
|
||||
|
||||
@Override
|
||||
protected List<String> getMIMETypes() {
|
||||
return Arrays.asList("image/jpeg");
|
||||
return Collections.singletonList("image/jpeg");
|
||||
}
|
||||
|
||||
// TODO: Test that subsampling is actually reading something
|
||||
@@ -368,7 +370,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTestCase<JPEGImageRe
|
||||
assertEquals(1772, image.getWidth());
|
||||
assertEquals(8, image.getHeight());
|
||||
|
||||
verify(warningListener).warningOccurred(eq(reader), anyString());
|
||||
verify(warningListener, atLeast(1)).warningOccurred(eq(reader), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1098,7 +1100,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTestCase<JPEGImageRe
|
||||
assertTrue(markerSequences.getLength() == 1 || markerSequences.getLength() == 2); // In case of JPEG encoded thumbnail, there will be 2
|
||||
IIOMetadataNode markerSequence = (IIOMetadataNode) markerSequences.item(0);
|
||||
assertNotNull(markerSequence);
|
||||
assertThat(markerSequence.getChildNodes().getLength(), new GreaterThan<Integer>(0));
|
||||
assertThat(markerSequence.getChildNodes().getLength(), new GreaterThan<>(0));
|
||||
|
||||
NodeList unknowns = markerSequence.getElementsByTagName("unknown");
|
||||
for (int j = 0; j < unknowns.getLength(); j++) {
|
||||
@@ -1157,10 +1159,6 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTestCase<JPEGImageRe
|
||||
JPEGImageReader reader = createReader();
|
||||
ImageReader referenceReader = createReferenceReader();
|
||||
|
||||
if (referenceReader == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (TestData testData : getTestData()) {
|
||||
reader.setInput(testData.getInputStream());
|
||||
referenceReader.setInput(testData.getInputStream());
|
||||
@@ -1201,13 +1199,15 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTestCase<JPEGImageRe
|
||||
Class<ImageReaderSpi> spiClass = (Class<ImageReaderSpi>) Class.forName("com.sun.imageio.plugins.jpeg.JPEGImageReaderSpi");
|
||||
ImageReaderSpi provider = spiClass.newInstance();
|
||||
|
||||
return provider.createReaderInstance();
|
||||
ImageReader reader = provider.createReaderInstance();
|
||||
assumeNotNull(reader);
|
||||
return reader;
|
||||
}
|
||||
catch (Throwable t) {
|
||||
System.err.println("WARNING: Could not create ImageReader for reference (missing dependency): " + t.getMessage());
|
||||
|
||||
return null;
|
||||
assumeNoException(t);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void assertTreesEquals(String message, Node expectedTree, Node actualTree) {
|
||||
@@ -1290,7 +1290,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTestCase<JPEGImageRe
|
||||
}
|
||||
|
||||
private List<IIOMetadataNode> sortNodes(final NodeList nodes) {
|
||||
ArrayList<IIOMetadataNode> sortedNodes = new ArrayList<IIOMetadataNode>(new AbstractList<IIOMetadataNode>() {
|
||||
ArrayList<IIOMetadataNode> sortedNodes = new ArrayList<>(new AbstractList<IIOMetadataNode>() {
|
||||
@Override
|
||||
public IIOMetadataNode get(int index) {
|
||||
return (IIOMetadataNode) nodes.item(index);
|
||||
@@ -1438,7 +1438,6 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTestCase<JPEGImageRe
|
||||
// TODO: Report bug!
|
||||
|
||||
ImageReader reader = createReader();
|
||||
// ImageReader reader = createReferenceReader();
|
||||
|
||||
try {
|
||||
reader.setInput(ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/progressive-adobe-sof-bands-dont-match-sos-band-count.jpg")));
|
||||
@@ -1494,4 +1493,40 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTestCase<JPEGImageRe
|
||||
reader.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRawImageTypeAdobeAPP14CMYKAnd3channelData() throws IOException {
|
||||
JPEGImageReader reader = createReader();
|
||||
|
||||
try {
|
||||
reader.setInput(ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/exif-jfif-app13-app14ycck-3channel.jpg")));
|
||||
|
||||
ImageTypeSpecifier rawType = reader.getRawImageType(0);
|
||||
assertNull(rawType); // But no exception, please...
|
||||
}
|
||||
finally {
|
||||
reader.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadAdobeAPP14CMYKAnd3channelData() throws IOException {
|
||||
JPEGImageReader reader = createReader();
|
||||
|
||||
try {
|
||||
reader.setInput(ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/exif-jfif-app13-app14ycck-3channel.jpg")));
|
||||
|
||||
assertEquals(310, reader.getWidth(0));
|
||||
assertEquals(384, reader.getHeight(0));
|
||||
|
||||
BufferedImage image = reader.read(0, null);
|
||||
assertNotNull(image);
|
||||
assertEquals(310, image.getWidth());
|
||||
assertEquals(384, image.getHeight());
|
||||
assertEquals(ColorSpace.TYPE_RGB, image.getColorModel().getColorSpace().getType());
|
||||
}
|
||||
finally {
|
||||
reader.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,19 @@
|
||||
package com.twelvemonkeys.imageio.plugins.jpeg;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfoTest;
|
||||
|
||||
/**
|
||||
* JPEGProviderInfoTest.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: harald.kuhr$
|
||||
* @version $Id: JPEGProviderInfoTest.java,v 1.0 02/06/16 harald.kuhr Exp$
|
||||
*/
|
||||
public class JPEGProviderInfoTest extends ReaderWriterProviderInfoTest {
|
||||
|
||||
@Override
|
||||
protected ReaderWriterProviderInfo createProviderInfo() {
|
||||
return new JPEGProviderInfo();
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user