mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2026-05-21 00:00:01 -04:00
Compare commits
199 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 873420286d | |||
| 4993331a5d | |||
| af0a3889db | |||
| 6acdfd3be6 | |||
| cfb664a76a | |||
| 86d95e1f02 | |||
| 54dd9b6d7b | |||
| 4e10fc019e | |||
| 1295951ead | |||
| d5e664cdcc | |||
| efe5f3c34a | |||
| 2488f6f67c | |||
| 6a66d2e059 | |||
| ebd5533879 | |||
| e6e4e96309 | |||
| aadc62dde9 | |||
| 24cbe57240 | |||
| f7d8ae0cd2 | |||
| 5da934e11b | |||
| 51297ad496 | |||
| 80a534cd62 | |||
| 24130d466d | |||
| 7559686782 | |||
| b6988c37a7 | |||
| bbffb1d416 | |||
| c68de3bc92 | |||
| a12b6044c6 | |||
| 9a0e2d9659 | |||
| b904f8952f | |||
| 187c952b8e | |||
| ca86605923 | |||
| 8bc863298f | |||
| 3447d1782c | |||
| bf245fde5f | |||
| c0748dcfd7 | |||
| c293516201 | |||
| f6dae36b7e | |||
| a14b481e9e | |||
| d9c1a39c37 | |||
| e9d9f99bb0 | |||
| b9749b94b0 | |||
| 4996dff6e4 | |||
| b1a2244c7f | |||
| c6fe747ca3 | |||
| e1af4d7da9 | |||
| 33556cc0ec | |||
| eaf13b102f | |||
| 74718f1ffb | |||
| bd3700ea59 | |||
| 8fccd9445f | |||
| cf0ed8f95c | |||
| c12e4e5646 | |||
| 287b73c732 | |||
| 24271b8cad | |||
| 3e2f54ee7c | |||
| 2511b2d0cd | |||
| a15d54c92c | |||
| 47b7c4b16c | |||
| 9e1b01a7fd | |||
| 769acc8726 | |||
| 1ace3a6d5f | |||
| a06eb53cd2 | |||
| 1e1a640a6c | |||
| 2444bc5ad4 | |||
| 2e656a45f9 | |||
| 657928f4a5 | |||
| 08f7e070dc | |||
| b205226f0c | |||
| 821df11d90 | |||
| a62a838a0d | |||
| 75ff0f265f | |||
| 15c7cfe9a6 | |||
| 1286077b02 | |||
| 5757743db7 | |||
| fbaa13d48d | |||
| f12df442e9 | |||
| 0c0712ab30 | |||
| 9267842788 | |||
| bcffeb04ec | |||
| d1a1bab18c | |||
| 6b5e75a22b | |||
| 7384118357 | |||
| 4d833a50e5 | |||
| 57b0fdac0b | |||
| e6bd94025f | |||
| 330a0414f0 | |||
| 5cc201b46d | |||
| 7e55d7765d | |||
| 8f942922fd | |||
| db5635e844 | |||
| 8bc952ba66 | |||
| 96cb3a07f4 | |||
| cd6a6258b6 | |||
| a0fa2c08ac | |||
| 6642b1647a | |||
| 5315caf830 | |||
| 872523b0f0 | |||
| 7bf99fb496 | |||
| a1047edddb | |||
| e956176872 | |||
| 6d2947b080 | |||
| fb304d6c27 | |||
| 903289caa4 | |||
| aff31ebd1b | |||
| b6773f6983 | |||
| 0d28eb31d2 | |||
| 4922ccf80b | |||
| f7ce772f84 | |||
| 3d4bc0e69d | |||
| c721291a78 | |||
| 5f9ea2e7c2 | |||
| c48af5acc7 | |||
| 8b86b57e63 | |||
| 420f78be88 | |||
| d2b58ed20e | |||
| 278ce6ef33 | |||
| 768bc30653 | |||
| d9d8419803 | |||
| 7a9523690c | |||
| e5c6832ec0 | |||
| 167686bdea | |||
| d3249dc3d5 | |||
| 5c1f51f3ca | |||
| fde3a5c2ab | |||
| ae213dcf5e | |||
| 51ace4ca7f | |||
| f15bcc7df9 | |||
| 6be86affd6 | |||
| eec8268eb9 | |||
| 859b232f64 | |||
| 5501c0e709 | |||
| 306d8ac5f6 | |||
| d41f6a4465 | |||
| 8ed5f06151 | |||
| 42e17f2063 | |||
| f449a50d64 | |||
| 0c2433dc9f | |||
| a5def1ba3d | |||
| 810b7197ab | |||
| 24a0786d64 | |||
| c1ce4b066d | |||
| b6aec25f00 | |||
| c1834dc78d | |||
| 9e23413456 | |||
| 7d2c692663 | |||
| 9ce1a86cae | |||
| b0b5de5fa4 | |||
| 64178bd636 | |||
| 7985cdd4a7 | |||
| 8247861095 | |||
| 8480c929c1 | |||
| e6f437f55b | |||
| ee049d9465 | |||
| f6aa810f8b | |||
| e8d1b999a2 | |||
| d7fbd6594e | |||
| caef9b6a9e | |||
| 7fad4d5cd8 | |||
| 3e4b14f984 | |||
| 5ade293445 | |||
| a0fc5f560d | |||
| 51b0734462 | |||
| 8e3cd5aedb | |||
| cbaa0dc688 | |||
| 61bac97933 | |||
| 3e9be32279 | |||
| d41acaee05 | |||
| a0955875d8 | |||
| 38ea0989d4 | |||
| 7e0de14783 | |||
| de34d4642e | |||
| b86bad2bf9 | |||
| cc5af0134c | |||
| edcac25f5e | |||
| 5a5c1f996b | |||
| f9b8cae0ea | |||
| 62538c3db9 | |||
| 48aa504be4 | |||
| ed2eb7642a | |||
| 24ec5ae34d | |||
| 0cd250646a | |||
| 586359e7ab | |||
| 5a3945c411 | |||
| a7b374e51a | |||
| 1a1067a6bd | |||
| d8d0131806 | |||
| 60180938d2 | |||
| c74630656f | |||
| 96901e020f | |||
| 5e3229c9b7 | |||
| fc8c891f07 | |||
| 632e5e6664 | |||
| 7af0cfaa78 | |||
| f4b154f6c8 | |||
| b09a8a5dbe | |||
| a651939274 | |||
| 08283299ba | |||
| 1228043d57 | |||
| c924f284a0 |
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
BSD 3-Clause License
|
BSD 3-Clause License
|
||||||
|
|
||||||
Copyright (c) 2017, Harald Kuhr
|
Copyright (c) 2008-2020, Harald Kuhr
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
## Latest
|
[](https://travis-ci.org/haraldk/TwelveMonkeys)
|
||||||
|
[](https://maven-badges.herokuapp.com/maven-central/com.twelvemonkeys.imageio/imageio)
|
||||||
Master branch build status: [](https://travis-ci.org/haraldk/TwelveMonkeys)
|
[](https://stackoverflow.com/questions/tagged/twelvemonkeys)
|
||||||
|
[](https://paypal.me/haraldk76/100)
|
||||||
Latest release is TwelveMonkeys ImageIO [3.4.1](https://search.maven.org/search?q=g:com.twelvemonkeys.imageio%20AND%20v:3.4.1) (Sep. 7th, 2018).
|
|
||||||
[Release notes](https://github.com/haraldk/TwelveMonkeys/releases/latest).
|
|
||||||
|
|
||||||
## About
|
## About
|
||||||
|
|
||||||
TwelveMonkeys ImageIO is a collection of plugins and extensions for Java's ImageIO.
|
TwelveMonkeys ImageIO is a collection of plugins and extensions for Java's ImageIO.
|
||||||
|
|
||||||
These plugins extends the number of image file formats supported in Java, using the javax.imageio.* package.
|
These plugins extend the number of image file formats supported in Java, using the `javax.imageio.*` package.
|
||||||
The main purpose of this project is to provide support for formats not covered by the JRE itself.
|
The main purpose of this project is to provide support for formats not covered by the JRE itself.
|
||||||
|
|
||||||
Support for formats is important, both to be able to read data found
|
Support for formats is important, both to be able to read data found
|
||||||
@@ -19,246 +17,63 @@ The goal is to create a set of efficient and robust ImageIO plug-ins, that can b
|
|||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
## Features
|
## File formats supported
|
||||||
|
|
||||||
Mainstream format support
|
| Plugin | Format | Description | Read | Write | Metadata | Notes |
|
||||||
|
| ------ | -------- | ----------- |:----:|:-----:| -------- | ----- |
|
||||||
|
| Batik | **SVG** | Scalable Vector Graphics | âś” | - | - | Requires [Batik](https://xmlgraphics.apache.org/batik/)
|
||||||
|
| | WMF | MS Windows Metafile | âś” | - | - | Requires [Batik](https://xmlgraphics.apache.org/batik/)
|
||||||
|
| [BMP](https://github.com/haraldk/TwelveMonkeys/wiki/BMP-Plugin) | **BMP** | MS Windows and IBM OS/2 Device Independent Bitmap | âś” | âś” | Native & Standard |
|
||||||
|
| | CUR | MS Windows Cursor Format | âś” | - | - |
|
||||||
|
| | ICO | MS Windows Icon Format | âś” | âś” | - |
|
||||||
|
| [HDR](https://github.com/haraldk/TwelveMonkeys/wiki/HDR-Plugin) | HDR | Radiance High Dynamic Range RGBE Format | âś” | - | Standard |
|
||||||
|
| [ICNS](https://github.com/haraldk/TwelveMonkeys/wiki/ICNS-Plugin) | ICNS | Apple Icon Image | âś” | âś” | - |
|
||||||
|
| [IFF](https://github.com/haraldk/TwelveMonkeys/wiki/IFF-Plugin) | IFF | Commodore Amiga/Electronic Arts Interchange File Format | âś” | âś” | Standard |
|
||||||
|
| [JPEG](https://github.com/haraldk/TwelveMonkeys/wiki/JPEG-Plugin) | **JPEG** | Joint Photographers Expert Group | âś” | âś” | Native & Standard |
|
||||||
|
| | **JPEG Lossless** | | âś” | - | Native & Standard |
|
||||||
|
| [PCX](https://github.com/haraldk/TwelveMonkeys/wiki/PCX-Plugin) | PCX | ZSoft Paintbrush Format | âś” | - | Standard |
|
||||||
|
| | DCX | Multi-page PCX fax document | âś” | - | Standard |
|
||||||
|
| [PICT](https://github.com/haraldk/TwelveMonkeys/wiki/PICT-Plugin) | PICT | Apple Mac Paint Picture Format | âś” | - | - |
|
||||||
|
| [PNM](https://github.com/haraldk/TwelveMonkeys/wiki/PNM-Plugin) | PAM | NetPBM Portable Any Map | âś” | âś” | Standard |
|
||||||
|
| | PBM | NetPBM Portable Bit Map | âś” | - | Standard |
|
||||||
|
| | PGM | NetPBM Portable Grey Map | âś” | - | Standard |
|
||||||
|
| | PPM | NetPBM Portable Pix Map | âś” | âś” | Standard |
|
||||||
|
| | PFM | Portable Float Map | âś” | - | Standard |
|
||||||
|
| [PSD](https://github.com/haraldk/TwelveMonkeys/wiki/PSD-Plugin) | **PSD** | Adobe Photoshop Document | âś” | - | Native & Standard |
|
||||||
|
| | PSB | Adobe Photoshop Large Document | âś” | - | Native & Standard |
|
||||||
|
| [SGI](https://github.com/haraldk/TwelveMonkeys/wiki/SGI-Plugin) | SGI | Silicon Graphics Image Format | âś” | - | Standard |
|
||||||
|
| [TGA](https://github.com/haraldk/TwelveMonkeys/wiki/TGA-Plugin) | TGA | Truevision TGA Image Format | âś” | âś” | Standard |
|
||||||
|
|ThumbsDB| Thumbs.db| MS Windows Thumbs DB | âś” | - | - | OLE2 Compound Document based format only
|
||||||
|
| [TIFF](https://github.com/haraldk/TwelveMonkeys/wiki/TIFF-Plugin) | **TIFF** | Aldus/Adobe Tagged Image File Format | âś” | âś” | Native & Standard |
|
||||||
|
| | BigTIFF | | âś” | - | Native & Standard |
|
||||||
|
| [WebP](https://github.com/haraldk/TwelveMonkeys/wiki/WebP-Plugin) | **WebP** | Google WebP Format | âś” | - | Standard | In progress
|
||||||
|
| XWD | XWD | X11 Window Dump Format | âś” | - | Standard |
|
||||||
|
|
||||||
#### BMP - MS Windows/IBM OS/2 Device Independent Bitmap
|
|
||||||
|
|
||||||
* Read support for all known versions of the DIB/BMP format
|
**Important note on using Batik:** *Please read [The Apache™ XML Graphics Project - Security](http://xmlgraphics.apache.org/security.html),
|
||||||
* Indexed color, 1, 4 and 8 bit, including 4 and 8 bit RLE
|
and make sure you use version 1.14 or later.*
|
||||||
* RGB, 16, 24 and 32 bit
|
|
||||||
* Embedded PNG and JPEG data
|
|
||||||
* Windows and OS/2 versions
|
|
||||||
* Native and standard metadata format
|
|
||||||
|
|
||||||
#### JPEG
|
|
||||||
|
|
||||||
* Read support for the following JPEG "flavors":
|
|
||||||
* All JFIF compliant JPEGs
|
|
||||||
* All Exif compliant JPEGs
|
|
||||||
* YCbCr JPEGs without JFIF segment (converted to RGB, using embedded ICC profile)
|
|
||||||
* CMYK JPEGs (converted to RGB by default or as CMYK, using embedded ICC profile)
|
|
||||||
* Adobe YCCK JPEGs (converted to RGB by default or as CMYK, using embedded ICC profile)
|
|
||||||
* JPEGs containing ICC profiles with interpretation other than 'Perceptual' or class other than 'Display'
|
|
||||||
* JPEGs containing ICC profiles that are incompatible with stream data, corrupted ICC profiles or corrupted `ICC_PROFILE` segments
|
|
||||||
* JPEGs using non-standard color spaces, unsupported by Java 2D
|
|
||||||
* JPEGs with APP14/Adobe segments with length other than 14 bytes
|
|
||||||
* 8 bit JPEGs with 16 bit DQT segments
|
|
||||||
* Issues warnings instead of throwing exceptions in cases of corrupted or non-conformant data where ever the image
|
|
||||||
data can still be read in a reasonable way
|
|
||||||
* Thumbnail support:
|
|
||||||
* JFIF thumbnails (even if stream contains "inconsistent metadata")
|
|
||||||
* JFXX thumbnails (JPEG, Indexed and RGB)
|
|
||||||
* EXIF thumbnails (JPEG, RGB and YCbCr)
|
|
||||||
* Metadata support:
|
|
||||||
* JPEG metadata in both standard and native formats (even if stream contains "inconsistent metadata")
|
|
||||||
* `javax_imageio_jpeg_image_1.0` format (currently as native format, may change in the future)
|
|
||||||
* Non-conforming combinations of JFIF, Exif and Adobe markers, using "unknown" segments in the
|
|
||||||
"MarkerSequence" tag for the unsupported segments (for `javax_imageio_jpeg_image_1.0` format)
|
|
||||||
* Extended write support:
|
|
||||||
* CMYK JPEGs
|
|
||||||
* YCCK JPEGs in progress
|
|
||||||
|
|
||||||
#### JPEG-2000
|
|
||||||
|
|
||||||
* Possibly coming in the future, pending some license issues.
|
|
||||||
|
|
||||||
If you are one of the authors, or know one of the authors and/or the current license holders of either the original
|
|
||||||
jj2000 package or the JAI ImageIO project, please contact me (I've tried to get in touch in various ways,
|
|
||||||
without success so far).
|
|
||||||
|
|
||||||
Alternatively, if you have or know of a JPEG-2000 implementation in Java with a suitable license, get in touch. :-)
|
|
||||||
|
|
||||||
#### PNM - NetPBM Portable Any Map
|
|
||||||
|
|
||||||
* Read support for the following file types:
|
|
||||||
* PBM in 'P1' (ASCII) and 'P4' (binary) formats, 1 bit per pixel
|
|
||||||
* PGM in 'P2' (ASCII) and 'P5' (binary) formats, up to 16/32 bits per pixel
|
|
||||||
* PPM in 'P3' (ASCII) and 'P6' (binary) formats, up to 16/32 bits per pixel component
|
|
||||||
* PAM in 'P7' (binary) format up to 32 bits per pixel component
|
|
||||||
* Limited support for PFM in 'Pf' (gray) and 'PF' (RGB) formats, 32 bits floating point
|
|
||||||
* Write support for the following formats:
|
|
||||||
* PPM in 'P6' (binary) format
|
|
||||||
* PAM in 'P7' (binary) format
|
|
||||||
* Standard metadata support
|
|
||||||
|
|
||||||
#### PSD - Adobe Photoshop Document
|
|
||||||
|
|
||||||
* Read support for the following file types:
|
|
||||||
* Monochrome, 1 channel, 1 bit
|
|
||||||
* Indexed, 1 channel, 8 bit
|
|
||||||
* Gray, 1 channel, 8, 16 and 32 bit
|
|
||||||
* Duotone, 1 channel, 8, 16 and 32 bit
|
|
||||||
* RGB, 3-4 channels, 8, 16 and 32 bit
|
|
||||||
* CMYK, 4-5 channels, 8, 16 and 32 bit
|
|
||||||
* Read support for the following compression types:
|
|
||||||
* Uncompressed
|
|
||||||
* RLE (PackBits)
|
|
||||||
* Layer support
|
|
||||||
* Image layers only, in all of the above types
|
|
||||||
* Thumbnail support
|
|
||||||
* JPEG
|
|
||||||
* RAW (RGB)
|
|
||||||
* Support for "Large Document Format" (PSB)
|
|
||||||
* Native and Standard metadata support
|
|
||||||
|
|
||||||
#### TIFF - Aldus/Adobe Tagged Image File Format
|
|
||||||
|
|
||||||
* Read support for the following "Baseline" TIFF file types:
|
|
||||||
* Class B (Bi-level), all relevant compression types, 1 bit per sample
|
|
||||||
* Class G (Gray), all relevant compression types, 2, 4, 8, 16 or 32 bits per sample, unsigned integer
|
|
||||||
* Class P (Palette/indexed color), all relevant compression types, 1, 2, 4, 8 or 16 bits per sample, unsigned integer
|
|
||||||
* Class R (RGB), all relevant compression types, 8 or 16 bits per sample, unsigned integer
|
|
||||||
* Read support for the following TIFF extensions:
|
|
||||||
* Tiling
|
|
||||||
* Class F (Facsimile), CCITT Modified Huffman RLE, T4 and T6 (type 2, 3 and 4) compressions.
|
|
||||||
* LZW Compression (type 5)
|
|
||||||
* "Old-style" JPEG Compression (type 6), as a best effort, as the spec is not well-defined
|
|
||||||
* JPEG Compression (type 7)
|
|
||||||
* ZLib (aka Adobe-style Deflate) Compression (type 8)
|
|
||||||
* Deflate Compression (type 32946)
|
|
||||||
* Horizontal differencing Predictor (type 2) for LZW, ZLib, Deflate and PackBits compression
|
|
||||||
* Alpha channel (ExtraSamples type 1/Associated Alpha and type 2/Unassociated Alpha)
|
|
||||||
* CMYK data (PhotometricInterpretation type 5/Separated)
|
|
||||||
* YCbCr data (PhotometricInterpretation type 6/YCbCr) for JPEG
|
|
||||||
* CIELab data in TIFF, ITU and ICC variants (PhotometricInterpretation type 9, 10 and 11)
|
|
||||||
* Planar data (PlanarConfiguration type 2/Planar)
|
|
||||||
* ICC profiles (ICCProfile)
|
|
||||||
* BitsPerSample values up to 16 for most PhotometricInterpretations
|
|
||||||
* Multiple images (pages) in one file
|
|
||||||
* Write support for most "Baseline" TIFF options
|
|
||||||
* Uncompressed, PackBits, ZLib and Deflate
|
|
||||||
* Additional support for CCITT T4 and and T6 compressions.
|
|
||||||
* Additional support for LZW and JPEG (type 7) compressions
|
|
||||||
* Horizontal differencing Predictor (type 2) for LZW, ZLib, Deflate
|
|
||||||
* Native and Standard metadata support
|
|
||||||
|
|
||||||
Legacy formats
|
|
||||||
|
|
||||||
#### HDR - Radiance High Dynamic Range RGBE Format
|
|
||||||
|
|
||||||
* Read support for the most common RGBE (.hdr) format
|
|
||||||
* Samples are converted to 32 bit floating point (`float`) and normalized using a global tone mapper by default.
|
|
||||||
* Support for custom global tone mappers
|
|
||||||
* Alternatively, use a "null-tone mapper", for unnormalized data (allows local tone mapping)
|
|
||||||
* Unconverted RGBE samples accessible using `readRaster`
|
|
||||||
* Standard metadata support
|
|
||||||
|
|
||||||
#### IFF - Commodore Amiga/Electronic Arts Interchange File Format
|
|
||||||
|
|
||||||
* Legacy format, allows reading popular image format from the Commodore Amiga computer.
|
|
||||||
* Read support for the following file types:
|
|
||||||
* ILBM Indexed color, 1-8 interleaved bit planes, including 6 bit EHB
|
|
||||||
* ILBM Gray, 8 bit interleaved bit planes
|
|
||||||
* ILBM RGB, 24 and 32 bit interleaved bit planes
|
|
||||||
* ILBM HAM6 and HAM8
|
|
||||||
* PBM Indexed color, 1-8 bit,
|
|
||||||
* PBM Gray, 8 bit
|
|
||||||
* PBM RGB, 24 and 32 bit
|
|
||||||
* PBM HAM6 and HAM8
|
|
||||||
* Write support
|
|
||||||
* ILBM Indexed color, 1-8 bits per sample, 8 bit gray, 24 and 32 bit true color.
|
|
||||||
* Support for the following compression types (read/write):
|
|
||||||
* Uncompressed
|
|
||||||
* RLE (PackBits)
|
|
||||||
|
|
||||||
#### PCX - ZSoft Paintbrush Format
|
|
||||||
|
|
||||||
* Read support for the following file types:
|
|
||||||
* Indexed color, 1, 2, 4 or 8 bits per pixel, bit planes or interleaved
|
|
||||||
* Grayscale, 8 bits per pixel
|
|
||||||
* Color (RGB), 8 bits per pixel component
|
|
||||||
* Read support for DCX (multi-page) fax format, containing any of the above types
|
|
||||||
* Support for the following compression types:
|
|
||||||
* Uncompressed (experimental)
|
|
||||||
* RLE compressed
|
|
||||||
* Standard metadata support
|
|
||||||
|
|
||||||
#### PICT - Apple Mac Paint Picture Format
|
|
||||||
|
|
||||||
* Legacy format, especially useful for reading OS X clipboard data.
|
|
||||||
* Read support for the following file types:
|
|
||||||
* QuickDraw (format support is not complete, but supports most OS X clipboard data as well as RGB pixel data)
|
|
||||||
* QuickDraw bitmap
|
|
||||||
* QuickDraw pixmap
|
|
||||||
* QuickTime stills
|
|
||||||
* Write support for RGB pixel data:
|
|
||||||
* QuickDraw pixmap
|
|
||||||
|
|
||||||
#### SGI - Silicon Graphics Image Format
|
|
||||||
|
|
||||||
* Read support for the following file types:
|
|
||||||
* 1, 2, 3 or 4 channel image data
|
|
||||||
* 8 or 16 bits per pixel component
|
|
||||||
* Support for the following compression types:
|
|
||||||
* Uncompressed
|
|
||||||
* RLE compressed
|
|
||||||
* Standard metadata support
|
|
||||||
|
|
||||||
#### TGA - Truevision TGA Image Format
|
|
||||||
|
|
||||||
* Read support for the following file types:
|
|
||||||
* ColorMapped
|
|
||||||
* Monochrome
|
|
||||||
* TrueColor
|
|
||||||
* Support for the following compression types:
|
|
||||||
* Uncompressed
|
|
||||||
* RLE compressed
|
|
||||||
* Standard metadata support
|
|
||||||
* Write support
|
|
||||||
|
|
||||||
Icon/other formats
|
|
||||||
|
|
||||||
#### ICNS - Apple Icon Image
|
|
||||||
|
|
||||||
* Read support for the following icon types:
|
|
||||||
* All known "native" icon types
|
|
||||||
* Large PNG encoded icons
|
|
||||||
* Large JPEG 2000 encoded icons (requires JPEG 2000 ImageIO plugin or fallback to `sips` command line tool)
|
|
||||||
* Write support for PNG encoded icons
|
|
||||||
|
|
||||||
#### ICO & CUR - MS Windows Icon and Cursor Formats
|
|
||||||
|
|
||||||
* Read support for the following file types:
|
|
||||||
* ICO Indexed color, 1, 4 and 8 bit
|
|
||||||
* ICO RGB, 16, 24 and 32 bit
|
|
||||||
* CUR Indexed color, 1, 4 and 8 bit
|
|
||||||
* CUR RGB, 16, 24 and 32 bit
|
|
||||||
* Write support
|
|
||||||
* *3.1* Note: These formats are now part of the BMP plugin
|
|
||||||
|
|
||||||
#### Thumbs.db - MS Windows Thumbs DB
|
|
||||||
|
|
||||||
* Read support
|
|
||||||
|
|
||||||
Other formats, using 3rd party libraries
|
|
||||||
|
|
||||||
#### SVG - Scalable Vector Graphics
|
|
||||||
|
|
||||||
* Read-only support using Batik
|
|
||||||
|
|
||||||
#### WMF - MS Windows MetaFile
|
|
||||||
|
|
||||||
* Limited read-only support using Batik
|
|
||||||
|
|
||||||
**Important note on using Batik:** *Please read [The Apache™ XML Graphics Project - Security](http://xmlgraphics.apache.org/security.html), and make sure you use
|
|
||||||
either version 1.6.1, 1.7.1 or 1.8+.*
|
|
||||||
|
|
||||||
|
Note that GIF, PNG and WBMP formats are already supported through the ImageIO API, using the
|
||||||
|
[JDK standard plugins](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/package-summary.html).
|
||||||
|
For BMP, JPEG, and TIFF formats the TwelveMonkeys plugins provides extended format support and additional features.
|
||||||
|
|
||||||
## Basic usage
|
## Basic usage
|
||||||
|
|
||||||
Most of the time, all you need to do is simply include the plugins in your project and write:
|
Most of the time, all you need to do is simply include the plugins in your project and write:
|
||||||
|
|
||||||
BufferedImage image = ImageIO.read(file);
|
```java
|
||||||
|
BufferedImage image = ImageIO.read(file);
|
||||||
|
```
|
||||||
|
|
||||||
This will load the first image of the file, entirely into memory.
|
This will load the first image of the file, entirely into memory.
|
||||||
|
|
||||||
The basic and simplest form of writing is:
|
The basic and simplest form of writing is:
|
||||||
|
|
||||||
if (!ImageIO.write(image, format, file)) {
|
```java
|
||||||
// Handle image not written case
|
if (!ImageIO.write(image, format, file)) {
|
||||||
}
|
// Handle image not written case
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
This will write the entire image into a single file, using the default settings for the given format.
|
This will write the entire image into a single file, using the default settings for the given format.
|
||||||
|
|
||||||
@@ -269,50 +84,44 @@ The plugins are discovered automatically at run time. See the [FAQ](#faq) for mo
|
|||||||
If you need more control of read parameters and the reading process, the common idiom for reading is something like:
|
If you need more control of read parameters and the reading process, the common idiom for reading is something like:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
// Create input stream
|
// Create input stream (in try-with-resource block to avoid leaks)
|
||||||
ImageInputStream input = ImageIO.createImageInputStream(file);
|
try (ImageInputStream input = ImageIO.createImageInputStream(file)) {
|
||||||
|
// Get the reader
|
||||||
|
Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
|
||||||
|
|
||||||
|
if (!readers.hasNext()) {
|
||||||
|
throw new IllegalArgumentException("No reader for: " + file);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageReader reader = readers.next();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Get the reader
|
reader.setInput(input);
|
||||||
Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
|
|
||||||
|
|
||||||
if (!readers.hasNext()) {
|
// Optionally, listen for read warnings, progress, etc.
|
||||||
throw new IllegalArgumentException("No reader for: " + file);
|
reader.addIIOReadWarningListener(...);
|
||||||
}
|
reader.addIIOReadProgressListener(...);
|
||||||
|
|
||||||
ImageReader reader = readers.next();
|
ImageReadParam param = reader.getDefaultReadParam();
|
||||||
|
|
||||||
try {
|
// Optionally, control read settings like sub sampling, source region or destination etc.
|
||||||
reader.setInput(input);
|
param.setSourceSubsampling(...);
|
||||||
|
param.setSourceRegion(...);
|
||||||
|
param.setDestination(...);
|
||||||
|
// ...
|
||||||
|
|
||||||
// Optionally, listen for read warnings, progress, etc.
|
// Finally read the image, using settings from param
|
||||||
reader.addIIOReadWarningListener(...);
|
BufferedImage image = reader.read(0, param);
|
||||||
reader.addIIOReadProgressListener(...);
|
|
||||||
|
|
||||||
ImageReadParam param = reader.getDefaultReadParam();
|
// Optionally, read thumbnails, meta data, etc...
|
||||||
|
int numThumbs = reader.getNumThumbnails(0);
|
||||||
// Optionally, control read settings like sub sampling, source region or destination etc.
|
// ...
|
||||||
param.setSourceSubsampling(...);
|
|
||||||
param.setSourceRegion(...);
|
|
||||||
param.setDestination(...);
|
|
||||||
// ...
|
|
||||||
|
|
||||||
// Finally read the image, using settings from param
|
|
||||||
BufferedImage image = reader.read(0, param);
|
|
||||||
|
|
||||||
// Optionally, read thumbnails, meta data, etc...
|
|
||||||
int numThumbs = reader.getNumThumbnails(0);
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
// Dispose reader in finally block to avoid memory leaks
|
|
||||||
reader.dispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
// Close stream in finally block to avoid resource leaks
|
// Dispose reader in finally block to avoid memory leaks
|
||||||
input.close();
|
reader.dispose();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Query the reader for source image dimensions using `reader.getWidth(n)` and `reader.getHeight(n)` without reading the
|
Query the reader for source image dimensions using `reader.getWidth(n)` and `reader.getHeight(n)` without reading the
|
||||||
@@ -324,86 +133,56 @@ It's also possible to read multiple images from the same file in a loop, using `
|
|||||||
If you need more control of write parameters and the writing process, the common idiom for writing is something like:
|
If you need more control of write parameters and the writing process, the common idiom for writing is something like:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
// Get the writer
|
// Get the writer
|
||||||
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(format);
|
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(format);
|
||||||
|
|
||||||
if (!writers.hasNext()) {
|
if (!writers.hasNext()) {
|
||||||
throw new IllegalArgumentException("No writer for: " + format);
|
throw new IllegalArgumentException("No writer for: " + format);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageWriter writer = writers.next();
|
ImageWriter writer = writers.next();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Create output stream
|
// Create output stream (in try-with-resource block to avoid leaks)
|
||||||
ImageOutputStream output = ImageIO.createImageOutputStream(file);
|
try (ImageOutputStream output = ImageIO.createImageOutputStream(file)) {
|
||||||
|
writer.setOutput(output);
|
||||||
try {
|
|
||||||
writer.setOutput(output);
|
// Optionally, listen to progress, warnings, etc.
|
||||||
|
|
||||||
// Optionally, listen to progress, warnings, etc.
|
ImageWriteParam param = writer.getDefaultWriteParam();
|
||||||
|
|
||||||
ImageWriteParam param = writer.getDefaultWriteParam();
|
// Optionally, control format specific settings of param (requires casting), or
|
||||||
|
// control generic write settings like sub sampling, source region, output type etc.
|
||||||
// Optionally, control format specific settings of param (requires casting), or
|
|
||||||
// control generic write settings like sub sampling, source region, output type etc.
|
// Optionally, provide thumbnails and image/stream metadata
|
||||||
|
writer.write(..., new IIOImage(..., image, ...), param);
|
||||||
// Optionally, provide thumbnails and image/stream metadata
|
|
||||||
writer.write(..., new IIOImage(..., image, ...), param);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
// Close stream in finally block to avoid resource leaks
|
|
||||||
output.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
// Dispose writer in finally block to avoid memory leaks
|
|
||||||
writer.dispose();
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
// Dispose writer in finally block to avoid memory leaks
|
||||||
|
writer.dispose();
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
For more advanced usage, and information on how to use the ImageIO API, I suggest you read the
|
For more advanced usage, and information on how to use the ImageIO API, I suggest you read the
|
||||||
[Java Image I/O API Guide](http://docs.oracle.com/javase/7/docs/technotes/guides/imageio/spec/imageio_guideTOC.fm.html)
|
[Java Image I/O API Guide](http://docs.oracle.com/javase/7/docs/technotes/guides/imageio/spec/imageio_guideTOC.fm.html)
|
||||||
from Oracle.
|
from Oracle.
|
||||||
|
|
||||||
|
#### Adobe Clipping Path support
|
||||||
|
|
||||||
#### Deploying the plugins in a web app
|
```java
|
||||||
|
import com.twelvemonkeys.imageio.path.Paths;
|
||||||
|
|
||||||
Because the `ImageIO` plugin registry (the `IIORegistry`) is "VM global", it doesn't by default work well with
|
...
|
||||||
servlet contexts. This is especially evident if you load plugins from the `WEB-INF/lib` or `classes` folder.
|
|
||||||
Unless you add `ImageIO.scanForPlugins()` somewhere in your code, the plugins might never be available at all.
|
|
||||||
|
|
||||||
In addition, servlet contexts dynamically loads and unloads classes (using a new class loader per context).
|
try (ImageInputStream stream = ImageIO.createImageInputStream(new File("image_with_path.jpg")) {
|
||||||
If you restart your application, old classes will by default remain in memory forever (because the next time
|
BufferedImage image = Paths.readClipped(stream);
|
||||||
`scanForPlugins` is called, it's another `ClassLoader` that scans/loads classes, and thus they will be new instances
|
|
||||||
in the registry). If a read is attempted using one of the remaining "old" readers, weird exceptions
|
|
||||||
(like `NullPointerException`s when accessing `static final` initialized fields or `NoClassDefFoundError`s
|
|
||||||
for uninitialized inner classes) may occur.
|
|
||||||
|
|
||||||
To work around both the discovery problem and the resource leak,
|
// Do something with the clipped image...
|
||||||
it is *strongly recommended* to use the `IIOProviderContextListener` that implements
|
}
|
||||||
dynamic loading and unloading of ImageIO plugins for web applications.
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<web-app ...>
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
<listener>
|
|
||||||
<display-name>ImageIO service provider loader/unloader</display-name>
|
|
||||||
<listener-class>com.twelvemonkeys.servlet.image.IIOProviderContextListener</listener-class>
|
|
||||||
</listener>
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
</web-app>
|
|
||||||
```
|
```
|
||||||
|
See [Adobe Clipping Path support on the Wiki](https://github.com/haraldk/TwelveMonkeys/wiki/Photoshop-Clipping-Path-support) for more details and example code.
|
||||||
|
|
||||||
Loading plugins from `WEB-INF/lib` without the context listener installed is unsupported and will not work correctly.
|
|
||||||
|
|
||||||
The context listener has no dependencies to the TwelveMonkeys ImageIO plugins, and may be used with JAI ImageIO
|
|
||||||
or other ImageIO plugins as well.
|
|
||||||
|
|
||||||
Another safe option, is to place the JAR files in the application server's shared or common lib folder.
|
|
||||||
|
|
||||||
#### Using the ResampleOp
|
#### Using the ResampleOp
|
||||||
|
|
||||||
@@ -411,15 +190,15 @@ The library comes with a resampling (image resizing) operation, that contains ma
|
|||||||
to provide excellent results at reasonable speed.
|
to provide excellent results at reasonable speed.
|
||||||
|
|
||||||
```java
|
```java
|
||||||
import com.twelvemonkeys.image.ResampleOp;
|
import com.twelvemonkeys.image.ResampleOp;
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|
||||||
BufferedImage input = ...; // Image to resample
|
BufferedImage input = ...; // Image to resample
|
||||||
int width, height = ...; // new width/height
|
int width, height = ...; // new width/height
|
||||||
|
|
||||||
BufferedImageOp resampler = new ResampleOp(width, height, ResampleOp.FILTER_LANCZOS); // A good default filter, see class documentation for more info
|
BufferedImageOp resampler = new ResampleOp(width, height, ResampleOp.FILTER_LANCZOS); // A good default filter, see class documentation for more info
|
||||||
BufferedImage output = resampler.filter(input, null);
|
BufferedImage output = resampler.filter(input, null);
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Using the DiffusionDither
|
#### Using the DiffusionDither
|
||||||
@@ -428,14 +207,14 @@ The library comes with a dithering operation, that can be used to convert `Buffe
|
|||||||
Floyd-Steinberg error-diffusion dither.
|
Floyd-Steinberg error-diffusion dither.
|
||||||
|
|
||||||
```java
|
```java
|
||||||
import com.twelvemonkeys.image.DiffusionDither;
|
import com.twelvemonkeys.image.DiffusionDither;
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|
||||||
BufferedImage input = ...; // Image to dither
|
BufferedImage input = ...; // Image to dither
|
||||||
|
|
||||||
BufferedImageOp ditherer = new DiffusionDither();
|
BufferedImageOp ditherer = new DiffusionDither();
|
||||||
BufferedImage output = ditherer.filter(input, null);
|
BufferedImage output = ditherer.filter(input, null);
|
||||||
```
|
```
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
@@ -451,7 +230,7 @@ Build the project (using [Maven](http://maven.apache.org/download.cgi)):
|
|||||||
|
|
||||||
$ mvn package
|
$ mvn package
|
||||||
|
|
||||||
Currently, the recommended JDK for making a build is Oracle JDK 7.x or 8.x.
|
Currently, the recommended JDK for making a build is Oracle JDK 8.x.
|
||||||
|
|
||||||
It's possible to build using OpenJDK, but some tests might fail due to some minor differences between the color management systems used. You will need to either disable the tests in question, or build without tests altogether.
|
It's possible to build using OpenJDK, but some tests might fail due to some minor differences between the color management systems used. You will need to either disable the tests in question, or build without tests altogether.
|
||||||
|
|
||||||
@@ -473,10 +252,10 @@ The ImageIO registry and service lookup mechanism will make sure the plugins are
|
|||||||
To verify that the JPEG plugin is installed and used at run-time, you could use the following code:
|
To verify that the JPEG plugin is installed and used at run-time, you could use the following code:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName("JPEG");
|
Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName("JPEG");
|
||||||
while (readers.hasNext()) {
|
while (readers.hasNext()) {
|
||||||
System.out.println("reader: " + readers.next());
|
System.out.println("reader: " + readers.next());
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The first line should print:
|
The first line should print:
|
||||||
@@ -488,82 +267,144 @@ The first line should print:
|
|||||||
To depend on the JPEG and TIFF plugin using Maven, add the following to your POM:
|
To depend on the JPEG and TIFF plugin using Maven, add the following to your POM:
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
|
...
|
||||||
|
<dependencies>
|
||||||
...
|
...
|
||||||
<dependencies>
|
<dependency>
|
||||||
...
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<dependency>
|
<artifactId>imageio-jpeg</artifactId>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<version>3.6.3</version>
|
||||||
<artifactId>imageio-jpeg</artifactId>
|
</dependency>
|
||||||
<version>3.4.1</version>
|
<dependency>
|
||||||
</dependency>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<dependency>
|
<artifactId>imageio-tiff</artifactId>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<version>3.6.3</version>
|
||||||
<artifactId>imageio-tiff</artifactId>
|
</dependency>
|
||||||
<version>3.4.1</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Optional dependency. Needed only if you deploy `ImageIO` plugins as part of a web app.
|
Optional dependency. Needed only if you deploy ImageIO plugins as part of a web app.
|
||||||
Make sure you add the `IIOProviderContextListener` to your `web.xml`, see above.
|
Make sure you add the IIOProviderContextListener to your web.xml, see above.
|
||||||
-->
|
-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.twelvemonkeys.servlet</groupId>
|
<groupId>com.twelvemonkeys.servlet</groupId>
|
||||||
<artifactId>servlet</artifactId>
|
<artifactId>servlet</artifactId>
|
||||||
<version>3.4.1</version>
|
<version>3.6.3</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Manual dependency example
|
#### Manual dependency example
|
||||||
|
|
||||||
To depend on the JPEG and TIFF plugin in your IDE or program, add all of the following JARs to your class path:
|
To depend on the JPEG and TIFF plugin in your IDE or program, add all of the following JARs to your class path:
|
||||||
|
|
||||||
twelvemonkeys-common-lang-3.4.1.jar
|
twelvemonkeys-common-lang-3.6.3.jar
|
||||||
twelvemonkeys-common-io-3.4.1.jar
|
twelvemonkeys-common-io-3.6.3.jar
|
||||||
twelvemonkeys-common-image-3.4.1.jar
|
twelvemonkeys-common-image-3.6.3.jar
|
||||||
twelvemonkeys-imageio-core-3.4.1.jar
|
twelvemonkeys-imageio-core-3.6.3.jar
|
||||||
twelvemonkeys-imageio-metadata-3.4.1.jar
|
twelvemonkeys-imageio-metadata-3.6.3.jar
|
||||||
twelvemonkeys-imageio-jpeg-3.4.1.jar
|
twelvemonkeys-imageio-jpeg-3.6.3.jar
|
||||||
twelvemonkeys-imageio-tiff-3.4.1.jar
|
twelvemonkeys-imageio-tiff-3.6.3.jar
|
||||||
|
|
||||||
|
#### Deploying the plugins in a web app
|
||||||
|
|
||||||
|
Because the `ImageIO` plugin registry (the `IIORegistry`) is "VM global", it doesn't by default work well with
|
||||||
|
servlet contexts. This is especially evident if you load plugins from the `WEB-INF/lib` or `classes` folder.
|
||||||
|
Unless you add `ImageIO.scanForPlugins()` somewhere in your code, the plugins might never be available at all.
|
||||||
|
|
||||||
|
In addition, servlet contexts dynamically loads and unloads classes (using a new class loader per context).
|
||||||
|
If you restart your application, old classes will by default remain in memory forever (because the next time
|
||||||
|
`scanForPlugins` is called, it's another `ClassLoader` that scans/loads classes, and thus they will be new instances
|
||||||
|
in the registry). If a read is attempted using one of the remaining "old" readers, weird exceptions
|
||||||
|
(like `NullPointerException`s when accessing `static final` initialized fields or `NoClassDefFoundError`s
|
||||||
|
for uninitialized inner classes) may occur.
|
||||||
|
|
||||||
|
To work around both the discovery problem and the resource leak,
|
||||||
|
it is *strongly recommended* to use the `IIOProviderContextListener` that implements
|
||||||
|
dynamic loading and unloading of ImageIO plugins for web applications.
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<web-app ...>
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
<listener>
|
||||||
|
<display-name>ImageIO service provider loader/unloader</display-name>
|
||||||
|
<listener-class>com.twelvemonkeys.servlet.image.IIOProviderContextListener</listener-class>
|
||||||
|
</listener>
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
</web-app>
|
||||||
|
```
|
||||||
|
|
||||||
|
Loading plugins from `WEB-INF/lib` without the context listener installed is unsupported and will not work correctly.
|
||||||
|
|
||||||
|
The context listener has no dependencies to the TwelveMonkeys ImageIO plugins, and may be used with JAI ImageIO
|
||||||
|
or other ImageIO plugins as well.
|
||||||
|
|
||||||
|
Another safe option, is to place the JAR files in the application server's shared or common lib folder.
|
||||||
|
|
||||||
|
#### Including the plugins in a "fat" JAR
|
||||||
|
|
||||||
|
The recommended way to use the plugins, is just to include the JARs as-is in your project, through a Maven dependency or similar.
|
||||||
|
Re-packaging is not necessary to use the library, and not recommended.
|
||||||
|
|
||||||
|
However, if you like to create a "fat"
|
||||||
|
JAR, or otherwise like to re-package the JARs for some reason, it's important to remember that automatic discovery of
|
||||||
|
the plugins by ImageIO depends on the
|
||||||
|
[Service Provider Interface (SPI)](https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html) mechanism.
|
||||||
|
In short, each JAR contains a special folder, named `META-INF/services` containing one or more files,
|
||||||
|
typically `javax.imageio.spi.ImageReaderSpi` and `javax.imageio.spi.ImageWriterSpi`.
|
||||||
|
These files exist *with the same name in every JAR*,
|
||||||
|
so if you simply unpack everything to a single folder or create a JAR, files will be overwritten and behavior be
|
||||||
|
unspecified (most likely you will end up with a single plugin being installed).
|
||||||
|
|
||||||
|
The solution is to make sure all files with the same name, are merged to a single file,
|
||||||
|
containing all the SPI information of each type. If using the Maven Shade plugin, you should use the
|
||||||
|
[ServicesResourceTransformer](https://maven.apache.org/plugins/maven-shade-plugin/examples/resource-transformers.html#ServicesResourceTransformer)
|
||||||
|
to properly merge these files. You may also want to use the
|
||||||
|
[ManifestResourceTransforme](https://maven.apache.org/plugins/maven-shade-plugin/examples/resource-transformers.html#ManifestResourceTransformer)
|
||||||
|
to get the correct vendor name, version info etc.
|
||||||
|
Other "fat" JAR bundlers will probably have similar mechanisms to merge entries with the same name.
|
||||||
|
|
||||||
### Links to prebuilt binaries
|
### Links to prebuilt binaries
|
||||||
|
|
||||||
##### Latest version (3.4.1)
|
##### Latest version (3.6.3)
|
||||||
|
|
||||||
Requires Java 7 or later.
|
Requires Java 7 or later.
|
||||||
|
|
||||||
Common dependencies
|
Common dependencies
|
||||||
* [common-lang-3.4.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-lang/3.4.1/common-lang-3.4.1.jar)
|
* [common-lang-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-lang/3.6.3/common-lang-3.6.3.jar)
|
||||||
* [common-io-3.4.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-io/3.4.1/common-io-3.4.1.jar)
|
* [common-io-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-io/3.6.3/common-io-3.6.3.jar)
|
||||||
* [common-image-3.4.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-image/3.4.1/common-image-3.4.1.jar)
|
* [common-image-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-image/3.6.3/common-image-3.6.3.jar)
|
||||||
|
|
||||||
ImageIO dependencies
|
ImageIO dependencies
|
||||||
* [imageio-core-3.4.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-core/3.4.1/imageio-core-3.4.1.jar)
|
* [imageio-core-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-core/3.6.3/imageio-core-3.6.3.jar)
|
||||||
* [imageio-metadata-3.4.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-metadata/3.4.1/imageio-metadata-3.4.1.jar)
|
* [imageio-metadata-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-metadata/3.6.3/imageio-metadata-3.6.3.jar)
|
||||||
|
|
||||||
ImageIO plugins
|
ImageIO plugins
|
||||||
* [imageio-bmp-3.4.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-bmp/3.4.1/imageio-bmp-3.4.1.jar)
|
* [imageio-bmp-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-bmp/3.6.3/imageio-bmp-3.6.3.jar)
|
||||||
* [imageio-jpeg-3.4.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jpeg/3.4.1/imageio-jpeg-3.4.1.jar)
|
* [imageio-hdr-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-hdr/3.6.3/imageio-hdr-3.6.3.jar)
|
||||||
* [imageio-tiff-3.4.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tiff/3.4.1/imageio-tiff-3.4.1.jar)
|
* [imageio-icns-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-icns/3.6.3/imageio-icns-3.6.3.jar)
|
||||||
* [imageio-pnm-3.4.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pnm/3.4.1/imageio-pnm-3.4.1.jar)
|
* [imageio-iff-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-iff/3.6.3/imageio-iff-3.6.3.jar)
|
||||||
* [imageio-psd-3.4.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-psd/3.4.1/imageio-psd-3.4.1.jar)
|
* [imageio-jpeg-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jpeg/3.6.3/imageio-jpeg-3.6.3.jar)
|
||||||
* [imageio-hdr-3.4.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-hdr/3.4.1/imageio-hdr-3.4.1.jar)
|
* [imageio-pcx-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pcx/3.6.3/imageio-pcx-3.6.3.jar)
|
||||||
* [imageio-iff-3.4.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-iff/3.4.1/imageio-iff-3.4.1.jar)
|
* [imageio-pict-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pict/3.6.3/imageio-pict-3.6.3.jar)
|
||||||
* [imageio-pcx-3.4.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pcx/3.4.1/imageio-pcx-3.4.1.jar)
|
* [imageio-pnm-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pnm/3.6.3/imageio-pnm-3.6.3.jar)
|
||||||
* [imageio-pict-3.4.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pict/3.4.1/imageio-pict-3.4.1.jar)
|
* [imageio-psd-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-psd/3.6.3/imageio-psd-3.6.3.jar)
|
||||||
* [imageio-sgi-3.4.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-sgi/3.4.1/imageio-sgi-3.4.1.jar)
|
* [imageio-sgi-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-sgi/3.6.3/imageio-sgi-3.6.3.jar)
|
||||||
* [imageio-tga-3.4.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tga/3.4.1/imageio-tga-3.4.1.jar)
|
* [imageio-tga-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tga/3.6.3/imageio-tga-3.6.3.jar)
|
||||||
* [imageio-icns-3.4.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-icns/3.4.1/imageio-icns-3.4.1.jar)
|
* [imageio-thumbsdb-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-thumbsdb/3.6.3/imageio-thumbsdb-3.6.3.jar)
|
||||||
* [imageio-thumbsdb-3.4.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-thumbsdb/3.4.1/imageio-thumbsdb-3.4.1.jar)
|
* [imageio-tiff-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tiff/3.6.3/imageio-tiff-3.6.3.jar)
|
||||||
|
|
||||||
ImageIO plugins requiring 3rd party libs
|
ImageIO plugins requiring 3rd party libs
|
||||||
* [imageio-batik-3.4.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-batik/3.4.1/imageio-batik-3.4.1.jar)
|
* [imageio-batik-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-batik/3.6.3/imageio-batik-3.6.3.jar)
|
||||||
|
|
||||||
Photoshop Path support for ImageIO
|
Photoshop Path support for ImageIO
|
||||||
* [imageio-clippath-3.4.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-clippath/3.4.1/imageio-clippath-3.4.1.jar)
|
* [imageio-clippath-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-clippath/3.6.3/imageio-clippath-3.6.3.jar)
|
||||||
|
|
||||||
Servlet support
|
Servlet support
|
||||||
* [servlet-3.4.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/servlet/servlet/3.4.1/servlet-3.4.1.jar)
|
* [servlet-3.6.3.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/servlet/servlet/3.6.3/servlet-3.6.3.jar)
|
||||||
|
|
||||||
##### Old version (3.0.x)
|
##### Old version (3.0.x)
|
||||||
|
|
||||||
@@ -598,9 +439,9 @@ Servlet support
|
|||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
The project is distributed under the OSI approved [BSD license](http://opensource.org/licenses/BSD-3-Clause):
|
This project is provided under the OSI approved [BSD license](http://opensource.org/licenses/BSD-3-Clause):
|
||||||
|
|
||||||
Copyright (c) 2008-2018, Harald Kuhr
|
Copyright (c) 2008-2020, Harald Kuhr
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
@@ -661,10 +502,15 @@ the Sun/Oracle provided `JPEGImageReader` and `BMPImageReader`, and the Apple pr
|
|||||||
respectively. Using the pairwise ordering will not remove any functionality form these implementations, but in most
|
respectively. Using the pairwise ordering will not remove any functionality form these implementations, but in most
|
||||||
cases you'll end up using the TwelveMonkeys plug-ins instead.
|
cases you'll end up using the TwelveMonkeys plug-ins instead.
|
||||||
|
|
||||||
|
q: Why is there no support for common formats like GIF or PNG?
|
||||||
|
|
||||||
|
a: The short answer is simply that the built-in support in ImageIO for these formats are good enough as-is.
|
||||||
|
If you are looking for better PNG write performance on Java 7 and 8, see [JDK9 PNG Writer Backport](https://github.com/gredler/jdk9-png-writer-backport).
|
||||||
|
|
||||||
|
|
||||||
q: What about JAI? Several of the formats are already supported by JAI.
|
q: What about JAI? Several of the formats are already supported by JAI.
|
||||||
|
|
||||||
a: While JAI (and jai-imageio in particular) have support for some of the formats, JAI has some major issues.
|
a: While JAI (and jai-imageio in particular) have support for some of the same formats, JAI has some major issues.
|
||||||
The most obvious being:
|
The most obvious being:
|
||||||
- It's not actively developed. No issues has been fixed for years.
|
- It's not actively developed. No issues has been fixed for years.
|
||||||
- To get full format support, you need native libs.
|
- To get full format support, you need native libs.
|
||||||
|
|||||||
+6
-1
@@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys</groupId>
|
<groupId>com.twelvemonkeys</groupId>
|
||||||
<artifactId>twelvemonkeys</artifactId>
|
<artifactId>twelvemonkeys</artifactId>
|
||||||
<version>3.4.4-SNAPSHOT</version>
|
<version>3.6.5-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<groupId>com.twelvemonkeys.bom</groupId>
|
<groupId>com.twelvemonkeys.bom</groupId>
|
||||||
@@ -123,6 +123,11 @@
|
|||||||
<artifactId>imageio-tiff</artifactId>
|
<artifactId>imageio-tiff</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
|
<artifactId>imageio-xwd</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- ImageIO 3rd party dependent plugins -->
|
<!-- ImageIO 3rd party dependent plugins -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.common</groupId>
|
<groupId>com.twelvemonkeys.common</groupId>
|
||||||
<artifactId>common</artifactId>
|
<artifactId>common</artifactId>
|
||||||
<version>3.4.4-SNAPSHOT</version>
|
<version>3.6.5-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>common-image</artifactId>
|
<artifactId>common-image</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.common</groupId>
|
<groupId>com.twelvemonkeys.common</groupId>
|
||||||
<artifactId>common</artifactId>
|
<artifactId>common</artifactId>
|
||||||
<version>3.4.4-SNAPSHOT</version>
|
<version>3.6.5-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>common-io</artifactId>
|
<artifactId>common-io</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(byte pBytes[], int pOffset, int pLength) {
|
public void write(byte[] pBytes, int pOffset, int pLength) {
|
||||||
if ((pOffset < 0) || (pOffset > pBytes.length) || (pLength < 0) ||
|
if ((pOffset < 0) || (pOffset > pBytes.length) || (pLength < 0) ||
|
||||||
((pOffset + pLength) > pBytes.length) || ((pOffset + pLength) < 0)) {
|
((pOffset + pLength) > pBytes.length) || ((pOffset + pLength) < 0)) {
|
||||||
throw new IndexOutOfBoundsException();
|
throw new IndexOutOfBoundsException();
|
||||||
@@ -98,7 +98,7 @@ public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
|
|||||||
private void growIfNeeded(int pNewCount) {
|
private void growIfNeeded(int pNewCount) {
|
||||||
if (pNewCount > buf.length) {
|
if (pNewCount > buf.length) {
|
||||||
int newSize = Math.max(Math.min(buf.length << 1, buf.length + maxGrowSize), pNewCount);
|
int newSize = Math.max(Math.min(buf.length << 1, buf.length + maxGrowSize), pNewCount);
|
||||||
byte newBuf[] = new byte[newSize];
|
byte[] newBuf = new byte[newSize];
|
||||||
System.arraycopy(buf, 0, newBuf, 0, count);
|
System.arraycopy(buf, 0, newBuf, 0, count);
|
||||||
buf = newBuf;
|
buf = newBuf;
|
||||||
}
|
}
|
||||||
@@ -113,7 +113,7 @@ public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
|
|||||||
// Non-synchronized version of toByteArray
|
// Non-synchronized version of toByteArray
|
||||||
@Override
|
@Override
|
||||||
public byte[] toByteArray() {
|
public byte[] toByteArray() {
|
||||||
byte newBuf[] = new byte[count];
|
byte[] newBuf = new byte[count];
|
||||||
System.arraycopy(buf, 0, newBuf, 0, count);
|
System.arraycopy(buf, 0, newBuf, 0, count);
|
||||||
|
|
||||||
return newBuf;
|
return newBuf;
|
||||||
|
|||||||
@@ -922,7 +922,7 @@ public final class FileUtil {
|
|||||||
// A URL should never be able to represent an opaque URI, test anyway
|
// A URL should never be able to represent an opaque URI, test anyway
|
||||||
throw new IllegalArgumentException("URI is not hierarchical");
|
throw new IllegalArgumentException("URI is not hierarchical");
|
||||||
}
|
}
|
||||||
if (path.equals("")) {
|
if (path.isEmpty()) {
|
||||||
throw new IllegalArgumentException("URI path component is empty");
|
throw new IllegalArgumentException("URI path component is empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-4
@@ -33,12 +33,12 @@ package com.twelvemonkeys.io.ole2;
|
|||||||
import com.twelvemonkeys.io.InputStreamAbstractTest;
|
import com.twelvemonkeys.io.InputStreamAbstractTest;
|
||||||
import com.twelvemonkeys.io.LittleEndianDataOutputStream;
|
import com.twelvemonkeys.io.LittleEndianDataOutputStream;
|
||||||
import com.twelvemonkeys.io.MemoryCacheSeekableStream;
|
import com.twelvemonkeys.io.MemoryCacheSeekableStream;
|
||||||
import com.twelvemonkeys.io.SeekableInputStream;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.net.URISyntaxException;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.net.URL;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.common</groupId>
|
<groupId>com.twelvemonkeys.common</groupId>
|
||||||
<artifactId>common</artifactId>
|
<artifactId>common</artifactId>
|
||||||
<version>3.4.4-SNAPSHOT</version>
|
<version>3.6.5-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>common-lang</artifactId>
|
<artifactId>common-lang</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ public final class BeanUtil {
|
|||||||
|
|
||||||
while (begIdx < pProperty.length() && begIdx >= 0) {
|
while (begIdx < pProperty.length() && begIdx >= 0) {
|
||||||
|
|
||||||
endIdx = pProperty.indexOf(".", endIdx + 1);
|
endIdx = pProperty.indexOf('.', endIdx + 1);
|
||||||
if (endIdx > 0) {
|
if (endIdx > 0) {
|
||||||
subProp = pProperty.substring(begIdx, endIdx);
|
subProp = pProperty.substring(begIdx, endIdx);
|
||||||
begIdx = endIdx + 1;
|
begIdx = endIdx + 1;
|
||||||
@@ -106,7 +106,7 @@ public final class BeanUtil {
|
|||||||
Class[] paramClass = new Class[0];
|
Class[] paramClass = new Class[0];
|
||||||
|
|
||||||
int begBracket;
|
int begBracket;
|
||||||
if ((begBracket = subProp.indexOf("[")) > 0) {
|
if ((begBracket = subProp.indexOf('[')) > 0) {
|
||||||
// An error if there is no matching bracket
|
// An error if there is no matching bracket
|
||||||
if (!subProp.endsWith("]")) {
|
if (!subProp.endsWith("]")) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -129,8 +129,7 @@ public class Time {
|
|||||||
* @see #toString(String)
|
* @see #toString(String)
|
||||||
*/
|
*/
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "" + getMinutes() + ":"
|
return getMinutes() + ":" + (getSeconds() < 10 ? "0" : "") + getSeconds();
|
||||||
+ (getSeconds() < 10 ? "0" : "") + getSeconds();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -404,7 +404,7 @@ class SecondsFormatter extends TimeFormatter {
|
|||||||
|
|
||||||
// Else return it with leading 0's
|
// Else return it with leading 0's
|
||||||
//return StringUtil.formatNumber(t.getSeconds(), digits);
|
//return StringUtil.formatNumber(t.getSeconds(), digits);
|
||||||
return StringUtil.pad("" + t.getSeconds(), digits, "0", true);
|
return StringUtil.pad(String.valueOf(t.getSeconds()), digits, "0", true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,7 +425,7 @@ class MinutesFormatter extends TimeFormatter {
|
|||||||
|
|
||||||
// Else return it with leading 0's
|
// Else return it with leading 0's
|
||||||
//return StringUtil.formatNumber(t.getMinutes(), digits);
|
//return StringUtil.formatNumber(t.getMinutes(), digits);
|
||||||
return StringUtil.pad("" + t.getMinutes(), digits, "0", true);
|
return StringUtil.pad(String.valueOf(t.getMinutes()), digits, "0", true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -622,7 +622,7 @@ public class WildcardStringParser {
|
|||||||
buffer.append("\n");
|
buffer.append("\n");
|
||||||
buffer.append(" Format: <state index>: <character> (<last free state>)");
|
buffer.append(" Format: <state index>: <character> (<last free state>)");
|
||||||
buffer.append("\n");
|
buffer.append("\n");
|
||||||
buffer.append(" Number of strings parsed: " + totalNumberOfStringsParsed);
|
buffer.append(" Number of strings parsed: ").append(totalNumberOfStringsParsed);
|
||||||
buffer.append("\n");
|
buffer.append("\n");
|
||||||
}
|
}
|
||||||
return buffer.toString();
|
return buffer.toString();
|
||||||
|
|||||||
@@ -1117,18 +1117,19 @@ public abstract class CollectionAbstractTest extends ObjectAbstractTest {
|
|||||||
/**
|
/**
|
||||||
* Tests {@link Collection#toArray(Object[])}.
|
* Tests {@link Collection#toArray(Object[])}.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings({"SuspiciousToArrayCall", "RedundantCast"})
|
||||||
@Test
|
@Test
|
||||||
public void testCollectionToArray2() {
|
public void testCollectionToArray2() {
|
||||||
resetEmpty();
|
resetEmpty();
|
||||||
Object[] a = new Object[] { new Object(), null, null };
|
Object[] a = new Object[] { new Object(), null, null };
|
||||||
Object[] array = collection.toArray(a);
|
Object[] array = collection.toArray(a);
|
||||||
assertEquals("Given array shouldn't shrink", array, a);
|
assertArrayEquals("Given array shouldn't shrink", array, a);
|
||||||
assertEquals("Last element should be set to null", a[0], null);
|
assertNull("Last element should be set to null", a[0]);
|
||||||
verifyAll();
|
verifyAll();
|
||||||
|
|
||||||
resetFull();
|
resetFull();
|
||||||
try {
|
try {
|
||||||
array = collection.toArray(new Void[0]);
|
collection.toArray(new Void[0]);
|
||||||
fail("toArray(new Void[0]) should raise ArrayStore");
|
fail("toArray(new Void[0]) should raise ArrayStore");
|
||||||
} catch (ArrayStoreException e) {
|
} catch (ArrayStoreException e) {
|
||||||
// expected
|
// expected
|
||||||
@@ -1136,7 +1137,7 @@ public abstract class CollectionAbstractTest extends ObjectAbstractTest {
|
|||||||
verifyAll();
|
verifyAll();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
array = collection.toArray(null);
|
collection.toArray((Object[]) null);
|
||||||
fail("toArray(null) should raise NPE");
|
fail("toArray(null) should raise NPE");
|
||||||
} catch (NullPointerException e) {
|
} catch (NullPointerException e) {
|
||||||
// expected
|
// expected
|
||||||
@@ -1150,13 +1151,13 @@ public abstract class CollectionAbstractTest extends ObjectAbstractTest {
|
|||||||
|
|
||||||
// Figure out if they're all the same class
|
// Figure out if they're all the same class
|
||||||
// TODO: It'd be nicer to detect a common superclass
|
// TODO: It'd be nicer to detect a common superclass
|
||||||
HashSet classes = new HashSet();
|
HashSet<Class<?>> classes = new HashSet<>();
|
||||||
for (int i = 0; i < array.length; i++) {
|
for (int i = 0; i < array.length; i++) {
|
||||||
classes.add((array[i] == null) ? null : array[i].getClass());
|
classes.add((array[i] == null) ? null : array[i].getClass());
|
||||||
}
|
}
|
||||||
if (classes.size() > 1) return;
|
if (classes.size() > 1) return;
|
||||||
|
|
||||||
Class cl = (Class)classes.iterator().next();
|
Class<?> cl = (Class<?>)classes.iterator().next();
|
||||||
if (Map.Entry.class.isAssignableFrom(cl)) { // check needed for protective cases like Predicated/Unmod map entrySet
|
if (Map.Entry.class.isAssignableFrom(cl)) { // check needed for protective cases like Predicated/Unmod map entrySet
|
||||||
cl = Map.Entry.class;
|
cl = Map.Entry.class;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -250,12 +250,12 @@ public class CollectionUtilTest {
|
|||||||
assertCorrectListIterator(new ArrayList<String>(Arrays.asList(new String[] {"foo", "bar", "baz", "boo"})).subList(1, 3).listIterator(0), new String[] {"bar", "baz"}, true, true);
|
assertCorrectListIterator(new ArrayList<String>(Arrays.asList(new String[] {"foo", "bar", "baz", "boo"})).subList(1, 3).listIterator(0), new String[] {"bar", "baz"}, true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertCorrectListIterator(ListIterator<String> iterator, final Object[] elements) {
|
private static void assertCorrectListIterator(ListIterator<String> iterator, final Object[] elements) {
|
||||||
assertCorrectListIterator(iterator, elements, false, false);
|
assertCorrectListIterator(iterator, elements, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: The test is can only test list iterators with a starting index == 0
|
// NOTE: The test is can only test list iterators with a starting index == 0
|
||||||
private void assertCorrectListIterator(ListIterator<String> iterator, final Object[] elements, boolean skipRemove, boolean skipAdd) {
|
private static void assertCorrectListIterator(ListIterator<String> iterator, final Object[] elements, boolean skipRemove, boolean skipAdd) {
|
||||||
// Index is now "before 0"
|
// Index is now "before 0"
|
||||||
assertEquals(-1, iterator.previousIndex());
|
assertEquals(-1, iterator.previousIndex());
|
||||||
assertEquals(0, iterator.nextIndex());
|
assertEquals(0, iterator.nextIndex());
|
||||||
|
|||||||
+2
-2
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys</groupId>
|
<groupId>com.twelvemonkeys</groupId>
|
||||||
<artifactId>twelvemonkeys</artifactId>
|
<artifactId>twelvemonkeys</artifactId>
|
||||||
<version>3.4.4-SNAPSHOT</version>
|
<version>3.6.5-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<groupId>com.twelvemonkeys.common</groupId>
|
<groupId>com.twelvemonkeys.common</groupId>
|
||||||
<artifactId>common</artifactId>
|
<artifactId>common</artifactId>
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>junit</groupId>
|
<groupId>junit</groupId>
|
||||||
<artifactId>junit</artifactId>
|
<artifactId>junit</artifactId>
|
||||||
<version>4.7</version>
|
<version>4.13.1</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|||||||
+2
-2
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys</groupId>
|
<groupId>com.twelvemonkeys</groupId>
|
||||||
<artifactId>twelvemonkeys</artifactId>
|
<artifactId>twelvemonkeys</artifactId>
|
||||||
<version>3.4.4-SNAPSHOT</version>
|
<version>3.6.5-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<groupId>com.twelvemonkeys.contrib</groupId>
|
<groupId>com.twelvemonkeys.contrib</groupId>
|
||||||
<artifactId>contrib</artifactId>
|
<artifactId>contrib</artifactId>
|
||||||
@@ -65,7 +65,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>junit</groupId>
|
<groupId>junit</groupId>
|
||||||
<artifactId>junit</artifactId>
|
<artifactId>junit</artifactId>
|
||||||
<version>4.7</version>
|
<version>4.13.1</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|||||||
@@ -0,0 +1,158 @@
|
|||||||
|
package com.twelvemonkeys.contrib.exif;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.image.ImageUtil;
|
||||||
|
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||||
|
|
||||||
|
import org.w3c.dom.NodeList;
|
||||||
|
|
||||||
|
import javax.imageio.IIOImage;
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import javax.imageio.ImageReader;
|
||||||
|
import javax.imageio.metadata.IIOMetadata;
|
||||||
|
import javax.imageio.metadata.IIOMetadataFormatImpl;
|
||||||
|
import javax.imageio.metadata.IIOMetadataNode;
|
||||||
|
import javax.imageio.stream.ImageInputStream;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import static com.twelvemonkeys.contrib.tiff.TIFFUtilities.applyOrientation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EXIFUtilities.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
|
* @version : EXIFUtilities.java,v 1.0 23/06/2020
|
||||||
|
*/
|
||||||
|
public class EXIFUtilities {
|
||||||
|
/**
|
||||||
|
* Reads image and metadata, applies Exif orientation to image, and returns everything as an {@code IIOImage}.
|
||||||
|
*
|
||||||
|
* @param input a {@code URL}
|
||||||
|
* @return an {@code IIOImage} containing the correctly oriented image and metadata including rotation info.
|
||||||
|
* @throws IOException if an error occurs during reading.
|
||||||
|
*/
|
||||||
|
public static IIOImage readWithOrientation(final URL input) throws IOException {
|
||||||
|
try (ImageInputStream stream = ImageIO.createImageInputStream(input)) {
|
||||||
|
return readWithOrientation(stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads image and metadata, applies Exif orientation to image, and returns everything as an {@code IIOImage}.
|
||||||
|
*
|
||||||
|
* @param input an {@code InputStream}
|
||||||
|
* @return an {@code IIOImage} containing the correctly oriented image and metadata including rotation info.
|
||||||
|
* @throws IOException if an error occurs during reading.
|
||||||
|
*/
|
||||||
|
public static IIOImage readWithOrientation(final InputStream input) throws IOException {
|
||||||
|
try (ImageInputStream stream = ImageIO.createImageInputStream(input)) {
|
||||||
|
return readWithOrientation(stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads image and metadata, applies Exif orientation to image, and returns everything as an {@code IIOImage}.
|
||||||
|
*
|
||||||
|
* @param input a {@code File}
|
||||||
|
* @return an {@code IIOImage} containing the correctly oriented image and metadata including rotation info.
|
||||||
|
* @throws IOException if an error occurs during reading.
|
||||||
|
*/
|
||||||
|
public static IIOImage readWithOrientation(final File input) throws IOException {
|
||||||
|
try (ImageInputStream stream = ImageIO.createImageInputStream(input)) {
|
||||||
|
return readWithOrientation(stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads image and metadata, applies Exif orientation to image, and returns everything as an {@code IIOImage}.
|
||||||
|
*
|
||||||
|
* @param input an {@code ImageInputStream}
|
||||||
|
* @return an {@code IIOImage} containing the correctly oriented image and metadata including rotation info.
|
||||||
|
* @throws IOException if an error occurs during reading.
|
||||||
|
*/
|
||||||
|
public static IIOImage readWithOrientation(final ImageInputStream input) throws IOException {
|
||||||
|
Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
|
||||||
|
if (!readers.hasNext()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageReader reader = readers.next();
|
||||||
|
try {
|
||||||
|
reader.setInput(input, true, false);
|
||||||
|
|
||||||
|
IIOMetadata metadata = reader.getImageMetadata(0);
|
||||||
|
BufferedImage bufferedImage = applyOrientation(reader.read(0), findImageOrientation(metadata).value());
|
||||||
|
|
||||||
|
return new IIOImage(bufferedImage, null, metadata);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
reader.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the {@code ImageOrientation} tag, if any, and returns an {@link Orientation} based on its
|
||||||
|
* {@code value} attribute.
|
||||||
|
* If no match is found or the tag is not present, {@code Normal} (the default orientation) is returned.
|
||||||
|
*
|
||||||
|
* @param metadata an {@code IIOMetadata} object
|
||||||
|
* @return the {@code Orientation} matching the {@code value} attribute of the {@code ImageOrientation} tag,
|
||||||
|
* or {@code Normal}, never {@code null}.
|
||||||
|
* @see Orientation
|
||||||
|
* @see <a href="https://docs.oracle.com/javase/7/docs/api/javax/imageio/metadata/doc-files/standard_metadata.html">Standard (Plug-in Neutral) Metadata Format Specification</a>
|
||||||
|
*/
|
||||||
|
public static Orientation findImageOrientation(final IIOMetadata metadata) {
|
||||||
|
if (metadata != null) {
|
||||||
|
IIOMetadataNode root = (IIOMetadataNode) metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName);
|
||||||
|
NodeList imageOrientations = root.getElementsByTagName("ImageOrientation");
|
||||||
|
|
||||||
|
if (imageOrientations != null && imageOrientations.getLength() > 0) {
|
||||||
|
IIOMetadataNode imageOrientation = (IIOMetadataNode) imageOrientations.item(0);
|
||||||
|
return Orientation.fromMetadataOrientation(imageOrientation.getAttribute("value"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Orientation.Normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws IOException {
|
||||||
|
for (String arg : args) {
|
||||||
|
File input = new File(arg);
|
||||||
|
|
||||||
|
// Read everything but thumbnails (similar to ImageReader.readAll(0, null)),
|
||||||
|
// and applies the correct image orientation
|
||||||
|
IIOImage image = readWithOrientation(input);
|
||||||
|
|
||||||
|
if (image == null) {
|
||||||
|
System.err.printf("No reader for %s%n", input);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finds the orientation as defined by the javax_imageio_1.0 format
|
||||||
|
Orientation orientation = findImageOrientation(image.getMetadata());
|
||||||
|
|
||||||
|
// Retrieve the image as a BufferedImage. The image is already rotated by the readWithOrientation method
|
||||||
|
// In this case it will already be a BufferedImage, so using a cast will also do
|
||||||
|
// (i.e.: BufferedImage bufferedImage = (BufferedImage) image.getRenderedImage())
|
||||||
|
BufferedImage bufferedImage = ImageUtil.toBuffered(image.getRenderedImage());
|
||||||
|
|
||||||
|
// Demo purpose only, show image with orientation details in title
|
||||||
|
DisplayHelper.showIt(bufferedImage, input.getName() + ": " + orientation.name() + "/" + orientation.value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't do this... :-) Provided for convenience/demo only!
|
||||||
|
static abstract class DisplayHelper extends ImageReaderBase {
|
||||||
|
private DisplayHelper() {
|
||||||
|
super(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void showIt(BufferedImage image, String title) {
|
||||||
|
ImageReaderBase.showIt(image, title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package com.twelvemonkeys.contrib.exif;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.contrib.tiff.TIFFUtilities;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Orientation.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
|
* @version : Orientation.java,v 1.0 10/07/2020 harald.kuhr
|
||||||
|
*/
|
||||||
|
public enum Orientation {
|
||||||
|
Normal(TIFFUtilities.TIFFBaseline.ORIENTATION_TOPLEFT),
|
||||||
|
FlipH(TIFFUtilities.TIFFExtension.ORIENTATION_TOPRIGHT),
|
||||||
|
Rotate180(TIFFUtilities.TIFFExtension.ORIENTATION_BOTRIGHT),
|
||||||
|
FlipV(TIFFUtilities.TIFFExtension.ORIENTATION_BOTLEFT),
|
||||||
|
FlipVRotate90(TIFFUtilities.TIFFExtension.ORIENTATION_LEFTTOP),
|
||||||
|
Rotate270(TIFFUtilities.TIFFExtension.ORIENTATION_RIGHTTOP),
|
||||||
|
FlipHRotate90(TIFFUtilities.TIFFExtension.ORIENTATION_RIGHTBOT),
|
||||||
|
Rotate90(TIFFUtilities.TIFFExtension.ORIENTATION_LEFTBOT);
|
||||||
|
|
||||||
|
// name as defined in javax.imageio metadata
|
||||||
|
private final int value; // value as defined in TIFF spec
|
||||||
|
|
||||||
|
Orientation(int value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int value() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Orientation fromMetadataOrientation(final String orientationName) {
|
||||||
|
if (orientationName != null) {
|
||||||
|
try {
|
||||||
|
return valueOf(orientationName);
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException e) {
|
||||||
|
// Not found, try ignore case match, as some metadata implementations are known to return "normal" etc.
|
||||||
|
String lowerCaseName = orientationName.toLowerCase();
|
||||||
|
|
||||||
|
for (Orientation orientation : values()) {
|
||||||
|
if (orientation.name().toLowerCase().equals(lowerCaseName)) {
|
||||||
|
return orientation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metadata does not have other orientations, default to Normal
|
||||||
|
return Normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Orientation fromTIFFOrientation(final int tiffOrientation) {
|
||||||
|
for (Orientation orientation : values()) {
|
||||||
|
if (orientation.value() == tiffOrientation) {
|
||||||
|
return orientation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No other TIFF orientations possible, default to Normal
|
||||||
|
return Normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -45,11 +45,9 @@ import javax.imageio.stream.ImageInputStream;
|
|||||||
import javax.imageio.stream.ImageOutputStream;
|
import javax.imageio.stream.ImageOutputStream;
|
||||||
import java.awt.geom.AffineTransform;
|
import java.awt.geom.AffineTransform;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.*;
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -202,11 +200,10 @@ public final class TIFFUtilities {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static List<TIFFPage> getPages(ImageInputStream imageInput) throws IOException {
|
public static List<TIFFPage> getPages(ImageInputStream imageInput) throws IOException {
|
||||||
ArrayList<TIFFPage> pages = new ArrayList<TIFFPage>();
|
|
||||||
|
|
||||||
CompoundDirectory IFDs = (CompoundDirectory) new TIFFReader().read(imageInput);
|
CompoundDirectory IFDs = (CompoundDirectory) new TIFFReader().read(imageInput);
|
||||||
|
|
||||||
int pageCount = IFDs.directoryCount();
|
final int pageCount = IFDs.directoryCount();
|
||||||
|
List<TIFFPage> pages = new ArrayList<>(pageCount);
|
||||||
for (int pageIndex = 0; pageIndex < pageCount; pageIndex++) {
|
for (int pageIndex = 0; pageIndex < pageCount; pageIndex++) {
|
||||||
pages.add(new TIFFPage(IFDs.getDirectory(pageIndex), imageInput));
|
pages.add(new TIFFPage(IFDs.getDirectory(pageIndex), imageInput));
|
||||||
}
|
}
|
||||||
@@ -358,13 +355,21 @@ public final class TIFFUtilities {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int compression = -1;
|
||||||
|
Entry compressionEntry = IFD.getEntryById(TIFF.TAG_COMPRESSION);
|
||||||
|
if (compressionEntry != null && compressionEntry.getValue() instanceof Number) {
|
||||||
|
compression = ((Number) compressionEntry.getValue()).shortValue();
|
||||||
|
}
|
||||||
|
|
||||||
boolean rearrangedByteStrips = false;
|
boolean rearrangedByteStrips = false;
|
||||||
Entry oldJpegData = IFD.getEntryById(TIFF.TAG_JPEG_INTERCHANGE_FORMAT);
|
Entry oldJpegData = IFD.getEntryById(TIFF.TAG_JPEG_INTERCHANGE_FORMAT);
|
||||||
Entry oldJpegDataLength = IFD.getEntryById(TIFF.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
|
Entry oldJpegDataLength = IFD.getEntryById(TIFF.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
|
||||||
|
long[] jpegByteCounts = null;
|
||||||
|
long[] jpegOffsets = null;
|
||||||
if (oldJpegData != null && oldJpegData.valueCount() > 0) {
|
if (oldJpegData != null && oldJpegData.valueCount() > 0) {
|
||||||
// convert JPEGInterchangeFormat to new-style-JPEG
|
// convert JPEGInterchangeFormat to new-style-JPEG
|
||||||
long[] jpegByteCounts = new long[0];
|
jpegByteCounts = new long[0];
|
||||||
long[] jpegOffsets = getValueAsLongArray(oldJpegData);
|
jpegOffsets = getValueAsLongArray(oldJpegData);
|
||||||
if (oldJpegDataLength != null && oldJpegDataLength.valueCount() > 0) {
|
if (oldJpegDataLength != null && oldJpegDataLength.valueCount() > 0) {
|
||||||
jpegByteCounts = getValueAsLongArray(oldJpegDataLength);
|
jpegByteCounts = getValueAsLongArray(oldJpegDataLength);
|
||||||
}
|
}
|
||||||
@@ -388,6 +393,20 @@ public final class TIFFUtilities {
|
|||||||
newIFD.remove(oldJpegDataLength);
|
newIFD.remove(oldJpegDataLength);
|
||||||
rearrangedByteStrips = true;
|
rearrangedByteStrips = true;
|
||||||
}
|
}
|
||||||
|
else if (offsets.length == 1 && oldJpegDataLength != null && (jpegOffsets[0] < offsets[0]) && (jpegOffsets[0] + jpegByteCounts[0]) > (offsets[0] + byteCounts[0])) {
|
||||||
|
|
||||||
|
// ByteStrip contains only a part of JPEGInterchangeFormat
|
||||||
|
newOffsets = writeData(jpegOffsets, jpegByteCounts, outputStream);
|
||||||
|
|
||||||
|
newIFD.remove(stripOffsetsEntry);
|
||||||
|
newIFD.add(new TIFFEntry(useTiles ? TIFF.TAG_TILE_OFFSETS : TIFF.TAG_STRIP_OFFSETS, newOffsets));
|
||||||
|
newIFD.remove(stripByteCountsEntry);
|
||||||
|
newIFD.add(new TIFFEntry(useTiles ? TIFF.TAG_TILE_BYTE_COUNTS : TIFF.TAG_STRIP_BYTE_COUNTS, new int[]{(int) (jpegByteCounts[0])}));
|
||||||
|
|
||||||
|
newIFD.remove(oldJpegData);
|
||||||
|
newIFD.remove(oldJpegDataLength);
|
||||||
|
rearrangedByteStrips = true;
|
||||||
|
}
|
||||||
else if (oldJpegDataLength != null) {
|
else if (oldJpegDataLength != null) {
|
||||||
// multiple bytestrips
|
// multiple bytestrips
|
||||||
// search for SOF on first strip and copy to each if needed
|
// search for SOF on first strip and copy to each if needed
|
||||||
@@ -423,7 +442,7 @@ public final class TIFFUtilities {
|
|||||||
byte[] buffer = new byte[(int) byteCounts[i]];
|
byte[] buffer = new byte[(int) byteCounts[i]];
|
||||||
newByteCounts[i] = (int) (jpegInterchangeData.length + byteCounts[i]);
|
newByteCounts[i] = (int) (jpegInterchangeData.length + byteCounts[i]);
|
||||||
stream.readFully(buffer);
|
stream.readFully(buffer);
|
||||||
if (buffer[0] != 0xff && buffer[1] != 0xda) {
|
if (buffer[0] != ((byte) 0xff) || buffer[1] != ((byte) 0xda)) {
|
||||||
outputStream.write(sosMarker);
|
outputStream.write(sosMarker);
|
||||||
newByteCounts[i] += sosMarker.length;
|
newByteCounts[i] += sosMarker.length;
|
||||||
}
|
}
|
||||||
@@ -440,7 +459,58 @@ public final class TIFFUtilities {
|
|||||||
rearrangedByteStrips = true;
|
rearrangedByteStrips = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (compression == TIFFExtension.COMPRESSION_OLD_JPEG) {
|
||||||
|
// old-style but no JPEGInterchangeFormat
|
||||||
|
|
||||||
|
long[] yCbCrSubSampling = getValueAsLongArray(IFD.getEntryById(TIFF.TAG_YCBCR_SUB_SAMPLING));
|
||||||
|
int subsampling = yCbCrSubSampling != null
|
||||||
|
? (int) ((yCbCrSubSampling[0] & 0xf) << 4 | yCbCrSubSampling[1] & 0xf)
|
||||||
|
: 0x22;
|
||||||
|
int bands = ((Number) IFD.getEntryById(TIFF.TAG_SAMPLES_PER_PIXEL).getValue()).intValue();
|
||||||
|
|
||||||
|
int w = ((Number) IFD.getEntryById(TIFF.TAG_IMAGE_WIDTH).getValue()).intValue();
|
||||||
|
int h = ((Number) IFD.getEntryById(TIFF.TAG_IMAGE_HEIGHT).getValue()).intValue();
|
||||||
|
|
||||||
|
int r = ((Number) (useTiles ? IFD.getEntryById(TIFF.TAG_TILE_HEIGTH) : IFD.getEntryById(TIFF.TAG_ROWS_PER_STRIP)).getValue()).intValue();
|
||||||
|
int c = useTiles ? ((Number) IFD.getEntryById(TIFF.TAG_TILE_WIDTH).getValue()).intValue() : w;
|
||||||
|
|
||||||
|
newOffsets = new int[offsets.length];
|
||||||
|
int[] newByteCounts = new int[byteCounts.length];
|
||||||
|
|
||||||
|
// No JPEGInterchangeFormat
|
||||||
|
for (int i = 0; i < offsets.length; i++) {
|
||||||
|
byte[] start = new byte[2];
|
||||||
|
stream.seek(offsets[i]);
|
||||||
|
stream.readFully(start);
|
||||||
|
newOffsets[i] = (int) outputStream.getStreamPosition();
|
||||||
|
if (start[0] == ((byte) 0xff) && start[1] == ((byte) 0xd8)) {
|
||||||
|
// full image stream, nothing to do
|
||||||
|
writeData(stream, outputStream, offsets[i], byteCounts[i]);
|
||||||
|
}
|
||||||
|
else if (start[0] == ((byte) 0xff) && start[1] == ((byte) 0xda)) {
|
||||||
|
// starts with SOS
|
||||||
|
outputStream.writeShort(JPEG.SOI);
|
||||||
|
writeSOF0(outputStream, bands, c, r, subsampling);
|
||||||
|
writeData(stream, outputStream, offsets[i], byteCounts[i]);
|
||||||
|
outputStream.writeShort(JPEG.EOI);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// raw data
|
||||||
|
outputStream.writeShort(JPEG.SOI);
|
||||||
|
writeSOF0(outputStream, bands, c, r, subsampling);
|
||||||
|
writeSOS(outputStream, bands);
|
||||||
|
writeData(stream, outputStream, offsets[i], byteCounts[i]);
|
||||||
|
outputStream.writeShort(JPEG.EOI);
|
||||||
|
}
|
||||||
|
newByteCounts[i] = ((int) outputStream.getStreamPosition()) - newOffsets[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
newIFD.remove(stripOffsetsEntry);
|
||||||
|
newIFD.add(new TIFFEntry(useTiles ? TIFF.TAG_TILE_OFFSETS : TIFF.TAG_STRIP_OFFSETS, newOffsets));
|
||||||
|
newIFD.remove(stripByteCountsEntry);
|
||||||
|
newIFD.add(new TIFFEntry(useTiles ? TIFF.TAG_TILE_BYTE_COUNTS : TIFF.TAG_STRIP_BYTE_COUNTS, newByteCounts));
|
||||||
|
rearrangedByteStrips = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (!rearrangedByteStrips && stripOffsetsEntry != null && stripByteCountsEntry != null) {
|
if (!rearrangedByteStrips && stripOffsetsEntry != null && stripByteCountsEntry != null) {
|
||||||
newOffsets = writeData(offsets, byteCounts, outputStream);
|
newOffsets = writeData(offsets, byteCounts, outputStream);
|
||||||
@@ -457,12 +527,21 @@ public final class TIFFUtilities {
|
|||||||
oldJpegTableQ = IFD.getEntryById(TIFF.TAG_OLD_JPEG_Q_TABLES);
|
oldJpegTableQ = IFD.getEntryById(TIFF.TAG_OLD_JPEG_Q_TABLES);
|
||||||
oldJpegTableDC = IFD.getEntryById(TIFF.TAG_OLD_JPEG_DC_TABLES);
|
oldJpegTableDC = IFD.getEntryById(TIFF.TAG_OLD_JPEG_DC_TABLES);
|
||||||
oldJpegTableAC = IFD.getEntryById(TIFF.TAG_OLD_JPEG_AC_TABLES);
|
oldJpegTableAC = IFD.getEntryById(TIFF.TAG_OLD_JPEG_AC_TABLES);
|
||||||
if (oldJpegTableQ != null || oldJpegTableDC != null || oldJpegTableAC != null) {
|
if ((oldJpegTableQ != null) || (oldJpegTableDC != null) || (oldJpegTableAC != null)) {
|
||||||
if (IFD.getEntryById(TIFF.TAG_JPEG_TABLES) != null) {
|
if (IFD.getEntryById(TIFF.TAG_JPEG_TABLES) != null) {
|
||||||
throw new IOException("Found old-style and new-style JPEGTables");
|
throw new IOException("Found old-style and new-style JPEGTables");
|
||||||
}
|
}
|
||||||
|
|
||||||
newIFD.add(mergeTables(oldJpegTableQ, oldJpegTableDC, oldJpegTableAC));
|
boolean tablesInStream = jfifContainsTables(oldJpegTableQ, jpegOffsets, jpegByteCounts);
|
||||||
|
tablesInStream &= jfifContainsTables(oldJpegTableDC, jpegOffsets, jpegByteCounts);
|
||||||
|
tablesInStream &= jfifContainsTables(oldJpegTableAC, jpegOffsets, jpegByteCounts);
|
||||||
|
if (!tablesInStream) {
|
||||||
|
// merge them only to JPEGTables if they are not already contained within the stream
|
||||||
|
Entry jpegTables = mergeTables(oldJpegTableQ, oldJpegTableDC, oldJpegTableAC);
|
||||||
|
if (jpegTables != null) {
|
||||||
|
newIFD.add(jpegTables);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (oldJpegTableQ != null) {
|
if (oldJpegTableQ != null) {
|
||||||
newIFD.remove(oldJpegTableQ);
|
newIFD.remove(oldJpegTableQ);
|
||||||
}
|
}
|
||||||
@@ -474,17 +553,66 @@ public final class TIFFUtilities {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Entry compressionEntry = IFD.getEntryById(TIFF.TAG_COMPRESSION);
|
if (compressionEntry != null && compression == TIFFExtension.COMPRESSION_OLD_JPEG) {
|
||||||
if(compressionEntry != null) {
|
newIFD.remove(compressionEntry);
|
||||||
Number compression = (Number) compressionEntry.getValue();
|
newIFD.add(new TIFFEntry(TIFF.TAG_COMPRESSION, TIFF.TYPE_SHORT, TIFFExtension.COMPRESSION_JPEG));
|
||||||
if (compression.shortValue() == TIFFExtension.COMPRESSION_OLD_JPEG) {
|
|
||||||
newIFD.remove(compressionEntry);
|
|
||||||
newIFD.add(new TIFFEntry(TIFF.TAG_COMPRESSION, TIFF.TYPE_SHORT, TIFFExtension.COMPRESSION_JPEG));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return newIFD;
|
return newIFD;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO merge/extract from TIFFReader Jpeg/6 stream reconstruction
|
||||||
|
private void writeSOF0(ImageOutputStream outputStream, int bands, int width, int height, int subsampling) throws IOException {
|
||||||
|
outputStream.writeShort(JPEG.SOF0); // TODO: Use correct process for data
|
||||||
|
outputStream.writeShort(2 + 6 + 3 * bands); // SOF0 len
|
||||||
|
outputStream.writeByte(8); // bits TODO: Consult raster/transfer type or BitsPerSample for 12/16 bits support
|
||||||
|
outputStream.writeShort(height); // height
|
||||||
|
outputStream.writeShort(width); // width
|
||||||
|
outputStream.writeByte(bands); // Number of components
|
||||||
|
|
||||||
|
for (int comp = 0; comp < bands; comp++) {
|
||||||
|
outputStream.writeByte(comp); // Component id
|
||||||
|
outputStream.writeByte(comp == 0 ? subsampling : 0x11); // h/v subsampling
|
||||||
|
outputStream.writeByte(comp); // Q table selector TODO: Consider merging if tables are equal, correct selection if only 1 or 2 valid tables are contained
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO merge/extract from TIFFReader Jpeg/6 stream reconstruction
|
||||||
|
private void writeSOS(ImageOutputStream outputStream, int bands) throws IOException {
|
||||||
|
outputStream.writeShort(JPEG.SOS);
|
||||||
|
outputStream.writeShort(6 + 2 * bands); // SOS length
|
||||||
|
outputStream.writeByte(bands); // Num comp
|
||||||
|
|
||||||
|
for (int component = 0; component < bands; component++) {
|
||||||
|
outputStream.writeByte(component); // Comp id
|
||||||
|
outputStream.writeByte(component == 0 ? component : 0x10 + (component & 0xf)); // dc/ac selector TODO: correct selection if only 1 or 2 valid tables are contained
|
||||||
|
}
|
||||||
|
|
||||||
|
outputStream.writeByte(0); // Spectral selection start
|
||||||
|
outputStream.writeByte(0); // Spectral selection end
|
||||||
|
outputStream.writeByte(0); // Approx high & low
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeData(ImageInputStream input, ImageOutputStream output, long offset, long length) throws IOException {
|
||||||
|
input.seek(offset);
|
||||||
|
byte[] buffer = new byte[(int) length];
|
||||||
|
stream.readFully(buffer);
|
||||||
|
output.write(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean jfifContainsTables(Entry tableEntry, long[] jpegOffsets, long[] jpegLengths) throws IOException {
|
||||||
|
if (jpegLengths == null || jpegOffsets == null || jpegLengths.length == 0) return false;
|
||||||
|
if (tableEntry != null) {
|
||||||
|
long[] tableOffsets = getValueAsLongArray(tableEntry);
|
||||||
|
for (long offset : tableOffsets) {
|
||||||
|
if (offset < jpegOffsets[0] || offset > (jpegOffsets[0] + jpegLengths[0])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO merge/extract from TIFFReader Jpeg/6 stream reconstruction
|
||||||
private Entry mergeTables(Entry qEntry, Entry dcEntry, Entry acEntry) throws IOException {
|
private Entry mergeTables(Entry qEntry, Entry dcEntry, Entry acEntry) throws IOException {
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
DataOutputStream dos = new DataOutputStream(bos);
|
DataOutputStream dos = new DataOutputStream(bos);
|
||||||
@@ -494,12 +622,16 @@ public final class TIFFUtilities {
|
|||||||
long[] off = getValueAsLongArray(qEntry);
|
long[] off = getValueAsLongArray(qEntry);
|
||||||
byte[] table = new byte[64];
|
byte[] table = new byte[64];
|
||||||
for (int tableId = 0; tableId < off.length; tableId++) {
|
for (int tableId = 0; tableId < off.length; tableId++) {
|
||||||
stream.seek(off[tableId]);
|
try {
|
||||||
stream.readFully(table);
|
stream.seek(off[tableId]);
|
||||||
dos.writeShort(JPEG.DQT);
|
stream.readFully(table);
|
||||||
dos.writeShort(3 + 64);
|
dos.writeShort(JPEG.DQT);
|
||||||
dos.writeByte(tableId);
|
dos.writeShort(3 + 64);
|
||||||
dos.write(table);
|
dos.writeByte(tableId);
|
||||||
|
dos.write(table);
|
||||||
|
} catch (EOFException e) {
|
||||||
|
// invalid table pointer, ignore
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -507,30 +639,50 @@ public final class TIFFUtilities {
|
|||||||
if (dcEntry != null && dcEntry.valueCount() > 0) {
|
if (dcEntry != null && dcEntry.valueCount() > 0) {
|
||||||
long[] off = getValueAsLongArray(dcEntry);
|
long[] off = getValueAsLongArray(dcEntry);
|
||||||
for (int tableId = 0; tableId < off.length; tableId++) {
|
for (int tableId = 0; tableId < off.length; tableId++) {
|
||||||
stream.seek(off[tableId]);
|
try {
|
||||||
byte[] table = readHUFFTable();
|
stream.seek(off[tableId]);
|
||||||
dos.writeShort(JPEG.DHT);
|
byte[] table = readHUFFTable();
|
||||||
dos.writeShort(3 + table.length);
|
if (table.length > (16 + 17)) {
|
||||||
dos.writeByte(tableId);
|
// to long, table is invalid, just ignoe
|
||||||
dos.write(table);
|
continue;
|
||||||
|
}
|
||||||
|
dos.writeShort(JPEG.DHT);
|
||||||
|
dos.writeShort(3 + table.length);
|
||||||
|
dos.writeByte(tableId);
|
||||||
|
dos.write(table);
|
||||||
|
} catch (EOFException e) {
|
||||||
|
// invalid table pointer, ignore
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (acEntry != null && acEntry.valueCount() > 0) {
|
if (acEntry != null && acEntry.valueCount() > 0) {
|
||||||
long[] off = getValueAsLongArray(acEntry);
|
long[] off = getValueAsLongArray(acEntry);
|
||||||
for (int tableId = 0; tableId < off.length; tableId++) {
|
for (int tableId = 0; tableId < off.length; tableId++) {
|
||||||
stream.seek(off[tableId]);
|
try {
|
||||||
byte[] table = readHUFFTable();
|
stream.seek(off[tableId]);
|
||||||
dos.writeShort(JPEG.DHT);
|
byte[] table = readHUFFTable();
|
||||||
dos.writeShort(3 + table.length);
|
if (table.length > (16 + 256)) {
|
||||||
dos.writeByte(16 | tableId);
|
// to long, table is invalid, just ignoe
|
||||||
dos.write(table);
|
continue;
|
||||||
|
}
|
||||||
|
dos.writeShort(JPEG.DHT);
|
||||||
|
dos.writeShort(3 + table.length);
|
||||||
|
dos.writeByte(16 | tableId);
|
||||||
|
dos.write(table);
|
||||||
|
} catch (EOFException e) {
|
||||||
|
// invalid table pointer, ignore
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dos.writeShort(JPEG.EOI);
|
dos.writeShort(JPEG.EOI);
|
||||||
|
|
||||||
bos.close();
|
bos.close();
|
||||||
|
if (bos.size() == 4) {
|
||||||
|
// no valid tables, don't add
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return new TIFFEntry(TIFF.TAG_JPEG_TABLES, TIFF.TYPE_UNDEFINED, bos.toByteArray());
|
return new TIFFEntry(TIFF.TAG_JPEG_TABLES, TIFF.TYPE_UNDEFINED, bos.toByteArray());
|
||||||
}
|
}
|
||||||
@@ -540,15 +692,11 @@ public final class TIFFUtilities {
|
|||||||
stream.readFully(lengths);
|
stream.readFully(lengths);
|
||||||
int numCodes = 0;
|
int numCodes = 0;
|
||||||
for (int i = 0; i < lengths.length; i++) {
|
for (int i = 0; i < lengths.length; i++) {
|
||||||
numCodes += lengths[i];
|
numCodes += ((int) lengths[i]) & 0xff;
|
||||||
}
|
}
|
||||||
byte table[] = new byte[16 + numCodes];
|
byte table[] = new byte[16 + numCodes];
|
||||||
System.arraycopy(lengths, 0, table, 0, 16);
|
System.arraycopy(lengths, 0, table, 0, 16);
|
||||||
int off = 16;
|
stream.readFully(table, 16, numCodes);
|
||||||
for (int i = 0; i < lengths.length; i++) {
|
|
||||||
stream.read(table, off, lengths[i]);
|
|
||||||
off += lengths[i];
|
|
||||||
}
|
|
||||||
return table;
|
return table;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -559,7 +707,11 @@ public final class TIFFUtilities {
|
|||||||
stream.seek(offsets[i]);
|
stream.seek(offsets[i]);
|
||||||
|
|
||||||
byte[] buffer = new byte[(int) byteCounts[i]];
|
byte[] buffer = new byte[(int) byteCounts[i]];
|
||||||
stream.readFully(buffer);
|
try {
|
||||||
|
stream.readFully(buffer);
|
||||||
|
} catch (EOFException e) {
|
||||||
|
// invalid strip length
|
||||||
|
}
|
||||||
outputStream.write(buffer);
|
outputStream.write(buffer);
|
||||||
}
|
}
|
||||||
return newOffsets;
|
return newOffsets;
|
||||||
@@ -571,7 +723,7 @@ public final class TIFFUtilities {
|
|||||||
|
|
||||||
if (entry.valueCount() == 1) {
|
if (entry.valueCount() == 1) {
|
||||||
// For single entries, this will be a boxed type
|
// For single entries, this will be a boxed type
|
||||||
value = new long[] {((Number) entry.getValue()).longValue()};
|
value = new long[]{((Number) entry.getValue()).longValue()};
|
||||||
}
|
}
|
||||||
else if (entry.getValue() instanceof short[]) {
|
else if (entry.getValue() instanceof short[]) {
|
||||||
short[] shorts = (short[]) entry.getValue();
|
short[] shorts = (short[]) entry.getValue();
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
package com.twelvemonkeys.contrib.exif;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static com.twelvemonkeys.contrib.exif.Orientation.*;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OrientationTest.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
|
* @author last modified by : harald.kuhr$
|
||||||
|
* @version : OrientationTest.java,v 1.0 10/07/2020 harald.kuhr Exp$
|
||||||
|
*/
|
||||||
|
public class OrientationTest {
|
||||||
|
@Test
|
||||||
|
public void testFromMetadataOrientationNull() {
|
||||||
|
assertEquals(Normal, Orientation.fromMetadataOrientation(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFromMetadataOrientation() {
|
||||||
|
assertEquals(Normal, Orientation.fromMetadataOrientation("Normal"));
|
||||||
|
assertEquals(Rotate90, Orientation.fromMetadataOrientation("Rotate90"));
|
||||||
|
assertEquals(Rotate180, Orientation.fromMetadataOrientation("Rotate180"));
|
||||||
|
assertEquals(Rotate270, Orientation.fromMetadataOrientation("Rotate270"));
|
||||||
|
assertEquals(FlipH, Orientation.fromMetadataOrientation("FlipH"));
|
||||||
|
assertEquals(FlipV, Orientation.fromMetadataOrientation("FlipV"));
|
||||||
|
assertEquals(FlipHRotate90, Orientation.fromMetadataOrientation("FlipHRotate90"));
|
||||||
|
assertEquals(FlipVRotate90, Orientation.fromMetadataOrientation("FlipVRotate90"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFromMetadataOrientationIgnoreCase() {
|
||||||
|
assertEquals(Normal, Orientation.fromMetadataOrientation("normal"));
|
||||||
|
assertEquals(Rotate90, Orientation.fromMetadataOrientation("rotate90"));
|
||||||
|
assertEquals(Rotate180, Orientation.fromMetadataOrientation("ROTATE180"));
|
||||||
|
assertEquals(Rotate270, Orientation.fromMetadataOrientation("ROTATE270"));
|
||||||
|
assertEquals(FlipH, Orientation.fromMetadataOrientation("FLIPH"));
|
||||||
|
assertEquals(FlipV, Orientation.fromMetadataOrientation("flipv"));
|
||||||
|
assertEquals(FlipHRotate90, Orientation.fromMetadataOrientation("FLIPhrotate90"));
|
||||||
|
assertEquals(FlipVRotate90, Orientation.fromMetadataOrientation("fLiPVRotAte90"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFromMetadataOrientationUnknown() {
|
||||||
|
assertEquals(Normal, Orientation.fromMetadataOrientation("foo"));
|
||||||
|
assertEquals(Normal, Orientation.fromMetadataOrientation("90"));
|
||||||
|
assertEquals(Normal, Orientation.fromMetadataOrientation("randomStringWithNumbers180"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFromTIFFOrientation() {
|
||||||
|
assertEquals(Normal, Orientation.fromTIFFOrientation(1));
|
||||||
|
assertEquals(FlipH, Orientation.fromTIFFOrientation(2));
|
||||||
|
assertEquals(Rotate180, Orientation.fromTIFFOrientation(3));
|
||||||
|
assertEquals(FlipV, Orientation.fromTIFFOrientation(4));
|
||||||
|
assertEquals(FlipVRotate90, Orientation.fromTIFFOrientation(5));
|
||||||
|
assertEquals(Rotate270, Orientation.fromTIFFOrientation(6));
|
||||||
|
assertEquals(FlipHRotate90, Orientation.fromTIFFOrientation(7));
|
||||||
|
assertEquals(Rotate90, Orientation.fromTIFFOrientation(8));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFromTIFFOrientationUnknown() {
|
||||||
|
assertEquals(Normal, Orientation.fromTIFFOrientation(-1));
|
||||||
|
assertEquals(Normal, Orientation.fromTIFFOrientation(0));
|
||||||
|
assertEquals(Normal, Orientation.fromTIFFOrientation(9));
|
||||||
|
for (int i = 10; i < 1024; i++) {
|
||||||
|
assertEquals(Normal, Orientation.fromTIFFOrientation(i));
|
||||||
|
}
|
||||||
|
assertEquals(Normal, Orientation.fromTIFFOrientation(Integer.MAX_VALUE));
|
||||||
|
assertEquals(Normal, Orientation.fromTIFFOrientation(Integer.MIN_VALUE));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -202,36 +202,39 @@ public class TIFFUtilitiesTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMergeBogusInterchangeFormatLength() throws IOException {
|
public void testOldStyleJPEGTransform() throws IOException {
|
||||||
String[] testFiles = new String[] {
|
String[] testFiles = new String[]{
|
||||||
"/tiff/old-style-jpeg-bogus-jpeginterchangeformatlength.tif", // InterchangeFormat before StripOffset, length not including StripOffset
|
"/tiff/old-style-jpeg-bogus-jpeginterchangeformatlength.tif", // InterchangeFormat before StripOffset, length not including StripOffset
|
||||||
"/tiff/old-style-jpeg-no-jpeginterchangeformatlength.tif", // missing JPEGInterChangeFormatLength and JPEGInterchangeFormat == StipOffset
|
"/tiff/old-style-jpeg-no-jpeginterchangeformatlength.tif", // missing JPEGInterChangeFormatLength and JPEGInterchangeFormat == StipOffset
|
||||||
"/tiff/old-style-jpeg-multiple-strips.tif" // InterchangeFormat with multiple strips
|
"/tiff/old-style-jpeg-multiple-strips.tif", // InterchangeFormat with multiple strips
|
||||||
|
"/contrib/tiff/old-style-jpeg-invalid-tables.tif", // AC/DC Tables are invalid (to long) and lie within the JPEGInterchangeFormat stream
|
||||||
|
"/contrib/tiff/smallliz.tif", // InterchangeFormat contains whole JPEG, ByteStrip only raw ImageData after SOS
|
||||||
|
"/contrib/tiff/WangJPEG.tif", // multiple strips, first strip contains SOS
|
||||||
|
"/contrib/tiff/zackthecat.tif" // No JPEGInterchangeFormat, ByteStrip contains only raw image data
|
||||||
};
|
};
|
||||||
|
|
||||||
for (String testFile : testFiles) {
|
for (String testFile : testFiles) {
|
||||||
File output = File.createTempFile("imageiotest", ".tif");
|
try {
|
||||||
ImageOutputStream outputStream = ImageIO.createImageOutputStream(output);
|
File output = File.createTempFile("imageiotest", ".tif");
|
||||||
InputStream inputStream1 = getClassLoaderResource(testFile).openStream();
|
ImageOutputStream outputStream = ImageIO.createImageOutputStream(output);
|
||||||
ImageInputStream imageInput1 = ImageIO.createImageInputStream(inputStream1);
|
InputStream inputStream = getClassLoaderResource(testFile).openStream();
|
||||||
InputStream inputStream2 = getClassLoaderResource(testFile).openStream();
|
ImageInputStream imageInput = ImageIO.createImageInputStream(inputStream);
|
||||||
ImageInputStream imageInput2 = ImageIO.createImageInputStream(inputStream2);
|
List<TIFFUtilities.TIFFPage> pages = TIFFUtilities.getPages(imageInput);
|
||||||
ArrayList<TIFFUtilities.TIFFPage> pages = new ArrayList<>();
|
TIFFUtilities.writePages(outputStream, pages);
|
||||||
pages.addAll(TIFFUtilities.getPages(imageInput1));
|
|
||||||
pages.addAll(TIFFUtilities.getPages(imageInput2));
|
|
||||||
TIFFUtilities.writePages(outputStream, pages);
|
|
||||||
|
|
||||||
ImageInputStream testOutput = ImageIO.createImageInputStream(output);
|
ImageInputStream testOutput = ImageIO.createImageInputStream(output);
|
||||||
ImageReader reader = ImageIO.getImageReaders(testOutput).next();
|
ImageReader reader = ImageIO.getImageReaders(testOutput).next();
|
||||||
reader.setInput(testOutput);
|
reader.setInput(testOutput);
|
||||||
int numImages = reader.getNumImages(true);
|
int numImages = reader.getNumImages(true);
|
||||||
for (int i = 0; i < numImages; i++) {
|
for (int i = 0; i < numImages; i++) {
|
||||||
reader.read(i);
|
reader.read(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
imageInput.close();
|
||||||
|
outputStream.close();
|
||||||
|
} catch (Exception exc) {
|
||||||
|
throw new IOException(testFile, exc);
|
||||||
}
|
}
|
||||||
|
|
||||||
imageInput1.close();
|
|
||||||
imageInput2.close();
|
|
||||||
outputStream.close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.4.4-SNAPSHOT</version>
|
<version>3.6.5-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-batik</artifactId>
|
<artifactId>imageio-batik</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: Batik Plugin</name>
|
<name>TwelveMonkeys :: ImageIO :: Batik Plugin</name>
|
||||||
@@ -14,6 +14,21 @@
|
|||||||
See the <a href="http://xmlgraphics.apache.org/batik/">Batik Home page</a>
|
See the <a href="http://xmlgraphics.apache.org/batik/">Batik Home page</a>
|
||||||
for more information.]]>
|
for more information.]]>
|
||||||
</description>
|
</description>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<systemPropertyVariables>
|
||||||
|
<com.twelvemonkeys.imageio.plugins.svg.allowexternalresources>
|
||||||
|
true
|
||||||
|
</com.twelvemonkeys.imageio.plugins.svg.allowexternalresources>
|
||||||
|
</systemPropertyVariables>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -24,6 +39,7 @@
|
|||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio-core</artifactId>
|
<artifactId>imageio-core</artifactId>
|
||||||
<type>test-jar</type>
|
<type>test-jar</type>
|
||||||
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -88,6 +104,6 @@
|
|||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<batik.version>1.9</batik.version>
|
<batik.version>1.14</batik.version>
|
||||||
</properties>
|
</properties>
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
+63
-14
@@ -33,9 +33,11 @@ package com.twelvemonkeys.imageio.plugins.svg;
|
|||||||
import com.twelvemonkeys.image.ImageUtil;
|
import com.twelvemonkeys.image.ImageUtil;
|
||||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||||
|
import com.twelvemonkeys.lang.StringUtil;
|
||||||
import org.apache.batik.anim.dom.SVGDOMImplementation;
|
import org.apache.batik.anim.dom.SVGDOMImplementation;
|
||||||
import org.apache.batik.anim.dom.SVGOMDocument;
|
import org.apache.batik.anim.dom.SVGOMDocument;
|
||||||
import org.apache.batik.bridge.*;
|
import org.apache.batik.bridge.*;
|
||||||
|
import org.apache.batik.css.parser.CSSLexicalUnit;
|
||||||
import org.apache.batik.dom.util.DOMUtilities;
|
import org.apache.batik.dom.util.DOMUtilities;
|
||||||
import org.apache.batik.ext.awt.image.GraphicsUtil;
|
import org.apache.batik.ext.awt.image.GraphicsUtil;
|
||||||
import org.apache.batik.gvt.CanvasGraphicsNode;
|
import org.apache.batik.gvt.CanvasGraphicsNode;
|
||||||
@@ -47,6 +49,7 @@ import org.apache.batik.transcoder.*;
|
|||||||
import org.apache.batik.transcoder.image.ImageTranscoder;
|
import org.apache.batik.transcoder.image.ImageTranscoder;
|
||||||
import org.apache.batik.util.ParsedURL;
|
import org.apache.batik.util.ParsedURL;
|
||||||
import org.apache.batik.util.SVGConstants;
|
import org.apache.batik.util.SVGConstants;
|
||||||
|
import org.apache.batik.xml.LexicalUnits;
|
||||||
import org.w3c.dom.DOMImplementation;
|
import org.w3c.dom.DOMImplementation;
|
||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
import org.w3c.dom.svg.SVGSVGElement;
|
import org.w3c.dom.svg.SVGSVGElement;
|
||||||
@@ -76,7 +79,12 @@ import java.util.Map;
|
|||||||
* @see <A href="http://www.mail-archive.com/batik-dev@xml.apache.org/msg00992.html">batik-dev</A>
|
* @see <A href="http://www.mail-archive.com/batik-dev@xml.apache.org/msg00992.html">batik-dev</A>
|
||||||
*/
|
*/
|
||||||
public class SVGImageReader extends ImageReaderBase {
|
public class SVGImageReader extends ImageReaderBase {
|
||||||
|
|
||||||
|
final static boolean DEFAULT_ALLOW_EXTERNAL_RESOURCES =
|
||||||
|
"true".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.imageio.plugins.svg.allowexternalresources"));
|
||||||
|
|
||||||
private Rasterizer rasterizer;
|
private Rasterizer rasterizer;
|
||||||
|
private boolean allowExternalResources = DEFAULT_ALLOW_EXTERNAL_RESOURCES;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an {@code SVGImageReader}.
|
* Creates an {@code SVGImageReader}.
|
||||||
@@ -113,6 +121,9 @@ public class SVGImageReader extends ImageReaderBase {
|
|||||||
if (pParam instanceof SVGReadParam) {
|
if (pParam instanceof SVGReadParam) {
|
||||||
SVGReadParam svgParam = (SVGReadParam) pParam;
|
SVGReadParam svgParam = (SVGReadParam) pParam;
|
||||||
|
|
||||||
|
// set the external-resource-resolution preference
|
||||||
|
allowExternalResources = svgParam.isAllowExternalResources();
|
||||||
|
|
||||||
// Get the base URI
|
// Get the base URI
|
||||||
// This must be done before converting the params to hints
|
// This must be done before converting the params to hints
|
||||||
String baseURI = svgParam.getBaseURI();
|
String baseURI = svgParam.getBaseURI();
|
||||||
@@ -324,24 +335,54 @@ public class SVGImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ----
|
// ----
|
||||||
|
SVGSVGElement rootElement = svgDoc.getRootElement();
|
||||||
|
|
||||||
// get the 'width' and 'height' attributes of the SVG document
|
// get the 'width' and 'height' attributes of the SVG document
|
||||||
Dimension2D docSize = ctx.getDocumentSize();
|
UnitProcessor.Context uctx
|
||||||
if (docSize != null) {
|
= UnitProcessor.createContext(ctx, rootElement);
|
||||||
defaultWidth = (float) docSize.getWidth();
|
String widthStr = rootElement.getAttributeNS(null, SVGConstants.SVG_WIDTH_ATTRIBUTE);
|
||||||
defaultHeight = (float) docSize.getHeight();
|
String heightStr = rootElement.getAttributeNS(null, SVGConstants.SVG_HEIGHT_ATTRIBUTE);
|
||||||
|
if (!StringUtil.isEmpty(widthStr)) {
|
||||||
|
defaultWidth = UnitProcessor.svgToUserSpace(widthStr, SVGConstants.SVG_WIDTH_ATTRIBUTE, UnitProcessor.HORIZONTAL_LENGTH, uctx);
|
||||||
}
|
}
|
||||||
else {
|
if(!StringUtil.isEmpty(heightStr)){
|
||||||
defaultWidth = 200;
|
defaultHeight = UnitProcessor.svgToUserSpace(heightStr, SVGConstants.SVG_HEIGHT_ATTRIBUTE, UnitProcessor.VERTICAL_LENGTH, uctx);
|
||||||
defaultHeight = 200;
|
|
||||||
}
|
}
|
||||||
SVGSVGElement rootElement = svgDoc.getRootElement();
|
|
||||||
String viewBoxStr = rootElement.getAttributeNS
|
boolean hasWidth = defaultWidth > 0.0;
|
||||||
(null, SVGConstants.SVG_VIEW_BOX_ATTRIBUTE);
|
boolean hasHeight = defaultHeight > 0.0;
|
||||||
if (viewBoxStr.length() != 0) {
|
|
||||||
float[] rect = ViewBox.parseViewBoxAttribute(rootElement, viewBoxStr, null);
|
if (!hasWidth || !hasHeight) {
|
||||||
defaultWidth = rect[2];
|
String viewBoxStr = rootElement.getAttributeNS
|
||||||
defaultHeight = rect[3];
|
(null, SVGConstants.SVG_VIEW_BOX_ATTRIBUTE);
|
||||||
|
if (viewBoxStr.length() != 0) {
|
||||||
|
float[] rect = ViewBox.parseViewBoxAttribute(rootElement, viewBoxStr, null);
|
||||||
|
// if one dimension is given, calculate other by aspect ratio in viewBox
|
||||||
|
// or use viewBox if no dimension is given
|
||||||
|
if (hasWidth) {
|
||||||
|
defaultHeight = defaultWidth * rect[3] / rect[2];
|
||||||
|
}
|
||||||
|
else if (hasHeight) {
|
||||||
|
defaultWidth = defaultHeight * rect[2] / rect[3];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
defaultWidth = rect[2];
|
||||||
|
defaultHeight = rect[3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (hasHeight) {
|
||||||
|
defaultWidth = defaultHeight;
|
||||||
|
}
|
||||||
|
else if (hasWidth) {
|
||||||
|
defaultHeight = defaultWidth;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// fallback to batik default sizes
|
||||||
|
defaultWidth = 400;
|
||||||
|
defaultHeight = 400;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hack to work around exception above
|
// Hack to work around exception above
|
||||||
@@ -608,6 +649,14 @@ public class SVGImageReader extends ImageReaderBase {
|
|||||||
public void displayMessage(String message) {
|
public void displayMessage(String message) {
|
||||||
processWarningOccurred(message.replaceAll("[\\r\\n]+", " "));
|
processWarningOccurred(message.replaceAll("[\\r\\n]+", " "));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExternalResourceSecurity getExternalResourceSecurity(ParsedURL resourceURL, ParsedURL docURL) {
|
||||||
|
if (allowExternalResources) {
|
||||||
|
return super.getExternalResourceSecurity(resourceURL, docURL);
|
||||||
|
}
|
||||||
|
return new NoLoadExternalResourceSecurity();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+13
@@ -41,6 +41,11 @@ import java.awt.*;
|
|||||||
public class SVGReadParam extends ImageReadParam {
|
public class SVGReadParam extends ImageReadParam {
|
||||||
private Paint background;
|
private Paint background;
|
||||||
private String baseURI;
|
private String baseURI;
|
||||||
|
private boolean allowExternalResources = SVGImageReader.DEFAULT_ALLOW_EXTERNAL_RESOURCES;
|
||||||
|
|
||||||
|
public SVGReadParam() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
public Paint getBackgroundColor() {
|
public Paint getBackgroundColor() {
|
||||||
return background;
|
return background;
|
||||||
@@ -58,6 +63,14 @@ public class SVGReadParam extends ImageReadParam {
|
|||||||
baseURI = pBaseURI;
|
baseURI = pBaseURI;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setAllowExternalResources(boolean allow) {
|
||||||
|
allowExternalResources = allow;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAllowExternalResources() {
|
||||||
|
return allowExternalResources;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canSetSourceRenderSize() {
|
public boolean canSetSourceRenderSize() {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
+44
-26
@@ -31,6 +31,7 @@
|
|||||||
package com.twelvemonkeys.imageio.plugins.svg;
|
package com.twelvemonkeys.imageio.plugins.svg;
|
||||||
|
|
||||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||||
|
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@@ -44,12 +45,10 @@ import javax.imageio.stream.ImageInputStream;
|
|||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.awt.image.ImagingOpException;
|
import java.awt.image.ImagingOpException;
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.Buffer;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -67,30 +66,25 @@ import static org.mockito.Mockito.*;
|
|||||||
* @version $Id: SVGImageReaderTest.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 SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader> {
|
public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader> {
|
||||||
private SVGImageReaderSpi provider = new SVGImageReaderSpi();
|
|
||||||
|
@Override
|
||||||
|
protected ImageReaderSpi createProvider() {
|
||||||
|
return new SVGImageReaderSpi();
|
||||||
|
}
|
||||||
|
|
||||||
protected List<TestData> getTestData() {
|
protected List<TestData> getTestData() {
|
||||||
return Arrays.asList(
|
return Arrays.asList(
|
||||||
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/red-square.svg"), new Dimension(100, 100)),
|
||||||
new TestData(getClassLoaderResource("/svg/blue-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(294, 345))
|
new TestData(getClassLoaderResource("/svg/Android_robot.svg"), new Dimension(294, 345)),
|
||||||
|
new TestData(getClassLoaderResource("/svg/sizes/w50h50.svg"), new Dimension(50, 50)),
|
||||||
|
new TestData(getClassLoaderResource("/svg/sizes/w50_1to2.svg"), new Dimension(25, 50)),
|
||||||
|
new TestData(getClassLoaderResource("/svg/sizes/h50_1to2.svg"), new Dimension(50, 100)),
|
||||||
|
new TestData(getClassLoaderResource("/svg/sizes/w50noview.svg"), new Dimension(50, 50))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ImageReaderSpi createProvider() {
|
|
||||||
return provider;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected SVGImageReader createReader() {
|
|
||||||
return new SVGImageReader(createProvider());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Class<SVGImageReader> getReaderClass() {
|
|
||||||
return SVGImageReader.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected List<String> getFormatNames() {
|
protected List<String> getFormatNames() {
|
||||||
return Collections.singletonList("svg");
|
return Collections.singletonList("svg");
|
||||||
}
|
}
|
||||||
@@ -107,8 +101,6 @@ public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader>
|
|||||||
public void testScaleViewBox() throws IOException {
|
public void testScaleViewBox() throws IOException {
|
||||||
URL svgUrl = getClassLoaderResource("/svg/quadrants.svg");
|
URL svgUrl = getClassLoaderResource("/svg/quadrants.svg");
|
||||||
|
|
||||||
File tmpDir = new File(System.getProperty("java.io.tmpdir"));
|
|
||||||
|
|
||||||
SVGImageReader reader = createReader();
|
SVGImageReader reader = createReader();
|
||||||
SVGReadParam param = new SVGReadParam();
|
SVGReadParam param = new SVGReadParam();
|
||||||
|
|
||||||
@@ -156,11 +148,11 @@ public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader>
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Override
|
@Override
|
||||||
public void testReadWithSizeParam() {
|
public void testReadWithSizeParam() throws IOException {
|
||||||
try {
|
try {
|
||||||
super.testReadWithSizeParam();
|
super.testReadWithSizeParam();
|
||||||
}
|
}
|
||||||
catch (AssertionError failure) {
|
catch (AssertionError | IOException failure) {
|
||||||
Throwable cause = failure;
|
Throwable cause = failure;
|
||||||
|
|
||||||
while (cause.getCause() != null) {
|
while (cause.getCause() != null) {
|
||||||
@@ -224,6 +216,7 @@ public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader>
|
|||||||
reader.addIIOReadWarningListener(listener);
|
reader.addIIOReadWarningListener(listener);
|
||||||
|
|
||||||
SVGReadParam param = reader.getDefaultReadParam();
|
SVGReadParam param = reader.getDefaultReadParam();
|
||||||
|
param.setAllowExternalResources(true);
|
||||||
param.setBaseURI(resource.toURI().toASCIIString());
|
param.setBaseURI(resource.toURI().toASCIIString());
|
||||||
BufferedImage image = reader.read(0, param);
|
BufferedImage image = reader.read(0, param);
|
||||||
|
|
||||||
@@ -244,6 +237,8 @@ public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader>
|
|||||||
// Asking for metadata, width, height etc, before attempting to read using a param,
|
// Asking for metadata, width, height etc, before attempting to read using a param,
|
||||||
// will cause the document to be parsed without a base URI.
|
// will cause the document to be parsed without a base URI.
|
||||||
// This will work, but may not use the CSS...
|
// This will work, but may not use the CSS...
|
||||||
|
// since the param is not available before the read operation is invoked,
|
||||||
|
// this test-case MUST use the system-property for backwards compatibility
|
||||||
URL resource = getClassLoaderResource("/svg/barChart.svg");
|
URL resource = getClassLoaderResource("/svg/barChart.svg");
|
||||||
|
|
||||||
SVGImageReader reader = createReader();
|
SVGImageReader reader = createReader();
|
||||||
@@ -282,18 +277,17 @@ public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader>
|
|||||||
public void testEmbeddedNoBaseURI() throws IOException {
|
public void testEmbeddedNoBaseURI() throws IOException {
|
||||||
// With no base URI, we will throw an exception, about the missing embedded resource
|
// With no base URI, we will throw an exception, about the missing embedded resource
|
||||||
URL resource = getClassLoaderResource("/svg/barChart.svg");
|
URL resource = getClassLoaderResource("/svg/barChart.svg");
|
||||||
|
|
||||||
SVGImageReader reader = createReader();
|
SVGImageReader reader = createReader();
|
||||||
|
|
||||||
TestData data = new TestData(resource, (Dimension) null);
|
TestData data = new TestData(resource, (Dimension) null);
|
||||||
try (ImageInputStream stream = data.getInputStream()) {
|
try (ImageInputStream stream = data.getInputStream()) {
|
||||||
reader.setInput(stream);
|
reader.setInput(stream);
|
||||||
|
|
||||||
BufferedImage image = reader.read(0);
|
SVGReadParam params = reader.getDefaultReadParam();
|
||||||
|
params.setAllowExternalResources(true);
|
||||||
|
reader.read(0, params);
|
||||||
|
|
||||||
assertNotNull(image);
|
fail("reader.read should've thrown an exception, but didn't");
|
||||||
assertEquals(450, image.getWidth());
|
|
||||||
assertEquals(500, image.getHeight());
|
|
||||||
}
|
}
|
||||||
catch (IIOException allowed) {
|
catch (IIOException allowed) {
|
||||||
assertTrue(allowed.getMessage().contains("batikLogo.svg")); // The embedded resource we don't find
|
assertTrue(allowed.getMessage().contains("batikLogo.svg")); // The embedded resource we don't find
|
||||||
@@ -302,4 +296,28 @@ public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader>
|
|||||||
reader.dispose();
|
reader.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = SecurityException.class)
|
||||||
|
public void testDisallowedExternalResources() throws URISyntaxException, IOException {
|
||||||
|
// system-property set to true in surefire-plugin-settings in the pom
|
||||||
|
URL resource = getClassLoaderResource("/svg/barChart.svg");
|
||||||
|
SVGImageReader reader = createReader();
|
||||||
|
|
||||||
|
TestData data = new TestData(resource, (Dimension) null);
|
||||||
|
try (ImageInputStream stream = data.getInputStream()) {
|
||||||
|
reader.setInput(stream);
|
||||||
|
|
||||||
|
SVGReadParam param = reader.getDefaultReadParam();
|
||||||
|
param.setBaseURI(resource.toURI().toASCIIString());
|
||||||
|
param.setAllowExternalResources(false);
|
||||||
|
// even when the system-property is set to true,
|
||||||
|
// `reader.read` for `/svg/barChart.svg` should raise
|
||||||
|
// a SecurityException when External Resources are blocked
|
||||||
|
// because the API invocation gets preference
|
||||||
|
reader.read(0, param);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
reader.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+7
-13
@@ -31,6 +31,7 @@
|
|||||||
package com.twelvemonkeys.imageio.plugins.wmf;
|
package com.twelvemonkeys.imageio.plugins.wmf;
|
||||||
|
|
||||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||||
|
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@@ -49,7 +50,10 @@ import java.util.List;
|
|||||||
* @version $Id: WMFImageReaderTest.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$
|
* @version $Id: WMFImageReaderTest.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$
|
||||||
*/
|
*/
|
||||||
public class WMFImageReaderTest extends ImageReaderAbstractTest<WMFImageReader> {
|
public class WMFImageReaderTest extends ImageReaderAbstractTest<WMFImageReader> {
|
||||||
private WMFImageReaderSpi provider = new WMFImageReaderSpi();
|
@Override
|
||||||
|
protected ImageReaderSpi createProvider() {
|
||||||
|
return new WMFImageReaderSpi();
|
||||||
|
}
|
||||||
|
|
||||||
protected List<TestData> getTestData() {
|
protected List<TestData> getTestData() {
|
||||||
return Collections.singletonList(
|
return Collections.singletonList(
|
||||||
@@ -57,27 +61,17 @@ public class WMFImageReaderTest extends ImageReaderAbstractTest<WMFImageReader>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ImageReaderSpi createProvider() {
|
|
||||||
return provider;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected WMFImageReader createReader() {
|
|
||||||
return new WMFImageReader(createProvider());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Class<WMFImageReader> getReaderClass() {
|
|
||||||
return WMFImageReader.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected List<String> getFormatNames() {
|
protected List<String> getFormatNames() {
|
||||||
return Collections.singletonList("wmf");
|
return Collections.singletonList("wmf");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
protected List<String> getSuffixes() {
|
protected List<String> getSuffixes() {
|
||||||
return Arrays.asList("wmf", "emf");
|
return Arrays.asList("wmf", "emf");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
protected List<String> getMIMETypes() {
|
protected List<String> getMIMETypes() {
|
||||||
return Arrays.asList("image/x-wmf", "application/x-msmetafile");
|
return Arrays.asList("image/x-wmf", "application/x-msmetafile");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="50" viewBox="0 0 100 200" version="1.1">
|
||||||
|
<g id="layer1">
|
||||||
|
<rect id="rect2985" width="50" height="50" 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: 427 B |
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="50" viewBox="0 0 100 200" version="1.1">
|
||||||
|
<g id="layer1">
|
||||||
|
<rect id="rect2985" width="50" height="50" 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: 428 B |
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 100 200" version="1.1">
|
||||||
|
<g id="layer1">
|
||||||
|
<rect id="rect2985" width="50" height="50" 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: 439 B |
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="50" version="1.1">
|
||||||
|
<g id="layer1">
|
||||||
|
<rect id="rect2985" width="50" height="50" 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: 405 B |
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.4.4-SNAPSHOT</version>
|
<version>3.6.5-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-bmp</artifactId>
|
<artifactId>imageio-bmp</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: BMP plugin</name>
|
<name>TwelveMonkeys :: ImageIO :: BMP plugin</name>
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio-core</artifactId>
|
<artifactId>imageio-core</artifactId>
|
||||||
<type>test-jar</type>
|
<type>test-jar</type>
|
||||||
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
+21
-19
@@ -4,26 +4,28 @@
|
|||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* 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
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
* list of conditions and the following disclaimer.
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
*
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
* and/or other materials provided with the distribution.
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
*
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
* contributors may be used to endorse or promote products derived from
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
* this software without specific prior written permission.
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||||
|
|||||||
+91
-75
@@ -4,46 +4,32 @@
|
|||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* 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
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
* list of conditions and the following disclaimer.
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
*
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
* and/or other materials provided with the distribution.
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
*
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
* contributors may be used to endorse or promote products derived from
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
* this software without specific prior written permission.
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||||
|
|
||||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
|
||||||
import com.twelvemonkeys.imageio.stream.SubImageInputStream;
|
|
||||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
|
||||||
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
|
||||||
import com.twelvemonkeys.imageio.util.ProgressListenerBase;
|
|
||||||
import com.twelvemonkeys.io.LittleEndianDataInputStream;
|
|
||||||
import com.twelvemonkeys.io.enc.DecoderStream;
|
|
||||||
import com.twelvemonkeys.xml.XMLSerializer;
|
|
||||||
|
|
||||||
import javax.imageio.*;
|
|
||||||
import javax.imageio.event.IIOReadUpdateListener;
|
|
||||||
import javax.imageio.event.IIOReadWarningListener;
|
|
||||||
import javax.imageio.metadata.IIOMetadata;
|
|
||||||
import javax.imageio.metadata.IIOMetadataFormatImpl;
|
|
||||||
import javax.imageio.spi.ImageReaderSpi;
|
|
||||||
import javax.imageio.stream.ImageInputStream;
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.color.ColorSpace;
|
import java.awt.color.ColorSpace;
|
||||||
import java.awt.image.*;
|
import java.awt.image.*;
|
||||||
@@ -54,13 +40,34 @@ import java.nio.ByteOrder;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import javax.imageio.IIOException;
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import javax.imageio.ImageReadParam;
|
||||||
|
import javax.imageio.ImageReader;
|
||||||
|
import javax.imageio.ImageTypeSpecifier;
|
||||||
|
import javax.imageio.event.IIOReadUpdateListener;
|
||||||
|
import javax.imageio.event.IIOReadWarningListener;
|
||||||
|
import javax.imageio.metadata.IIOMetadata;
|
||||||
|
import javax.imageio.metadata.IIOMetadataFormatImpl;
|
||||||
|
import javax.imageio.spi.ImageReaderSpi;
|
||||||
|
import javax.imageio.stream.ImageInputStream;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||||
|
import com.twelvemonkeys.imageio.stream.SubImageInputStream;
|
||||||
|
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||||
|
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
||||||
|
import com.twelvemonkeys.imageio.util.ProgressListenerBase;
|
||||||
|
import com.twelvemonkeys.io.LittleEndianDataInputStream;
|
||||||
|
import com.twelvemonkeys.io.enc.DecoderStream;
|
||||||
|
import com.twelvemonkeys.xml.XMLSerializer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ImageReader for Microsoft Windows Bitmap (BMP) format.
|
* ImageReader for Microsoft Windows Bitmap (BMP) format.
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @author last modified by $Author: haraldk$
|
* @author last modified by $Author: haraldk$
|
||||||
* @version $Id: CURImageReader.java,v 1.0 Apr 20, 2009 11:54:28 AM haraldk Exp$
|
* @version $Id: BMPImageReader.java,v 1.0 Apr 20, 2009 11:54:28 AM haraldk Exp$
|
||||||
* @see com.twelvemonkeys.imageio.plugins.bmp.ICOImageReader
|
* @see ICOImageReader
|
||||||
*/
|
*/
|
||||||
public final class BMPImageReader extends ImageReaderBase {
|
public final class BMPImageReader extends ImageReaderBase {
|
||||||
private long pixelOffset;
|
private long pixelOffset;
|
||||||
@@ -123,6 +130,10 @@ public final class BMPImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
// Read DIB header
|
// Read DIB header
|
||||||
header = DIBHeader.read(imageInput);
|
header = DIBHeader.read(imageInput);
|
||||||
|
|
||||||
|
if (pixelOffset < header.size + DIB.BMP_FILE_HEADER_SIZE) {
|
||||||
|
throw new IIOException("Invalid pixel offset: " + pixelOffset);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,50 +216,55 @@ public final class BMPImageReader extends ImageReaderBase {
|
|||||||
throw new IIOException("Multiple planes not supported");
|
throw new IIOException("Multiple planes not supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (header.getBitCount()) {
|
try {
|
||||||
case 1:
|
switch (header.getBitCount()) {
|
||||||
case 2:
|
case 1:
|
||||||
case 4:
|
case 2:
|
||||||
case 8:
|
case 4:
|
||||||
return ImageTypeSpecifiers.createFromIndexColorModel(readColorMap());
|
case 8:
|
||||||
|
return ImageTypeSpecifiers.createFromIndexColorModel(readColorMap());
|
||||||
|
|
||||||
case 16:
|
case 16:
|
||||||
if (header.hasMasks()) {
|
if (header.hasMasks()) {
|
||||||
return ImageTypeSpecifiers.createPacked(
|
return ImageTypeSpecifiers.createPacked(
|
||||||
ColorSpace.getInstance(ColorSpace.CS_sRGB),
|
ColorSpace.getInstance(ColorSpace.CS_sRGB),
|
||||||
header.masks[0], header.masks[1], header.masks[2], header.masks[3],
|
header.masks[0], header.masks[1], header.masks[2], header.masks[3],
|
||||||
DataBuffer.TYPE_USHORT, false
|
DataBuffer.TYPE_USHORT, false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default if no mask is 555
|
// Default if no mask is 555
|
||||||
return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_USHORT_555_RGB);
|
return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_USHORT_555_RGB);
|
||||||
|
|
||||||
case 24:
|
case 24:
|
||||||
if (header.getCompression() != DIB.COMPRESSION_RGB) {
|
if (header.getCompression() != DIB.COMPRESSION_RGB) {
|
||||||
throw new IIOException("Unsupported compression for RGB: " + header.getCompression());
|
throw new IIOException("Unsupported compression for RGB: " + header.getCompression());
|
||||||
}
|
}
|
||||||
|
|
||||||
return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR);
|
return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR);
|
||||||
|
|
||||||
case 32:
|
case 32:
|
||||||
if (header.hasMasks()) {
|
if (header.hasMasks()) {
|
||||||
return ImageTypeSpecifiers.createPacked(
|
return ImageTypeSpecifiers.createPacked(
|
||||||
ColorSpace.getInstance(ColorSpace.CS_sRGB),
|
ColorSpace.getInstance(ColorSpace.CS_sRGB),
|
||||||
header.masks[0], header.masks[1], header.masks[2], header.masks[3],
|
header.masks[0], header.masks[1], header.masks[2], header.masks[3],
|
||||||
DataBuffer.TYPE_INT, false
|
DataBuffer.TYPE_INT, false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default if no mask
|
// Default if no mask
|
||||||
return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB);
|
return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB);
|
||||||
|
|
||||||
case 0:
|
case 0:
|
||||||
if (header.getCompression() == DIB.COMPRESSION_JPEG || header.getCompression() == DIB.COMPRESSION_PNG) {
|
if (header.getCompression() == DIB.COMPRESSION_JPEG || header.getCompression() == DIB.COMPRESSION_PNG) {
|
||||||
return initReaderDelegate(header.getCompression()).getRawImageType(0);
|
return initReaderDelegate(header.getCompression()).getRawImageType(0);
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
throw new IIOException("Unsupported bit count: " + header.getBitCount());
|
throw new IIOException("Unsupported bit count: " + header.getBitCount());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException e) {
|
||||||
|
throw new IIOException(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -612,7 +628,7 @@ public final class BMPImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Why, oh why..? Instead of accepting it's own native format as it should,
|
// Why, oh why..? Instead of accepting it's own native format as it should,
|
||||||
// The BMPImageWriter only accepts instances of com.sun.imageio.plugins.bmp.BMPMetadata...
|
// The DIBImageWriter only accepts instances of com.sun.imageio.plugins.bmp.BMPMetadata...
|
||||||
// TODO: Consider reflectively construct a BMPMetadata and inject fields
|
// TODO: Consider reflectively construct a BMPMetadata and inject fields
|
||||||
return new BMPMetadata(header, colors);
|
return new BMPMetadata(header, colors);
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-19
@@ -4,26 +4,28 @@
|
|||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* 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
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
* list of conditions and the following disclaimer.
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
*
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
* and/or other materials provided with the distribution.
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
*
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
* contributors may be used to endorse or promote products derived from
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
* this software without specific prior written permission.
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||||
|
|||||||
+150
@@ -0,0 +1,150 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017, Harald Kuhr
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||||
|
|
||||||
|
import javax.imageio.IIOImage;
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import javax.imageio.ImageTypeSpecifier;
|
||||||
|
import javax.imageio.ImageWriteParam;
|
||||||
|
import javax.imageio.metadata.IIOMetadata;
|
||||||
|
import javax.imageio.plugins.bmp.BMPImageWriteParam;
|
||||||
|
import javax.imageio.spi.ImageWriterSpi;
|
||||||
|
import javax.imageio.stream.ImageOutputStream;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BMPImageWriter
|
||||||
|
*/
|
||||||
|
public final class BMPImageWriter extends DIBImageWriter {
|
||||||
|
protected BMPImageWriter(ImageWriterSpi provider) {
|
||||||
|
super(provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ImageWriteParam getDefaultWriteParam() {
|
||||||
|
// We can use the existing BMPImageWriteParam, as it's part of the javax.imageio API.
|
||||||
|
return new BMPImageWriteParam(getLocale());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IIOMetadata getDefaultImageMetadata(ImageTypeSpecifier imageType, ImageWriteParam param) {
|
||||||
|
// TODO
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IIOMetadata convertImageMetadata(IIOMetadata inData, ImageTypeSpecifier imageType, ImageWriteParam param) {
|
||||||
|
// TODO: Support both our own and the com.sun.. metadata + standard metadata
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(IIOMetadata streamMetadata, IIOImage image, ImageWriteParam param) throws IOException {
|
||||||
|
assertOutput();
|
||||||
|
|
||||||
|
if (image == null) {
|
||||||
|
throw new IllegalArgumentException("image may not be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (image.hasRaster()) {
|
||||||
|
// TODO: The default BMPWriter seems to support this, consider doing so as well
|
||||||
|
throw new UnsupportedOperationException("image has a Raster!");
|
||||||
|
}
|
||||||
|
|
||||||
|
imageOutput.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
|
||||||
|
clearAbortRequest();
|
||||||
|
processImageStarted(0);
|
||||||
|
|
||||||
|
if (param == null) {
|
||||||
|
param = getDefaultWriteParam();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default to bottom-up
|
||||||
|
// TODO: top-down only allowed for RGB and BITFIELDS compressions (not RLE or PNG)!
|
||||||
|
// Though Windows seems to support top-down for RLE too...
|
||||||
|
final boolean isTopDown = param instanceof BMPImageWriteParam && ((BMPImageWriteParam) param).isTopDown();
|
||||||
|
// int compression = DIB.COMPRESSION_ALPHA_BITFIELDS; // BMP can use BIFTFIELDS or ALPHA_BITFIELDS
|
||||||
|
int compression = DIB.COMPRESSION_RGB;
|
||||||
|
|
||||||
|
// TODO: Fix
|
||||||
|
BufferedImage img = (BufferedImage) image.getRenderedImage();
|
||||||
|
|
||||||
|
int height = img.getHeight();
|
||||||
|
int width = img.getWidth();
|
||||||
|
|
||||||
|
// Write File header
|
||||||
|
// TODO: Always use V4/V5 header, when writing with alpha, to avoid ambiguity
|
||||||
|
// TODO: Allow writing normal BITMAP_INFO_HEADER_SIZE with "fake" alpha as well?
|
||||||
|
int infoHeaderSize = DIB.BITMAP_INFO_HEADER_SIZE;
|
||||||
|
boolean hasExtraMasks = infoHeaderSize == DIB.BITMAP_INFO_HEADER_SIZE && (compression == DIB.COMPRESSION_BITFIELDS || compression == DIB.COMPRESSION_ALPHA_BITFIELDS);
|
||||||
|
// TODO: Allow writing without file header for ICO/CUR support
|
||||||
|
writeFileHeader(infoHeaderSize, DIB.BMP_FILE_HEADER_SIZE + infoHeaderSize + width * height * 4, hasExtraMasks);
|
||||||
|
writeDIBHeader(infoHeaderSize, img.getWidth(), img.getHeight(), isTopDown, img.getColorModel().getPixelSize(), compression);
|
||||||
|
// writeDIBHeader(infoHeaderSize, img, isTopDown, DIB.COMPRESSION_RGB);
|
||||||
|
|
||||||
|
if (hasExtraMasks) {
|
||||||
|
imageOutput.writeInt(0x000000FF); // B
|
||||||
|
imageOutput.writeInt(0x0000FF00); // G
|
||||||
|
imageOutput.writeInt(0x00FF0000); // R
|
||||||
|
imageOutput.writeInt(0xFF000000); // A
|
||||||
|
}
|
||||||
|
|
||||||
|
writeUncompressed(isTopDown, img, height, width);
|
||||||
|
|
||||||
|
processImageComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeFileHeader(int infoHeaderSize, int fileSize, boolean hasMasks) throws IOException {
|
||||||
|
// 14 bytes
|
||||||
|
imageOutput.writeShort('M' << 8 | 'B');
|
||||||
|
imageOutput.writeInt(fileSize + (hasMasks ? 16 : 0)); // File size (only known at this time if uncompressed!)
|
||||||
|
imageOutput.writeShort(0); // Reserved
|
||||||
|
imageOutput.writeShort(0); // Reserved
|
||||||
|
|
||||||
|
imageOutput.writeInt(DIB.BMP_FILE_HEADER_SIZE + infoHeaderSize + (hasMasks ? 16 : 0)); // Offset to image data
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws IOException {
|
||||||
|
File input = new File(args[0]);
|
||||||
|
File output = new File(args[0].replace('.', '_') + "_copy.bmp");
|
||||||
|
|
||||||
|
try (ImageOutputStream stream = ImageIO.createImageOutputStream(output)) {
|
||||||
|
DIBImageWriter writer = new BMPImageWriter(null);
|
||||||
|
writer.setOutput(stream);
|
||||||
|
writer.write(ImageIO.read(input));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+76
@@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017, Harald Kuhr
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.imageio.spi.ImageWriterSpiBase;
|
||||||
|
|
||||||
|
import javax.imageio.ImageTypeSpecifier;
|
||||||
|
import javax.imageio.spi.ImageWriterSpi;
|
||||||
|
import javax.imageio.spi.ServiceRegistry;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import static com.twelvemonkeys.imageio.util.IIOUtil.lookupProviderByName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BMPImageWriterSpi
|
||||||
|
*/
|
||||||
|
public final class BMPImageWriterSpi extends ImageWriterSpiBase {
|
||||||
|
public BMPImageWriterSpi() {
|
||||||
|
super(new BMPProviderInfo());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRegistration(ServiceRegistry registry, Class<?> category) {
|
||||||
|
// Make sure we register BEHIND the built-in BMP writer
|
||||||
|
ImageWriterSpi sunSpi = lookupProviderByName(registry, "com.sun.imageio.plugins.bmp.BMPImageWriterSpi", ImageWriterSpi.class);
|
||||||
|
|
||||||
|
if (sunSpi != null && sunSpi.getVendorName() != null) {
|
||||||
|
registry.setOrdering((Class<ImageWriterSpi>) category, sunSpi, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canEncodeImage(final ImageTypeSpecifier type) {
|
||||||
|
// TODO: Support more types, as time permits.
|
||||||
|
return type.getBufferedImageType() == BufferedImage.TYPE_4BYTE_ABGR;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BMPImageWriter createWriterInstance(final Object extension) {
|
||||||
|
return new BMPImageWriter(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription(final Locale locale) {
|
||||||
|
return "Windows Device Independent Bitmap Format (BMP) Writer";
|
||||||
|
}
|
||||||
|
}
|
||||||
+104
-97
@@ -4,26 +4,28 @@
|
|||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* 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
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
* list of conditions and the following disclaimer.
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
*
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
* and/or other materials provided with the distribution.
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
*
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
* contributors may be used to endorse or promote products derived from
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
* this software without specific prior written permission.
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||||
@@ -168,87 +170,90 @@ final class BMPMetadata extends AbstractMetadata {
|
|||||||
protected IIOMetadataNode getStandardCompressionNode() {
|
protected IIOMetadataNode getStandardCompressionNode() {
|
||||||
IIOMetadataNode compression = new IIOMetadataNode("Compression");
|
IIOMetadataNode compression = new IIOMetadataNode("Compression");
|
||||||
IIOMetadataNode compressionTypeName = addChildNode(compression, "CompressionTypeName", null);
|
IIOMetadataNode compressionTypeName = addChildNode(compression, "CompressionTypeName", null);
|
||||||
compressionTypeName.setAttribute("value", "NONE");
|
|
||||||
|
// TODO: Should the compression names always match the compression names used in the ImageWriteParam?
|
||||||
|
// OR should they be as standard as possible..?
|
||||||
|
// The built-in plugin uses "BI_RGB", "BI_RLE8", "BI_RLE4", "BI_BITFIELDS", "BI_JPEG and "BI_PNG"
|
||||||
|
switch (header.compression) {
|
||||||
|
case DIB.COMPRESSION_RLE4:
|
||||||
|
case DIB.COMPRESSION_RLE8:
|
||||||
|
compressionTypeName.setAttribute("value", "RLE");
|
||||||
|
break;
|
||||||
|
case DIB.COMPRESSION_JPEG:
|
||||||
|
compressionTypeName.setAttribute("value", "JPEG");
|
||||||
|
break;
|
||||||
|
case DIB.COMPRESSION_PNG:
|
||||||
|
compressionTypeName.setAttribute("value", "PNG");
|
||||||
|
break;
|
||||||
|
case DIB.COMPRESSION_RGB:
|
||||||
|
case DIB.COMPRESSION_BITFIELDS:
|
||||||
|
case DIB.COMPRESSION_ALPHA_BITFIELDS:
|
||||||
|
default:
|
||||||
|
compressionTypeName.setAttribute("value", "NONE");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return compression;
|
return compression;
|
||||||
// switch (header.getImageType()) {
|
|
||||||
// case TGA.IMAGETYPE_COLORMAPPED_RLE:
|
|
||||||
// case TGA.IMAGETYPE_TRUECOLOR_RLE:
|
|
||||||
// case TGA.IMAGETYPE_MONOCHROME_RLE:
|
|
||||||
// case TGA.IMAGETYPE_COLORMAPPED_HUFFMAN:
|
|
||||||
// case TGA.IMAGETYPE_COLORMAPPED_HUFFMAN_QUADTREE:
|
|
||||||
// IIOMetadataNode node = new IIOMetadataNode("Compression");
|
|
||||||
// IIOMetadataNode compressionTypeName = new IIOMetadataNode("CompressionTypeName");
|
|
||||||
//
|
|
||||||
// // Compression can be RLE4, RLE8, PNG, JPEG or NONE
|
|
||||||
// String value = header.getImageType() == TGA.IMAGETYPE_COLORMAPPED_HUFFMAN || header.getImageType() == TGA.IMAGETYPE_COLORMAPPED_HUFFMAN_QUADTREE
|
|
||||||
// ? "Uknown" : "RLE";
|
|
||||||
// compressionTypeName.setAttribute("value", value);
|
|
||||||
// node.appendChild(compressionTypeName);
|
|
||||||
//
|
|
||||||
// IIOMetadataNode lossless = new IIOMetadataNode("Lossless");
|
|
||||||
// lossless.setAttribute("value", "TRUE"); // TODO: Unless JPEG!
|
|
||||||
// node.appendChild(lossless);
|
|
||||||
//
|
|
||||||
// return node;
|
|
||||||
// default:
|
|
||||||
// // No compression
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected IIOMetadataNode getStandardDataNode() {
|
protected IIOMetadataNode getStandardDataNode() {
|
||||||
IIOMetadataNode node = new IIOMetadataNode("Data");
|
IIOMetadataNode node = new IIOMetadataNode("Data");
|
||||||
|
|
||||||
// IIOMetadataNode planarConfiguration = new IIOMetadataNode("PlanarConfiguration");
|
|
||||||
// planarConfiguration.setAttribute("value", "PixelInterleaved");
|
|
||||||
// node.appendChild(planarConfiguration);
|
|
||||||
|
|
||||||
// IIOMetadataNode sampleFormat = new IIOMetadataNode("SampleFormat");
|
|
||||||
// switch (header.getImageType()) {
|
|
||||||
// case TGA.IMAGETYPE_COLORMAPPED:
|
|
||||||
// case TGA.IMAGETYPE_COLORMAPPED_RLE:
|
|
||||||
// case TGA.IMAGETYPE_COLORMAPPED_HUFFMAN:
|
|
||||||
// case TGA.IMAGETYPE_COLORMAPPED_HUFFMAN_QUADTREE:
|
|
||||||
// sampleFormat.setAttribute("value", "Index");
|
|
||||||
// break;
|
|
||||||
// default:
|
|
||||||
// sampleFormat.setAttribute("value", "UnsignedIntegral");
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// node.appendChild(sampleFormat);
|
|
||||||
|
|
||||||
IIOMetadataNode bitsPerSample = new IIOMetadataNode("BitsPerSample");
|
IIOMetadataNode bitsPerSample = new IIOMetadataNode("BitsPerSample");
|
||||||
switch (header.getBitCount()) {
|
switch (header.getBitCount()) {
|
||||||
|
// TODO: case 0: determined by embedded format (PNG/JPEG)
|
||||||
case 1:
|
case 1:
|
||||||
case 2:
|
case 2:
|
||||||
case 4:
|
case 4:
|
||||||
case 8:
|
case 8:
|
||||||
bitsPerSample.setAttribute("value", createListValue(1, Integer.toString(header.getBitCount())));
|
bitsPerSample.setAttribute("value", createListValue(1, Integer.toString(header.getBitCount())));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 16:
|
case 16:
|
||||||
// TODO: Consult masks here!
|
// Default is 555
|
||||||
bitsPerSample.setAttribute("value", createListValue(4, Integer.toString(4)));
|
bitsPerSample.setAttribute("value", header.hasMasks()
|
||||||
|
? createBitsPerSampleForBitMasks()
|
||||||
|
: createListValue(3, Integer.toString(5)));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 24:
|
case 24:
|
||||||
bitsPerSample.setAttribute("value", createListValue(3, Integer.toString(8)));
|
bitsPerSample.setAttribute("value", createListValue(3, Integer.toString(8)));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 32:
|
case 32:
|
||||||
bitsPerSample.setAttribute("value", createListValue(4, Integer.toString(8)));
|
// Default is 888
|
||||||
|
bitsPerSample.setAttribute("value", header.hasMasks()
|
||||||
|
? createBitsPerSampleForBitMasks()
|
||||||
|
: createListValue(3, Integer.toString(8)));
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
node.appendChild(bitsPerSample);
|
node.appendChild(bitsPerSample);
|
||||||
|
|
||||||
// TODO: Do we need MSB?
|
|
||||||
// IIOMetadataNode sampleMSB = new IIOMetadataNode("SampleMSB");
|
|
||||||
// sampleMSB.setAttribute("value", createListValue(header.getChannels(), "0"));
|
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String createBitsPerSampleForBitMasks() {
|
||||||
|
boolean hasAlpha = header.masks[3] != 0;
|
||||||
|
|
||||||
|
return createListValue(hasAlpha ? 4 : 3,
|
||||||
|
Integer.toString(countMaskBits(header.masks[0])), Integer.toString(countMaskBits(header.masks[1])),
|
||||||
|
Integer.toString(countMaskBits(header.masks[2])), Integer.toString(countMaskBits(header.masks[3])));
|
||||||
|
}
|
||||||
|
|
||||||
|
private int countMaskBits(int mask) {
|
||||||
|
// See https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan
|
||||||
|
int count;
|
||||||
|
|
||||||
|
for (count = 0; mask != 0; count++) {
|
||||||
|
mask &= mask - 1; // clear the least significant bit set
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
private String createListValue(final int itemCount, final String... values) {
|
private String createListValue(final int itemCount, final String... values) {
|
||||||
StringBuilder buffer = new StringBuilder();
|
StringBuilder buffer = new StringBuilder();
|
||||||
|
|
||||||
@@ -265,28 +270,31 @@ final class BMPMetadata extends AbstractMetadata {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected IIOMetadataNode getStandardDimensionNode() {
|
protected IIOMetadataNode getStandardDimensionNode() {
|
||||||
if (header.xPixelsPerMeter > 0 || header.yPixelsPerMeter > 0) {
|
IIOMetadataNode dimension = new IIOMetadataNode("Dimension");
|
||||||
IIOMetadataNode dimension = new IIOMetadataNode("Dimension");
|
|
||||||
|
|
||||||
addChildNode(dimension, "PixelAspectRatio", null);
|
if (header.xPixelsPerMeter > 0 && header.yPixelsPerMeter > 0) {
|
||||||
addChildNode(dimension, "HorizontalPhysicalPixelSpacing", null);
|
float ratio = header.xPixelsPerMeter / (float) header.yPixelsPerMeter;
|
||||||
addChildNode(dimension, "VerticalPhysicalPixelSpacing", null);
|
addChildNode(dimension, "PixelAspectRatio", null)
|
||||||
|
.setAttribute("value", String.valueOf(ratio));
|
||||||
|
|
||||||
// IIOMetadataNode imageOrientation = new IIOMetadataNode("ImageOrientation");
|
addChildNode(dimension, "HorizontalPixelSize", null)
|
||||||
//
|
.setAttribute("value", String.valueOf(1f / header.xPixelsPerMeter * 1000));
|
||||||
// if (header.topDown) {
|
addChildNode(dimension, "VerticalPixelSize", null)
|
||||||
// imageOrientation.setAttribute("value", "FlipH");
|
.setAttribute("value", String.valueOf(1f / header.yPixelsPerMeter * 1000));
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// imageOrientation.setAttribute("value", "Normal");
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// dimension.appendChild(imageOrientation);
|
|
||||||
|
|
||||||
return dimension;
|
// Hmmm.. The JRE version includes these for some reason, even if values seem to be same as default...
|
||||||
|
addChildNode(dimension, "HorizontalPhysicalPixelSpacing", null)
|
||||||
|
.setAttribute("value", String.valueOf(0));
|
||||||
|
addChildNode(dimension, "VerticalPhysicalPixelSpacing", null)
|
||||||
|
.setAttribute("value", String.valueOf(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
if (header.topDown) {
|
||||||
|
addChildNode(dimension, "ImageOrientation", null)
|
||||||
|
.setAttribute("value", "FlipH"); // For BMP, bottom-up is "normal"...
|
||||||
|
}
|
||||||
|
|
||||||
|
return dimension;
|
||||||
}
|
}
|
||||||
|
|
||||||
// No document node
|
// No document node
|
||||||
@@ -297,16 +305,15 @@ final class BMPMetadata extends AbstractMetadata {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected IIOMetadataNode getStandardTransparencyNode() {
|
protected IIOMetadataNode getStandardTransparencyNode() {
|
||||||
return null;
|
if (header.hasMasks() && header.masks[3] != 0) {
|
||||||
|
IIOMetadataNode transparency = new IIOMetadataNode("Transparency");
|
||||||
|
IIOMetadataNode alpha = new IIOMetadataNode("Alpha");
|
||||||
|
alpha.setAttribute("value", "nonpremultiplied");
|
||||||
|
transparency.appendChild(alpha);
|
||||||
|
|
||||||
// IIOMetadataNode transparency = new IIOMetadataNode("Transparency");
|
return transparency;
|
||||||
//
|
}
|
||||||
// IIOMetadataNode alpha = new IIOMetadataNode("Alpha");
|
|
||||||
//
|
return null;
|
||||||
// // TODO: Consult masks
|
|
||||||
// alpha.setAttribute("value", header.getBitCount() == 32 ? "nonpremultiplied" : "none");
|
|
||||||
// transparency.appendChild(alpha);
|
|
||||||
//
|
|
||||||
// return transparency;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+39
-7
@@ -1,3 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* 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 of the copyright holder nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||||
|
|
||||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
||||||
@@ -10,22 +40,24 @@ import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
|||||||
* @version $Id: BMPProviderInfo.java,v 1.0 20/03/15 harald.kuhr Exp$
|
* @version $Id: BMPProviderInfo.java,v 1.0 20/03/15 harald.kuhr Exp$
|
||||||
*/
|
*/
|
||||||
final class BMPProviderInfo extends ReaderWriterProviderInfo {
|
final class BMPProviderInfo extends ReaderWriterProviderInfo {
|
||||||
protected BMPProviderInfo() {
|
BMPProviderInfo() {
|
||||||
super(
|
super(
|
||||||
BMPProviderInfo.class,
|
BMPProviderInfo.class,
|
||||||
new String[] {"bmp", "BMP"},
|
new String[] {"bmp", "BMP"},
|
||||||
new String[] {"bmp", "rle"},
|
new String[] {"bmp", "rle"},
|
||||||
new String[] {
|
new String[] {
|
||||||
"image/bmp",
|
"image/bmp",
|
||||||
"image/x-bmp"
|
"image/x-bmp",
|
||||||
// "image/vnd.microsoft.bitmap", // TODO: Official IANA MIME
|
"image/vnd.microsoft.bitmap"
|
||||||
},
|
},
|
||||||
"com.twelvemonkeys.imageio.plugins.bmp.BMPImageReader",
|
"com.twelvemonkeys.imageio.plugins.bmp.BMPImageReader",
|
||||||
new String[] {"com.twelvemonkeys.imageio.plugins.bmp.BMPImageReaderSpi"},
|
new String[] {"com.twelvemonkeys.imageio.plugins.bmp.BMPImageReaderSpi"},
|
||||||
"com.sun.imageio.plugins.bmp.BMPImageWriter",
|
"com.twelvemonkeys.imageio.plugins.bmp.BMPImageWriter",
|
||||||
new String[]{"com.sun.imageio.plugins.bmp.BMPImageWriterSpi"}, // We support the same native metadata format
|
new String[] {"com.twelvemonkeys.imageio.plugins.bmp.BMPImageWriterSpi"},
|
||||||
false, null, null, null, null,
|
false, null, null,
|
||||||
true, BMPMetadata.nativeMetadataFormatName, "com.sun.imageio.plugins.bmp.BMPMetadataFormat", null, null
|
null, null,
|
||||||
|
true, BMPMetadata.nativeMetadataFormatName, "com.sun.imageio.plugins.bmp.BMPMetadataFormat",
|
||||||
|
null, null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+26
-27
@@ -4,33 +4,35 @@
|
|||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* 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
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
* list of conditions and the following disclaimer.
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
*
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
* and/or other materials provided with the distribution.
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
*
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
* contributors may be used to endorse or promote products derived from
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
* this software without specific prior written permission.
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||||
|
|
||||||
import com.twelvemonkeys.lang.Validate;
|
import static com.twelvemonkeys.lang.Validate.notNull;
|
||||||
|
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes a bitmap structure.
|
* Describes a bitmap structure.
|
||||||
@@ -46,14 +48,11 @@ abstract class BitmapDescriptor {
|
|||||||
protected BitmapMask mask;
|
protected BitmapMask mask;
|
||||||
|
|
||||||
public BitmapDescriptor(final DirectoryEntry pEntry, final DIBHeader pHeader) {
|
public BitmapDescriptor(final DirectoryEntry pEntry, final DIBHeader pHeader) {
|
||||||
Validate.notNull(pEntry, "entry");
|
entry = notNull(pEntry, "entry");;
|
||||||
Validate.notNull(pHeader, "header");
|
header = notNull(pHeader, "header");
|
||||||
|
|
||||||
entry = pEntry;
|
|
||||||
header = pHeader;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract public BufferedImage getImage();
|
abstract public BufferedImage getImage() throws IOException;
|
||||||
|
|
||||||
public final int getWidth() {
|
public final int getWidth() {
|
||||||
return entry.getWidth();
|
return entry.getWidth();
|
||||||
|
|||||||
+22
-20
@@ -4,28 +4,29 @@
|
|||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* 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
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
* list of conditions and the following disclaimer.
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
*
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
* and/or other materials provided with the distribution.
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
*
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
* contributors may be used to endorse or promote products derived from
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
* this software without specific prior written permission.
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||||
|
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
@@ -162,6 +163,7 @@ class BitmapIndexed extends BitmapDescriptor {
|
|||||||
return transparent;
|
return transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BufferedImage getImage() {
|
public BufferedImage getImage() {
|
||||||
if (image == null) {
|
if (image == null) {
|
||||||
image = createImageIndexed();
|
image = createImageIndexed();
|
||||||
|
|||||||
+21
-19
@@ -4,26 +4,28 @@
|
|||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* 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
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
* list of conditions and the following disclaimer.
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
*
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
* and/or other materials provided with the distribution.
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
*
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
* contributors may be used to endorse or promote products derived from
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
* this software without specific prior written permission.
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||||
|
|||||||
+22
-19
@@ -4,26 +4,28 @@
|
|||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* 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
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
* list of conditions and the following disclaimer.
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
*
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
* and/or other materials provided with the distribution.
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
*
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
* contributors may be used to endorse or promote products derived from
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
* this software without specific prior written permission.
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||||
@@ -44,6 +46,7 @@ class BitmapRGB extends BitmapDescriptor {
|
|||||||
super(pEntry, pHeader);
|
super(pEntry, pHeader);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BufferedImage getImage() {
|
public BufferedImage getImage() {
|
||||||
// Test is mask != null rather than hasMask(), as 32 bit (w/alpha)
|
// Test is mask != null rather than hasMask(), as 32 bit (w/alpha)
|
||||||
// might still have bitmask, but we don't read or use it.
|
// might still have bitmask, but we don't read or use it.
|
||||||
|
|||||||
+29
-23
@@ -4,31 +4,36 @@
|
|||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* 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
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
* list of conditions and the following disclaimer.
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
*
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
* and/or other materials provided with the distribution.
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
*
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
* contributors may be used to endorse or promote products derived from
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
* this software without specific prior written permission.
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||||
|
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.imageio.IIOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents bitmap structures we can't read.
|
* Represents bitmap structures we can't read.
|
||||||
@@ -40,13 +45,14 @@ import java.awt.image.BufferedImage;
|
|||||||
class BitmapUnsupported extends BitmapDescriptor {
|
class BitmapUnsupported extends BitmapDescriptor {
|
||||||
private String message;
|
private String message;
|
||||||
|
|
||||||
public BitmapUnsupported(final DirectoryEntry pEntry, final String pMessage) {
|
public BitmapUnsupported(final DirectoryEntry pEntry, DIBHeader header, final String pMessage) {
|
||||||
super(pEntry, null);
|
super(pEntry, header);
|
||||||
|
|
||||||
message = pMessage;
|
message = pMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BufferedImage getImage() {
|
@Override
|
||||||
throw new IllegalStateException(message);
|
public BufferedImage getImage() throws IOException {
|
||||||
|
throw new IIOException(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-19
@@ -4,26 +4,28 @@
|
|||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* 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
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
* list of conditions and the following disclaimer.
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
*
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
* and/or other materials provided with the distribution.
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
*
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
* contributors may be used to endorse or promote products derived from
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
* this software without specific prior written permission.
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||||
|
|||||||
+21
-19
@@ -4,26 +4,28 @@
|
|||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* 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
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
* list of conditions and the following disclaimer.
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
*
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
* and/or other materials provided with the distribution.
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
*
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
* contributors may be used to endorse or promote products derived from
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
* this software without specific prior written permission.
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||||
|
|||||||
+22
-20
@@ -4,26 +4,28 @@
|
|||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* 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
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
* list of conditions and the following disclaimer.
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
*
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
* and/or other materials provided with the distribution.
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
*
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
* contributors may be used to endorse or promote products derived from
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
* this software without specific prior written permission.
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||||
@@ -38,7 +40,7 @@ import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
|||||||
* @version $Id: CURProviderInfo.java,v 1.0 20/03/15 harald.kuhr Exp$
|
* @version $Id: CURProviderInfo.java,v 1.0 20/03/15 harald.kuhr Exp$
|
||||||
*/
|
*/
|
||||||
final class CURProviderInfo extends ReaderWriterProviderInfo {
|
final class CURProviderInfo extends ReaderWriterProviderInfo {
|
||||||
protected CURProviderInfo() {
|
CURProviderInfo() {
|
||||||
super(
|
super(
|
||||||
CURProviderInfo.class,
|
CURProviderInfo.class,
|
||||||
new String[]{"cur", "CUR"},
|
new String[]{"cur", "CUR"},
|
||||||
|
|||||||
@@ -4,26 +4,28 @@
|
|||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* 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
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
* list of conditions and the following disclaimer.
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
*
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
* and/or other materials provided with the distribution.
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
*
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
* contributors may be used to endorse or promote products derived from
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
* this software without specific prior written permission.
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||||
|
|||||||
+25
-2
@@ -30,11 +30,12 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||||
|
|
||||||
import javax.imageio.IIOException;
|
|
||||||
import java.io.DataInput;
|
import java.io.DataInput;
|
||||||
import java.io.DataOutput;
|
import java.io.DataOutput;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.imageio.IIOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the DIB (Device Independent Bitmap) Information header structure.
|
* Represents the DIB (Device Independent Bitmap) Information header structure.
|
||||||
*
|
*
|
||||||
@@ -213,7 +214,7 @@ abstract class DIBHeader {
|
|||||||
|
|
||||||
// NOTE: Unlike all other headers, width and height are unsigned SHORT values (16 bit)!
|
// NOTE: Unlike all other headers, width and height are unsigned SHORT values (16 bit)!
|
||||||
width = pStream.readUnsignedShort();
|
width = pStream.readUnsignedShort();
|
||||||
height = pStream.readUnsignedShort();
|
height = pStream.readShort();
|
||||||
|
|
||||||
if (height < 0) {
|
if (height < 0) {
|
||||||
height = -height;
|
height = -height;
|
||||||
@@ -240,6 +241,7 @@ abstract class DIBHeader {
|
|||||||
* @see <a href="http://www.fileformat.info/format/os2bmp/egff.htm">OS/2 Bitmap File Format Summary</a>
|
* @see <a href="http://www.fileformat.info/format/os2bmp/egff.htm">OS/2 Bitmap File Format Summary</a>
|
||||||
*/
|
*/
|
||||||
static final class BitmapCoreHeaderV2 extends DIBHeader {
|
static final class BitmapCoreHeaderV2 extends DIBHeader {
|
||||||
|
@SuppressWarnings("unused")
|
||||||
protected void read(final int pSize, final DataInput pStream) throws IOException {
|
protected void read(final int pSize, final DataInput pStream) throws IOException {
|
||||||
if (pSize != DIB.OS2_V2_HEADER_SIZE && pSize != DIB.OS2_V2_HEADER_16_SIZE) {
|
if (pSize != DIB.OS2_V2_HEADER_SIZE && pSize != DIB.OS2_V2_HEADER_16_SIZE) {
|
||||||
throw new IIOException(String.format("Size: %s !=: %s", pSize, DIB.OS2_V2_HEADER_SIZE));
|
throw new IIOException(String.format("Size: %s !=: %s", pSize, DIB.OS2_V2_HEADER_SIZE));
|
||||||
@@ -339,6 +341,27 @@ abstract class DIBHeader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void write(final DataOutput stream) throws IOException {
|
||||||
|
stream.writeInt(DIB.BITMAP_INFO_HEADER_SIZE);
|
||||||
|
|
||||||
|
stream.writeInt(width);
|
||||||
|
stream.writeInt(topDown ? -height : height);
|
||||||
|
|
||||||
|
stream.writeShort(planes);
|
||||||
|
stream.writeShort(bitCount);
|
||||||
|
stream.writeInt(compression);
|
||||||
|
|
||||||
|
stream.writeInt(imageSize);
|
||||||
|
|
||||||
|
stream.writeInt(xPixelsPerMeter);
|
||||||
|
stream.writeInt(yPixelsPerMeter);
|
||||||
|
|
||||||
|
stream.writeInt(colorsUsed);
|
||||||
|
stream.writeInt(colorsImportant);
|
||||||
|
|
||||||
|
// TODO: Write masks, if bitfields
|
||||||
|
}
|
||||||
|
|
||||||
public String getBMPVersion() {
|
public String getBMPVersion() {
|
||||||
// This is to be compatible with the native metadata of the original com.sun....BMPMetadata
|
// This is to be compatible with the native metadata of the original com.sun....BMPMetadata
|
||||||
return compression == DIB.COMPRESSION_BITFIELDS ? "BMP v. 3.x NT" : "BMP v. 3.x";
|
return compression == DIB.COMPRESSION_BITFIELDS ? "BMP v. 3.x NT" : "BMP v. 3.x";
|
||||||
|
|||||||
+51
-42
@@ -4,40 +4,32 @@
|
|||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* 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
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
* list of conditions and the following disclaimer.
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
*
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
* and/or other materials provided with the distribution.
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
*
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
* contributors may be used to endorse or promote products derived from
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
* this software without specific prior written permission.
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||||
|
|
||||||
import com.twelvemonkeys.image.ImageUtil;
|
|
||||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
|
||||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
|
||||||
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
|
||||||
import com.twelvemonkeys.util.WeakWeakMap;
|
|
||||||
|
|
||||||
import javax.imageio.*;
|
|
||||||
import javax.imageio.spi.ImageReaderSpi;
|
|
||||||
import javax.imageio.stream.ImageInputStream;
|
|
||||||
import javax.swing.*;
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.color.ColorSpace;
|
import java.awt.color.ColorSpace;
|
||||||
import java.awt.event.WindowAdapter;
|
import java.awt.event.WindowAdapter;
|
||||||
@@ -45,10 +37,27 @@ import java.awt.event.WindowEvent;
|
|||||||
import java.awt.image.*;
|
import java.awt.image.*;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.*;
|
import java.util.Map;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
|
import javax.imageio.IIOException;
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import javax.imageio.ImageReadParam;
|
||||||
|
import javax.imageio.ImageReader;
|
||||||
|
import javax.imageio.ImageTypeSpecifier;
|
||||||
|
import javax.imageio.spi.ImageReaderSpi;
|
||||||
|
import javax.imageio.stream.ImageInputStream;
|
||||||
|
import javax.swing.*;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.image.ImageUtil;
|
||||||
|
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||||
|
import com.twelvemonkeys.imageio.stream.SubImageInputStream;
|
||||||
|
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
||||||
|
import com.twelvemonkeys.util.WeakWeakMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ImageReader for Microsoft Windows ICO (icon) format.
|
* ImageReader for Microsoft Windows ICO (icon) format.
|
||||||
@@ -233,8 +242,9 @@ abstract class DIBImageReader extends ImageReaderBase {
|
|||||||
ImageReader pngReader = getPNGReader();
|
ImageReader pngReader = getPNGReader();
|
||||||
|
|
||||||
imageInput.seek(pEntry.getOffset());
|
imageInput.seek(pEntry.getOffset());
|
||||||
InputStream inputStream = IIOUtil.createStreamAdapter(imageInput, pEntry.getSize());
|
// InputStream inputStream = IIOUtil.createStreamAdapter(imageInput, pEntry.getSize());
|
||||||
ImageInputStream stream = ImageIO.createImageInputStream(inputStream);
|
// ImageInputStream stream = ImageIO.createImageInputStream(inputStream);
|
||||||
|
ImageInputStream stream = new SubImageInputStream(imageInput, pEntry.getSize());
|
||||||
|
|
||||||
// NOTE: Will throw IOException on later reads if input is not PNG
|
// NOTE: Will throw IOException on later reads if input is not PNG
|
||||||
pngReader.setInput(stream);
|
pngReader.setInput(stream);
|
||||||
@@ -284,8 +294,8 @@ abstract class DIBImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Support this, it's already in the BMP reader, spec allows RLE4 and RLE8
|
// TODO: Support this, it's already in the BMP reader, spec allows RLE4 and RLE8
|
||||||
if (header.getCompression() != 0) {
|
if (header.getCompression() != DIB.COMPRESSION_RGB) {
|
||||||
descriptor = new BitmapUnsupported(pEntry, String.format("Unsupported compression: %d", header.getCompression()));
|
descriptor = new BitmapUnsupported(pEntry, header, String.format("Unsupported compression: %d", header.getCompression()));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
int bitCount = header.getBitCount();
|
int bitCount = header.getBitCount();
|
||||||
@@ -313,7 +323,7 @@ abstract class DIBImageReader extends ImageReaderBase {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
descriptor = new BitmapUnsupported(pEntry, String.format("Unsupported bit count %d", bitCount));
|
descriptor = new BitmapUnsupported(pEntry, header, String.format("Unsupported bit count %d", bitCount));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -353,7 +363,7 @@ abstract class DIBImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void readBitmapIndexed1(final BitmapIndexed pBitmap, final boolean pAsMask) throws IOException {
|
private void readBitmapIndexed1(final BitmapIndexed pBitmap, final boolean pAsMask) throws IOException {
|
||||||
int width = adjustToPadding(pBitmap.getWidth() >> 3);
|
int width = adjustToPadding((pBitmap.getWidth() + 7) >> 3);
|
||||||
byte[] row = new byte[width];
|
byte[] row = new byte[width];
|
||||||
|
|
||||||
for (int y = 0; y < pBitmap.getHeight(); y++) {
|
for (int y = 0; y < pBitmap.getHeight(); y++) {
|
||||||
@@ -387,7 +397,7 @@ abstract class DIBImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void readBitmapIndexed4(final BitmapIndexed pBitmap) throws IOException {
|
private void readBitmapIndexed4(final BitmapIndexed pBitmap) throws IOException {
|
||||||
int width = adjustToPadding(pBitmap.getWidth() >> 1);
|
int width = adjustToPadding((pBitmap.getWidth() + 1) >> 1);
|
||||||
byte[] row = new byte[width];
|
byte[] row = new byte[width];
|
||||||
|
|
||||||
for (int y = 0; y < pBitmap.getHeight(); y++) {
|
for (int y = 0; y < pBitmap.getHeight(); y++) {
|
||||||
@@ -455,13 +465,12 @@ abstract class DIBImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void readBitmap16(final BitmapDescriptor pBitmap) throws IOException {
|
private void readBitmap16(final BitmapDescriptor pBitmap) throws IOException {
|
||||||
// TODO: No idea if this actually works..
|
|
||||||
short[] pixels = new short[pBitmap.getWidth() * pBitmap.getHeight()];
|
short[] pixels = new short[pBitmap.getWidth() * pBitmap.getHeight()];
|
||||||
|
|
||||||
// TODO: Support TYPE_USHORT_565 and the RGB 444/ARGB 4444 layouts
|
// TODO: Support TYPE_USHORT_565 and the RGB 444/ARGB 4444 layouts
|
||||||
// Will create TYPE_USHORT_555
|
// Will create TYPE_USHORT_555
|
||||||
DirectColorModel cm = new DirectColorModel(16, 0x7C00, 0x03E0, 0x001F);
|
DirectColorModel cm = new DirectColorModel(16, 0x7C00, 0x03E0, 0x001F);
|
||||||
DataBuffer buffer = new DataBufferShort(pixels, pixels.length);
|
DataBuffer buffer = new DataBufferUShort(pixels, pixels.length);
|
||||||
WritableRaster raster = Raster.createPackedRaster(
|
WritableRaster raster = Raster.createPackedRaster(
|
||||||
buffer, pBitmap.getWidth(), pBitmap.getHeight(), pBitmap.getWidth(), cm.getMasks(), null
|
buffer, pBitmap.getWidth(), pBitmap.getHeight(), pBitmap.getWidth(), cm.getMasks(), null
|
||||||
);
|
);
|
||||||
@@ -699,10 +708,10 @@ abstract class DIBImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
button.setText("" + image.getWidth() + "x" +
|
button.setText(image.getWidth() + "x" +
|
||||||
image.getHeight() + ": "
|
image.getHeight() + ": "
|
||||||
+ ((image.getColorModel() instanceof IndexColorModel) ?
|
+ ((image.getColorModel() instanceof IndexColorModel) ?
|
||||||
"" + ((IndexColorModel) image.getColorModel()).getMapSize() :
|
String.valueOf(((IndexColorModel) image.getColorModel()).getMapSize()) :
|
||||||
"TrueColor"));
|
"TrueColor"));
|
||||||
|
|
||||||
pParent.add(button);
|
pParent.add(button);
|
||||||
|
|||||||
+116
@@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017, Harald Kuhr
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.imageio.ImageWriterBase;
|
||||||
|
import com.twelvemonkeys.imageio.plugins.bmp.DIBHeader.BitmapInfoHeader;
|
||||||
|
|
||||||
|
import javax.imageio.IIOException;
|
||||||
|
import javax.imageio.spi.ImageWriterSpi;
|
||||||
|
import java.awt.image.*;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DIBImageWriter
|
||||||
|
*/
|
||||||
|
abstract class DIBImageWriter extends ImageWriterBase {
|
||||||
|
|
||||||
|
DIBImageWriter(ImageWriterSpi provider) {
|
||||||
|
super(provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setOutput(Object output) {
|
||||||
|
super.setOutput(output);
|
||||||
|
if (imageOutput != null) {
|
||||||
|
imageOutput.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeDIBHeader(int infoHeaderSize, int width, int height, boolean isTopDown, int pixelSize, int compression) throws IOException {
|
||||||
|
switch (infoHeaderSize) {
|
||||||
|
case DIB.BITMAP_INFO_HEADER_SIZE:
|
||||||
|
BitmapInfoHeader header = new BitmapInfoHeader();
|
||||||
|
// TODO: Consider a constructor/factory for this
|
||||||
|
header.width = width;
|
||||||
|
header.height = height;
|
||||||
|
header.topDown = isTopDown;
|
||||||
|
|
||||||
|
header.planes = 1; // Always 1 plane
|
||||||
|
header.bitCount = pixelSize;
|
||||||
|
header.compression = compression;
|
||||||
|
|
||||||
|
header.colorsUsed = 0; // Means 2 ^ bitCount
|
||||||
|
header.colorsImportant = 0; // Means all colors important
|
||||||
|
|
||||||
|
header.imageSize = header.height * ((header.width * header.bitCount + 31) / 32) * 4; // Rows padded to 32 bit
|
||||||
|
|
||||||
|
header.xPixelsPerMeter = 2835; // 72 DPI
|
||||||
|
header.yPixelsPerMeter = 2835;
|
||||||
|
|
||||||
|
header.write(imageOutput);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IIOException("Unsupported header size: " + infoHeaderSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeUncompressed(boolean isTopDown, BufferedImage img, int height, int width) throws IOException {
|
||||||
|
if (img.getType() != BufferedImage.TYPE_4BYTE_ABGR) {
|
||||||
|
throw new IIOException("Only TYPE_4BYTE_ABGR supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Support
|
||||||
|
// - TODO: IndexColorModel (ucompressed, RLE4, RLE8 or BI_PNG)
|
||||||
|
// - TODO: ComponentColorModel (1 channel gray, 3 channel BGR and 4 channel BGRA, uncompressed and RLE8? BI_BITFIELDS? BI_PNG? BI_JPEG?)
|
||||||
|
// - TODO: Packed/DirectColorModel (16 and 32 bit, BI_BITFIELDS, BI_PNG? BI_JPEG?)
|
||||||
|
|
||||||
|
Raster raster = img.getRaster();
|
||||||
|
WritableRaster rowRaster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, width, 1, width * 4, 4, new int[] {2, 1, 0, 3}, null);
|
||||||
|
byte[] row = ((DataBufferByte) rowRaster.getDataBuffer()).getData();
|
||||||
|
final int[] bandList = {2, 1, 0, 3};
|
||||||
|
|
||||||
|
for (int i = 0; i < height; i++) {
|
||||||
|
int line = isTopDown ? i : height - 1 - i;
|
||||||
|
rowRaster.setDataElements(0, 0, raster.createChild(0, line, width, 1, 0, 0, bandList));
|
||||||
|
|
||||||
|
imageOutput.write(row);
|
||||||
|
|
||||||
|
if (abortRequested()) {
|
||||||
|
processWriteAborted();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
processImageProgress(100f * i / (float) height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+21
-19
@@ -4,26 +4,28 @@
|
|||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* 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
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
* list of conditions and the following disclaimer.
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
*
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
* and/or other materials provided with the distribution.
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
*
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
* contributors may be used to endorse or promote products derived from
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
* this software without specific prior written permission.
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||||
|
|||||||
+52
-24
@@ -4,33 +4,38 @@
|
|||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* 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
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
* list of conditions and the following disclaimer.
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
*
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
* and/or other materials provided with the distribution.
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
*
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
* contributors may be used to endorse or promote products derived from
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
* this software without specific prior written permission.
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||||
|
|
||||||
import javax.imageio.IIOException;
|
import javax.imageio.IIOException;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
import java.awt.image.ColorModel;
|
||||||
|
import java.awt.image.IndexColorModel;
|
||||||
import java.io.DataInput;
|
import java.io.DataInput;
|
||||||
|
import java.io.DataOutput;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -42,13 +47,13 @@ import java.io.IOException;
|
|||||||
* @see <a href="http://en.wikipedia.org/wiki/ICO_(icon_image_file_format)#Directory">Wikipedia</a>
|
* @see <a href="http://en.wikipedia.org/wiki/ICO_(icon_image_file_format)#Directory">Wikipedia</a>
|
||||||
*/
|
*/
|
||||||
abstract class DirectoryEntry {
|
abstract class DirectoryEntry {
|
||||||
private int width;
|
int width;
|
||||||
private int height;
|
int height;
|
||||||
private int colorCount;
|
int colorCount;
|
||||||
int planes;
|
int planes;
|
||||||
int bitCount;
|
int bitCount;
|
||||||
private int size;
|
int size;
|
||||||
private int offset;
|
int offset;
|
||||||
|
|
||||||
DirectoryEntry() {
|
DirectoryEntry() {
|
||||||
}
|
}
|
||||||
@@ -96,6 +101,18 @@ abstract class DirectoryEntry {
|
|||||||
offset = pStream.readInt();
|
offset = pStream.readInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void write(final DataOutput output) throws IOException {
|
||||||
|
output.writeByte(width % 256);
|
||||||
|
output.writeByte(height % 256);
|
||||||
|
output.writeByte(colorCount);
|
||||||
|
output.writeByte(0); // Reserved
|
||||||
|
output.writeShort(1); // Color planes 0 or 1
|
||||||
|
output.writeShort(bitCount);
|
||||||
|
output.writeInt(size); // Size, depends on compression...
|
||||||
|
output.writeInt(offset);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return String.format(
|
return String.format(
|
||||||
"%s: width: %d, height: %d, colors: %d, planes: %d, bit count: %d, size: %d, offset: %d",
|
"%s: width: %d, height: %d, colors: %d, planes: %d, bit count: %d, size: %d, offset: %d",
|
||||||
@@ -160,5 +177,16 @@ abstract class DirectoryEntry {
|
|||||||
* Icon directory entry.
|
* Icon directory entry.
|
||||||
*/
|
*/
|
||||||
static final class ICOEntry extends DirectoryEntry {
|
static final class ICOEntry extends DirectoryEntry {
|
||||||
|
private ICOEntry() {}
|
||||||
|
|
||||||
|
ICOEntry(final int width, final int height, final ColorModel colorModel, int size, final int offset) {
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
this.colorCount = colorModel instanceof IndexColorModel ? ((IndexColorModel) colorModel).getMapSize() : 0;
|
||||||
|
this.planes = 1;
|
||||||
|
this.bitCount = colorModel.getPixelSize();
|
||||||
|
this.size = size;
|
||||||
|
this.offset = offset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+21
-20
@@ -4,26 +4,28 @@
|
|||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* 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
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
* list of conditions and the following disclaimer.
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
*
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
* and/or other materials provided with the distribution.
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
*
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
* contributors may be used to endorse or promote products derived from
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
* this software without specific prior written permission.
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||||
@@ -47,5 +49,4 @@ public final class ICOImageReader extends DIBImageReader {
|
|||||||
protected ICOImageReader(final ImageReaderSpi pProvider) {
|
protected ICOImageReader(final ImageReaderSpi pProvider) {
|
||||||
super(pProvider);
|
super(pProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-19
@@ -4,26 +4,28 @@
|
|||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* 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
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
* list of conditions and the following disclaimer.
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
*
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
* and/or other materials provided with the distribution.
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
*
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
* contributors may be used to endorse or promote products derived from
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
* this software without specific prior written permission.
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||||
|
|||||||
+49
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017, Harald Kuhr
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||||
|
|
||||||
|
import javax.imageio.ImageWriteParam;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ICOImageWriteParam
|
||||||
|
*/
|
||||||
|
public final class ICOImageWriteParam extends ImageWriteParam {
|
||||||
|
public ICOImageWriteParam(final Locale locale) {
|
||||||
|
super(locale);
|
||||||
|
|
||||||
|
// The names are consistent with those from the BMPImageWriteParam
|
||||||
|
compressionTypes = new String[] {"BI_RGB", "BI_RLE8", "BI_RLE4", "BI_PNG"};
|
||||||
|
compressionType = compressionTypes[DIB.COMPRESSION_RGB];
|
||||||
|
|
||||||
|
canWriteCompressed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
+327
@@ -0,0 +1,327 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017, Harald Kuhr
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.imageio.stream.SubImageOutputStream;
|
||||||
|
import com.twelvemonkeys.imageio.util.ProgressListenerBase;
|
||||||
|
|
||||||
|
import javax.imageio.*;
|
||||||
|
import javax.imageio.event.IIOWriteWarningListener;
|
||||||
|
import javax.imageio.metadata.IIOMetadata;
|
||||||
|
import javax.imageio.spi.ImageWriterSpi;
|
||||||
|
import javax.imageio.stream.ImageInputStream;
|
||||||
|
import javax.imageio.stream.ImageOutputStream;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.awt.image.ColorModel;
|
||||||
|
import java.awt.image.RenderedImage;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import static com.twelvemonkeys.imageio.plugins.bmp.DirectoryEntry.ICOEntry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ImageWriter implementation for Windows Icon (ICO) format.
|
||||||
|
*/
|
||||||
|
public final class ICOImageWriter extends DIBImageWriter {
|
||||||
|
|
||||||
|
// TODO: Support appending/updating an existing ICO file?
|
||||||
|
// - canInsertImage/canRemoveImage
|
||||||
|
|
||||||
|
private static final int ENTRY_SIZE = 16;
|
||||||
|
private static final int ICO_MAX_DIMENSION = 256;
|
||||||
|
private static final int INITIAL_ENTRY_COUNT = 8;
|
||||||
|
|
||||||
|
private int sequenceIndex = -1;
|
||||||
|
|
||||||
|
private ImageWriter pngDelegate;
|
||||||
|
|
||||||
|
protected ICOImageWriter(final ImageWriterSpi provider) {
|
||||||
|
super(provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void resetMembers() {
|
||||||
|
sequenceIndex = -1;
|
||||||
|
|
||||||
|
if (pngDelegate != null) {
|
||||||
|
pngDelegate.dispose();
|
||||||
|
pngDelegate = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IIOMetadata getDefaultImageMetadata(final ImageTypeSpecifier imageType, final ImageWriteParam param) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IIOMetadata convertImageMetadata(final IIOMetadata inData, final ImageTypeSpecifier imageType, final ImageWriteParam param) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(final IIOMetadata streamMetadata, final IIOImage image, final ImageWriteParam param) throws IOException {
|
||||||
|
prepareWriteSequence(streamMetadata);
|
||||||
|
writeToSequence(image, param);
|
||||||
|
endWriteSequence();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canWriteSequence() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void prepareWriteSequence(final IIOMetadata streamMetadata) throws IOException {
|
||||||
|
assertOutput();
|
||||||
|
|
||||||
|
if (sequenceIndex >= 0) {
|
||||||
|
throw new IllegalStateException("writeSequence already started");
|
||||||
|
}
|
||||||
|
|
||||||
|
writeICOHeader();
|
||||||
|
|
||||||
|
// Count: Needs to be updated for each new image
|
||||||
|
imageOutput.writeShort(0);
|
||||||
|
sequenceIndex = 0;
|
||||||
|
|
||||||
|
// TODO: Allow passing the initial size of the directory in the stream metadata?
|
||||||
|
// - as this is much more efficient than growing...
|
||||||
|
// How do we update the "image directory" containing "image entries",
|
||||||
|
// and which must be written *before* the image data?
|
||||||
|
// - Allocate a block of N * 16 bytes
|
||||||
|
// - If image count % N > N, we need to move the first image backwards in the file and allocate another N items...
|
||||||
|
imageOutput.write(new byte[INITIAL_ENTRY_COUNT * ENTRY_SIZE]); // Allocate room for 8 entries for now
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endWriteSequence() throws IOException {
|
||||||
|
assertOutput();
|
||||||
|
|
||||||
|
if (sequenceIndex < 0) {
|
||||||
|
throw new IllegalStateException("prepareWriteSequence not called");
|
||||||
|
}
|
||||||
|
|
||||||
|
sequenceIndex = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToSequence(final IIOImage image, final ImageWriteParam param) throws IOException {
|
||||||
|
assertOutput();
|
||||||
|
|
||||||
|
if (sequenceIndex < 0) {
|
||||||
|
throw new IllegalStateException("prepareWriteSequence not called");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (image.hasRaster()) {
|
||||||
|
throw new UnsupportedOperationException("Raster not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sequenceIndex >= INITIAL_ENTRY_COUNT) {
|
||||||
|
growIfNecessary();
|
||||||
|
}
|
||||||
|
|
||||||
|
int width = image.getRenderedImage().getWidth();
|
||||||
|
int height = image.getRenderedImage().getHeight();
|
||||||
|
ColorModel colorModel = image.getRenderedImage().getColorModel();
|
||||||
|
|
||||||
|
// TODO: The output size may depend on the param (subsampling, source region, etc)
|
||||||
|
if (width > ICO_MAX_DIMENSION || height > ICO_MAX_DIMENSION) {
|
||||||
|
throw new IIOException(String.format("ICO maximum width or height (%d) exceeded", ICO_MAX_DIMENSION));
|
||||||
|
}
|
||||||
|
|
||||||
|
long imageOffset = imageOutput.getStreamPosition();
|
||||||
|
|
||||||
|
if (imageOffset > Integer.MAX_VALUE) {
|
||||||
|
throw new IIOException("ICO file too large");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uncompressed, RLE4/RLE8 or PNG compressed
|
||||||
|
boolean pngCompression = param != null && "BI_PNG".equals(param.getCompressionType());
|
||||||
|
|
||||||
|
processImageStarted(sequenceIndex);
|
||||||
|
|
||||||
|
if (pngCompression) {
|
||||||
|
// NOTE: Embedding a PNG in a ICO is slightly different than a BMP with BI_PNG compression,
|
||||||
|
// so we'll just handle it directly
|
||||||
|
ImageWriter writer = getPNGDelegate();
|
||||||
|
writer.setOutput(new SubImageOutputStream(imageOutput));
|
||||||
|
writer.write(null, image, copyParam(param, writer));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
RenderedImage img = image.getRenderedImage();
|
||||||
|
// ICO needs height to include height of mask, even if mask isn't written
|
||||||
|
writeDIBHeader(DIB.BITMAP_INFO_HEADER_SIZE, img.getWidth(), img.getHeight() * 2,
|
||||||
|
false, img.getColorModel().getPixelSize(), DIB.COMPRESSION_RGB);
|
||||||
|
writeUncompressed(false, (BufferedImage) img, img.getWidth(), img.getHeight());
|
||||||
|
// TODO: Write mask
|
||||||
|
imageOutput.write(new byte[((width * height + 31) / 32) * 4]);
|
||||||
|
// writeUncompressed(false, new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY), img.getWidth(), img.getHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
processImageComplete();
|
||||||
|
|
||||||
|
long nextPosition = imageOutput.getStreamPosition();
|
||||||
|
|
||||||
|
// Update count
|
||||||
|
imageOutput.seek(4);
|
||||||
|
imageOutput.writeShort(sequenceIndex + 1);
|
||||||
|
|
||||||
|
// Write entry
|
||||||
|
int entryPosition = 6 + sequenceIndex * ENTRY_SIZE;
|
||||||
|
imageOutput.seek(entryPosition);
|
||||||
|
|
||||||
|
long size = nextPosition - imageOffset;
|
||||||
|
writeEntry(width, height, colorModel, (int) size, (int) imageOffset);
|
||||||
|
|
||||||
|
sequenceIndex++;
|
||||||
|
|
||||||
|
imageOutput.seek(nextPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeICOHeader() throws IOException {
|
||||||
|
if (imageOutput.getStreamPosition() != 0) {
|
||||||
|
throw new IllegalStateException("Stream already written to");
|
||||||
|
}
|
||||||
|
|
||||||
|
imageOutput.writeShort(0);
|
||||||
|
imageOutput.writeShort(DIB.TYPE_ICO);
|
||||||
|
imageOutput.flushBefore(imageOutput.getStreamPosition());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void growIfNecessary() {
|
||||||
|
// TODO: Allow growing the directory index...
|
||||||
|
// Move the first icon to the back, update offset
|
||||||
|
throw new IllegalStateException(String.format("Maximum number of icons supported (%d) exceeded", INITIAL_ENTRY_COUNT));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ImageWriteParam getDefaultWriteParam() {
|
||||||
|
return new ICOImageWriteParam(getLocale());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ImageWriteParam copyParam(final ImageWriteParam param, ImageWriter writer) {
|
||||||
|
if (param == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageWriteParam writeParam = writer.getDefaultWriteParam();
|
||||||
|
writeParam.setSourceSubsampling(param.getSourceXSubsampling(), param.getSourceYSubsampling(), param.getSubsamplingXOffset(), param.getSubsamplingYOffset());
|
||||||
|
writeParam.setSourceRegion(param.getSourceRegion());
|
||||||
|
writeParam.setSourceBands(param.getSourceBands());
|
||||||
|
|
||||||
|
return writeParam;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ImageWriter getPNGDelegate() {
|
||||||
|
if (pngDelegate == null) {
|
||||||
|
// There's always a PNG writer...
|
||||||
|
pngDelegate = ImageIO.getImageWritersByFormatName("PNG").next();
|
||||||
|
pngDelegate.setLocale(getLocale());
|
||||||
|
pngDelegate.addIIOWriteProgressListener(new ProgressListenerBase() {
|
||||||
|
@Override
|
||||||
|
public void imageProgress(ImageWriter source, float percentageDone) {
|
||||||
|
processImageProgress(percentageDone);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeAborted(ImageWriter source) {
|
||||||
|
processWriteAborted();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
pngDelegate.addIIOWriteWarningListener(new IIOWriteWarningListener() {
|
||||||
|
@Override
|
||||||
|
public void warningOccurred(ImageWriter source, int imageIndex, String warning) {
|
||||||
|
processWarningOccurred(sequenceIndex, warning);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return pngDelegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeEntry(final int width, final int height, final ColorModel colorModel, int size, final int offset) throws IOException {
|
||||||
|
new ICOEntry(width, height, colorModel, size, offset)
|
||||||
|
.write(imageOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws IOException {
|
||||||
|
boolean pngCompression = false;
|
||||||
|
int firstArg = 0;
|
||||||
|
|
||||||
|
while (args.length > firstArg && args[firstArg].charAt(0) == '-') {
|
||||||
|
if (args[firstArg].equals("-p") || args[firstArg].equals("--png")) {
|
||||||
|
pngCompression = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
firstArg++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.length - firstArg < 2) {
|
||||||
|
System.err.println("Usage: command [-p|--png] <output.ico> <input> [<input>...]");
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
try (ImageOutputStream out = ImageIO.createImageOutputStream(new File(args[firstArg++]))) {
|
||||||
|
ImageWriter writer = new ICOImageWriter(null);
|
||||||
|
writer.setOutput(out);
|
||||||
|
|
||||||
|
ImageWriteParam param = writer.getDefaultWriteParam();
|
||||||
|
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
|
||||||
|
param.setCompressionType(pngCompression ? "BI_PNG" : "BI_RGB");
|
||||||
|
|
||||||
|
writer.prepareWriteSequence(null);
|
||||||
|
|
||||||
|
for (int i = firstArg; i < args.length; i++) {
|
||||||
|
File inFile = new File(args[i]);
|
||||||
|
try (ImageInputStream input = ImageIO.createImageInputStream(inFile)) {
|
||||||
|
Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
|
||||||
|
|
||||||
|
if (!readers.hasNext()) {
|
||||||
|
System.err.printf("Can't read %s\n", inFile.getAbsolutePath());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ImageReader reader = readers.next();
|
||||||
|
reader.setInput(input);
|
||||||
|
for (int j = 0; j < reader.getNumImages(true); j++) {
|
||||||
|
IIOImage image = reader.readAll(j, null);
|
||||||
|
writer.writeToSequence(image, param);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.endWriteSequence();
|
||||||
|
writer.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+63
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017, Harald Kuhr
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.imageio.spi.ImageWriterSpiBase;
|
||||||
|
|
||||||
|
import javax.imageio.ImageTypeSpecifier;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ICOImageWriterSpi
|
||||||
|
*/
|
||||||
|
public final class ICOImageWriterSpi extends ImageWriterSpiBase {
|
||||||
|
public ICOImageWriterSpi() {
|
||||||
|
super(new ICOProviderInfo());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canEncodeImage(final ImageTypeSpecifier type) {
|
||||||
|
// TODO: Support more types, as time permits.
|
||||||
|
// NOTE: We do support more types, if writing using PNG compression
|
||||||
|
return type.getBufferedImageType() == BufferedImage.TYPE_4BYTE_ABGR;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ICOImageWriter createWriterInstance(final Object extension) {
|
||||||
|
return new ICOImageWriter(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription(final Locale locale) {
|
||||||
|
return "Windows Icon Format (ICO) Writer";
|
||||||
|
}
|
||||||
|
}
|
||||||
+30
-25
@@ -1,29 +1,31 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015, Harald Kuhr
|
* Copyright (c) 2017, Harald Kuhr
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* 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
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
* list of conditions and the following disclaimer.
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
*
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
* and/or other materials provided with the distribution.
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
*
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
* contributors may be used to endorse or promote products derived from
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
* this software without specific prior written permission.
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||||
@@ -31,14 +33,14 @@ package com.twelvemonkeys.imageio.plugins.bmp;
|
|||||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CURProviderInfo.
|
* ICOProviderInfo.
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @author last modified by $Author: harald.kuhr$
|
* @author last modified by $Author: harald.kuhr$
|
||||||
* @version $Id: CURProviderInfo.java,v 1.0 20/03/15 harald.kuhr Exp$
|
* @version $Id: ICOProviderInfo.java,v 1.0 20/03/15 harald.kuhr Exp$
|
||||||
*/
|
*/
|
||||||
final class ICOProviderInfo extends ReaderWriterProviderInfo {
|
final class ICOProviderInfo extends ReaderWriterProviderInfo {
|
||||||
protected ICOProviderInfo() {
|
ICOProviderInfo() {
|
||||||
super(
|
super(
|
||||||
ICOProviderInfo.class,
|
ICOProviderInfo.class,
|
||||||
new String[]{"ico", "ICO"},
|
new String[]{"ico", "ICO"},
|
||||||
@@ -50,9 +52,12 @@ final class ICOProviderInfo extends ReaderWriterProviderInfo {
|
|||||||
},
|
},
|
||||||
"com.twelvemonkeys.imageio.plugins.bmp.ICOImageReader",
|
"com.twelvemonkeys.imageio.plugins.bmp.ICOImageReader",
|
||||||
new String[] {"com.twelvemonkeys.imageio.plugins.bmp.ICOImageReaderSpi"},
|
new String[] {"com.twelvemonkeys.imageio.plugins.bmp.ICOImageReaderSpi"},
|
||||||
|
"com.twelvemonkeys.imageio.plugins.bmp.ICOImageWriter",
|
||||||
|
new String[] {"com.twelvemonkeys.imageio.plugins.bmp.ICOImageWriterSpi"},
|
||||||
|
false, null, null,
|
||||||
null, null,
|
null, null,
|
||||||
false, null, null, null, null,
|
true, null, null,
|
||||||
true, null, null, null, null
|
null, null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+26
-25
@@ -4,33 +4,34 @@
|
|||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* 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
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
* list of conditions and the following disclaimer.
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
*
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
* and/or other materials provided with the distribution.
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
*
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
* contributors may be used to endorse or promote products derived from
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
* this software without specific prior written permission.
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.io.InputStream;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -40,8 +41,8 @@ import java.util.Arrays;
|
|||||||
* @version $Id: RLE4Decoder.java#1 $
|
* @version $Id: RLE4Decoder.java#1 $
|
||||||
*/
|
*/
|
||||||
final class RLE4Decoder extends AbstractRLEDecoder {
|
final class RLE4Decoder extends AbstractRLEDecoder {
|
||||||
final static int BIT_MASKS[] = {0xf0, 0x0f};
|
final static int[] BIT_MASKS = {0xf0, 0x0f};
|
||||||
final static int BIT_SHIFTS[] = {4, 0};
|
final static int[] BIT_SHIFTS = {4, 0};
|
||||||
|
|
||||||
public RLE4Decoder(final int width) {
|
public RLE4Decoder(final int width) {
|
||||||
super(width, 4);
|
super(width, 4);
|
||||||
@@ -93,7 +94,7 @@ final class RLE4Decoder extends AbstractRLEDecoder {
|
|||||||
boolean paddingByte = (((byte2 + 1) / 2) % 2) != 0;
|
boolean paddingByte = (((byte2 + 1) / 2) % 2) != 0;
|
||||||
|
|
||||||
int packed = 0;
|
int packed = 0;
|
||||||
for (int i = 0; i < byte2; i++) {
|
for (int i = 0; i < byte2 && srcX / 2 < row.length; i++) {
|
||||||
if (i % 2 == 0) {
|
if (i % 2 == 0) {
|
||||||
packed = checkEOF(stream.read());
|
packed = checkEOF(stream.read());
|
||||||
}
|
}
|
||||||
@@ -110,7 +111,7 @@ final class RLE4Decoder extends AbstractRLEDecoder {
|
|||||||
else {
|
else {
|
||||||
// Encoded mode
|
// Encoded mode
|
||||||
// Replicate the two samples in byte2 as many times as byte1 says
|
// Replicate the two samples in byte2 as many times as byte1 says
|
||||||
for (int i = 0; i < byte1; i++) {
|
for (int i = 0; i < byte1 && srcX / 2 < row.length; i++) {
|
||||||
row[srcX / 2] |= (byte) (((byte2 & BIT_MASKS[i % 2]) >> BIT_SHIFTS[i % 2]) << BIT_SHIFTS[srcX % 2]);
|
row[srcX / 2] |= (byte) (((byte2 & BIT_MASKS[i % 2]) >> BIT_SHIFTS[i % 2]) << BIT_SHIFTS[srcX % 2]);
|
||||||
srcX++;
|
srcX++;
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-22
@@ -4,32 +4,34 @@
|
|||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* 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
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
* list of conditions and the following disclaimer.
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
*
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
* and/or other materials provided with the distribution.
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
*
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
* contributors may be used to endorse or promote products derived from
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
* this software without specific prior written permission.
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -92,7 +94,7 @@ final class RLE8Decoder extends AbstractRLEDecoder {
|
|||||||
// an additional padding byte is in the stream and must be skipped
|
// an additional padding byte is in the stream and must be skipped
|
||||||
boolean paddingByte = (byte2 % 2) != 0;
|
boolean paddingByte = (byte2 % 2) != 0;
|
||||||
|
|
||||||
while (byte2-- > 0) {
|
while (byte2-- > 0 && srcX < row.length) {
|
||||||
row[srcX++] = (byte) checkEOF(stream.read());
|
row[srcX++] = (byte) checkEOF(stream.read());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +107,7 @@ final class RLE8Decoder extends AbstractRLEDecoder {
|
|||||||
// Encoded mode
|
// Encoded mode
|
||||||
// Replicate byte2 as many times as byte1 says
|
// Replicate byte2 as many times as byte1 says
|
||||||
byte value = (byte) byte2;
|
byte value = (byte) byte2;
|
||||||
while (byte1-- > 0) {
|
while (byte1-- > 0 && srcX < row.length) {
|
||||||
row[srcX++] = value;
|
row[srcX++] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+29
@@ -1,3 +1,32 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2017, Harald Kuhr
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions are met:
|
||||||
|
#
|
||||||
|
# * Redistributions of source code must retain the above copyright notice, this
|
||||||
|
# list of conditions and the following disclaimer.
|
||||||
|
#
|
||||||
|
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
# this list of conditions and the following disclaimer in the documentation
|
||||||
|
# and/or other materials provided with the distribution.
|
||||||
|
#
|
||||||
|
# * Neither the name of the copyright holder nor the names of its
|
||||||
|
# contributors may be used to endorse or promote products derived from
|
||||||
|
# this software without specific prior written permission.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
com.twelvemonkeys.imageio.plugins.bmp.BMPImageReaderSpi
|
com.twelvemonkeys.imageio.plugins.bmp.BMPImageReaderSpi
|
||||||
com.twelvemonkeys.imageio.plugins.bmp.CURImageReaderSpi
|
com.twelvemonkeys.imageio.plugins.bmp.CURImageReaderSpi
|
||||||
com.twelvemonkeys.imageio.plugins.bmp.ICOImageReaderSpi
|
com.twelvemonkeys.imageio.plugins.bmp.ICOImageReaderSpi
|
||||||
|
|||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2017, Harald Kuhr
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions are met:
|
||||||
|
#
|
||||||
|
# * Redistributions of source code must retain the above copyright notice, this
|
||||||
|
# list of conditions and the following disclaimer.
|
||||||
|
#
|
||||||
|
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
# this list of conditions and the following disclaimer in the documentation
|
||||||
|
# and/or other materials provided with the distribution.
|
||||||
|
#
|
||||||
|
# * Neither the name of the copyright holder nor the names of its
|
||||||
|
# contributors may be used to endorse or promote products derived from
|
||||||
|
# this software without specific prior written permission.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
com.twelvemonkeys.imageio.plugins.bmp.BMPImageWriterSpi
|
||||||
|
com.twelvemonkeys.imageio.plugins.bmp.ICOImageWriterSpi
|
||||||
+97
-53
@@ -1,32 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009, Harald Kuhr
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||||
|
|
||||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
import static org.junit.Assert.*;
|
||||||
|
import static org.junit.Assume.assumeNoException;
|
||||||
|
import static org.mockito.Matchers.anyInt;
|
||||||
|
import static org.mockito.Matchers.eq;
|
||||||
|
import static org.mockito.Mockito.atLeastOnce;
|
||||||
|
import static org.mockito.Mockito.inOrder;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.imageio.IIOException;
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import javax.imageio.ImageReadParam;
|
||||||
|
import javax.imageio.ImageReader;
|
||||||
|
import javax.imageio.ImageTypeSpecifier;
|
||||||
|
import javax.imageio.event.IIOReadProgressListener;
|
||||||
|
import javax.imageio.metadata.IIOMetadata;
|
||||||
|
import javax.imageio.metadata.IIOMetadataNode;
|
||||||
|
import javax.imageio.spi.ImageReaderSpi;
|
||||||
|
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mockito.InOrder;
|
import org.mockito.InOrder;
|
||||||
import org.w3c.dom.Node;
|
import org.w3c.dom.Node;
|
||||||
import org.w3c.dom.NodeList;
|
import org.w3c.dom.NodeList;
|
||||||
|
|
||||||
import javax.imageio.*;
|
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||||
import javax.imageio.event.IIOReadProgressListener;
|
import com.twelvemonkeys.xml.XMLSerializer;
|
||||||
import javax.imageio.metadata.IIOMetadata;
|
|
||||||
import javax.imageio.metadata.IIOMetadataNode;
|
|
||||||
import javax.imageio.spi.ImageReaderSpi;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.image.BufferedImage;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.reflect.Constructor;
|
|
||||||
import java.net.URISyntaxException;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
|
||||||
import static org.junit.Assume.assumeNoException;
|
|
||||||
import static org.mockito.Matchers.anyInt;
|
|
||||||
import static org.mockito.Matchers.eq;
|
|
||||||
import static org.mockito.Mockito.*;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BMPImageReaderTest
|
* BMPImageReaderTest
|
||||||
@@ -36,6 +76,12 @@ import static org.mockito.Mockito.*;
|
|||||||
* @version $Id: BMPImageReaderTest.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$
|
* @version $Id: BMPImageReaderTest.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$
|
||||||
*/
|
*/
|
||||||
public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader> {
|
public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader> {
|
||||||
|
@Override
|
||||||
|
protected ImageReaderSpi createProvider() {
|
||||||
|
return new BMPImageReaderSpi();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
protected List<TestData> getTestData() {
|
protected List<TestData> getTestData() {
|
||||||
return Arrays.asList(
|
return Arrays.asList(
|
||||||
// BMP Suite "Good"
|
// BMP Suite "Good"
|
||||||
@@ -112,27 +158,17 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ImageReaderSpi createProvider() {
|
|
||||||
return new BMPImageReaderSpi();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected BMPImageReader createReader() {
|
|
||||||
return new BMPImageReader(createProvider());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Class<BMPImageReader> getReaderClass() {
|
|
||||||
return BMPImageReader.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected List<String> getFormatNames() {
|
protected List<String> getFormatNames() {
|
||||||
return Collections.singletonList("bmp");
|
return Collections.singletonList("bmp");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
protected List<String> getSuffixes() {
|
protected List<String> getSuffixes() {
|
||||||
return Arrays.asList("bmp", "rle");
|
return Arrays.asList("bmp", "rle");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
protected List<String> getMIMETypes() {
|
protected List<String> getMIMETypes() {
|
||||||
return Collections.singletonList("image/bmp");
|
return Collections.singletonList("image/bmp");
|
||||||
}
|
}
|
||||||
@@ -146,7 +182,7 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
|
|||||||
|
|
||||||
ImageTypeSpecifier rawType = reader.getRawImageType(0);
|
ImageTypeSpecifier rawType = reader.getRawImageType(0);
|
||||||
|
|
||||||
// As the JPEGImageReader we delegate to returns null for YCbCr, we'll have to do the same
|
// As the JPEGImageReader we delegate to may return null for YCbCr, we'll have to do the same
|
||||||
if (rawType == null && data.getInput().toString().contains("jpeg")) {
|
if (rawType == null && data.getInput().toString().contains("jpeg")) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -241,7 +277,7 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddIIOReadProgressListenerCallbacksJPEG() {
|
public void testAddIIOReadProgressListenerCallbacksJPEG() throws IOException {
|
||||||
ImageReader reader = createReader();
|
ImageReader reader = createReader();
|
||||||
TestData data = new TestData(getClassLoaderResource("/bmpsuite/q/rgb24jpeg.bmp"), new Dimension(127, 64));
|
TestData data = new TestData(getClassLoaderResource("/bmpsuite/q/rgb24jpeg.bmp"), new Dimension(127, 64));
|
||||||
reader.setInput(data.getInputStream());
|
reader.setInput(data.getInputStream());
|
||||||
@@ -264,7 +300,7 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddIIOReadProgressListenerCallbacksPNG() {
|
public void testAddIIOReadProgressListenerCallbacksPNG() throws IOException {
|
||||||
ImageReader reader = createReader();
|
ImageReader reader = createReader();
|
||||||
TestData data = new TestData(getClassLoaderResource("/bmpsuite/q/rgb24png.bmp"), new Dimension(127, 64));
|
TestData data = new TestData(getClassLoaderResource("/bmpsuite/q/rgb24png.bmp"), new Dimension(127, 64));
|
||||||
reader.setInput(data.getInputStream());
|
reader.setInput(data.getInputStream());
|
||||||
@@ -287,7 +323,7 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMetadataEqualsJRE() throws IOException, URISyntaxException {
|
public void testMetadataEqualsJRE() throws IOException {
|
||||||
ImageReader jreReader;
|
ImageReader jreReader;
|
||||||
try {
|
try {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@@ -324,7 +360,7 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
|
|||||||
}
|
}
|
||||||
IIOMetadata jreMetadata = jreReader.getImageMetadata(0);
|
IIOMetadata jreMetadata = jreReader.getImageMetadata(0);
|
||||||
|
|
||||||
assertEquals(true, metadata.isStandardMetadataFormatSupported());
|
assertTrue(metadata.isStandardMetadataFormatSupported());
|
||||||
assertEquals(jreMetadata.getNativeMetadataFormatName(), metadata.getNativeMetadataFormatName());
|
assertEquals(jreMetadata.getNativeMetadataFormatName(), metadata.getNativeMetadataFormatName());
|
||||||
assertArrayEquals(jreMetadata.getExtraMetadataFormatNames(), metadata.getExtraMetadataFormatNames());
|
assertArrayEquals(jreMetadata.getExtraMetadataFormatNames(), metadata.getExtraMetadataFormatNames());
|
||||||
|
|
||||||
@@ -337,20 +373,20 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
|
|||||||
Node expectedTree = jreMetadata.getAsTree(format);
|
Node expectedTree = jreMetadata.getAsTree(format);
|
||||||
Node actualTree = metadata.getAsTree(format);
|
Node actualTree = metadata.getAsTree(format);
|
||||||
|
|
||||||
// try {
|
try {
|
||||||
assertNodeEquals(localPath + " - " + format, expectedTree, actualTree);
|
assertNodeEquals(localPath + " - " + format, expectedTree, actualTree);
|
||||||
// }
|
}
|
||||||
// catch (AssertionError e) {
|
catch (AssertionError e) {
|
||||||
// ByteArrayOutputStream expected = new ByteArrayOutputStream();
|
ByteArrayOutputStream expected = new ByteArrayOutputStream();
|
||||||
// ByteArrayOutputStream actual = new ByteArrayOutputStream();
|
ByteArrayOutputStream actual = new ByteArrayOutputStream();
|
||||||
//
|
|
||||||
// new XMLSerializer(expected, "UTF-8").serialize(expectedTree, false);
|
new XMLSerializer(expected, "UTF-8").serialize(expectedTree, false);
|
||||||
// new XMLSerializer(actual, "UTF-8").serialize(actualTree, false);
|
new XMLSerializer(actual, "UTF-8").serialize(actualTree, false);
|
||||||
//
|
|
||||||
// assertEquals(e.getMessage(), new String(expected.toByteArray(), "UTF-8"), new String(actual.toByteArray(), "UTF-8"));
|
assertEquals(e.getMessage(), new String(expected.toByteArray(), StandardCharsets.UTF_8), new String(actual.toByteArray(), StandardCharsets.UTF_8));
|
||||||
//
|
|
||||||
// throw e;
|
throw e;
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -372,13 +408,21 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
|
|||||||
NodeList expectedChildNodes = expected.getChildNodes();
|
NodeList expectedChildNodes = expected.getChildNodes();
|
||||||
NodeList actualChildNodes = actual.getChildNodes();
|
NodeList actualChildNodes = actual.getChildNodes();
|
||||||
|
|
||||||
assertEquals(message + " child length differs: " + toString(expectedChildNodes) + " != " + toString(actualChildNodes),
|
assertTrue(message + " child length differs: " + toString(expectedChildNodes) + " != " + toString(actualChildNodes),
|
||||||
expectedChildNodes.getLength(), actualChildNodes.getLength());
|
expectedChildNodes.getLength() <= actualChildNodes.getLength());
|
||||||
|
|
||||||
for (int i = 0; i < expectedChildNodes.getLength(); i++) {
|
for (int i = 0; i < expectedChildNodes.getLength(); i++) {
|
||||||
Node expectedChild = expectedChildNodes.item(i);
|
Node expectedChild = expectedChildNodes.item(i);
|
||||||
|
|
||||||
Node actualChild = actualChildNodes.item(i);
|
Node actualChild = actualChildNodes.item(i);
|
||||||
|
|
||||||
|
for (int j = 0; j < actualChildNodes.getLength(); j++) {
|
||||||
|
if (actualChildNodes.item(j).getLocalName().equals(expectedChild.getLocalName())) {
|
||||||
|
actualChild = actualChildNodes.item(j);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
assertEquals(message + " node name differs", expectedChild.getLocalName(), actualChild.getLocalName());
|
assertEquals(message + " node name differs", expectedChild.getLocalName(), actualChild.getLocalName());
|
||||||
assertNodeEquals(message + "/" + expectedChild.getLocalName(), expectedChild, actualChild);
|
assertNodeEquals(message + "/" + expectedChild.getLocalName(), expectedChild, actualChild);
|
||||||
}
|
}
|
||||||
|
|||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.imageio.util.ImageWriterAbstractTest;
|
||||||
|
|
||||||
|
import javax.imageio.spi.ImageWriterSpi;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.awt.image.RenderedImage;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BMPImageWriterTest.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
|
* @author last modified by : harald.kuhr$
|
||||||
|
* @version : BMPImageWriterTest.java,v 1.0 25/06/2020 harald.kuhr Exp$
|
||||||
|
*/
|
||||||
|
public class BMPImageWriterTest extends ImageWriterAbstractTest<BMPImageWriter> {
|
||||||
|
@Override
|
||||||
|
protected ImageWriterSpi createProvider() {
|
||||||
|
return new BMPImageWriterSpi();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<? extends RenderedImage> getTestData() {
|
||||||
|
return Collections.singletonList(
|
||||||
|
new BufferedImage(10, 10, BufferedImage.TYPE_4BYTE_ABGR)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
+30
@@ -1,3 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016, Harald Kuhr
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||||
|
|
||||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
||||||
|
|||||||
+40
-13
@@ -1,6 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009, Harald Kuhr
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||||
|
|
||||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||||
|
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@@ -23,6 +54,12 @@ import static org.junit.Assert.*;
|
|||||||
* @version $Id: CURImageReaderTest.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$
|
* @version $Id: CURImageReaderTest.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$
|
||||||
*/
|
*/
|
||||||
public class CURImageReaderTest extends ImageReaderAbstractTest<CURImageReader> {
|
public class CURImageReaderTest extends ImageReaderAbstractTest<CURImageReader> {
|
||||||
|
@Override
|
||||||
|
protected ImageReaderSpi createProvider() {
|
||||||
|
return new CURImageReaderSpi();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
protected List<TestData> getTestData() {
|
protected List<TestData> getTestData() {
|
||||||
return Arrays.asList(
|
return Arrays.asList(
|
||||||
new TestData(getClassLoaderResource("/cur/hand.cur"), new Dimension(32, 32)),
|
new TestData(getClassLoaderResource("/cur/hand.cur"), new Dimension(32, 32)),
|
||||||
@@ -30,27 +67,17 @@ public class CURImageReaderTest extends ImageReaderAbstractTest<CURImageReader>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ImageReaderSpi createProvider() {
|
|
||||||
return new CURImageReaderSpi();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected CURImageReader createReader() {
|
|
||||||
return new CURImageReader();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Class<CURImageReader> getReaderClass() {
|
|
||||||
return CURImageReader.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected List<String> getFormatNames() {
|
protected List<String> getFormatNames() {
|
||||||
return Collections.singletonList("cur");
|
return Collections.singletonList("cur");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
protected List<String> getSuffixes() {
|
protected List<String> getSuffixes() {
|
||||||
return Collections.singletonList("cur");
|
return Collections.singletonList("cur");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
protected List<String> getMIMETypes() {
|
protected List<String> getMIMETypes() {
|
||||||
return Arrays.asList("image/vnd.microsoft.cursor", "image/cursor", "image/x-cursor");
|
return Arrays.asList("image/vnd.microsoft.cursor", "image/cursor", "image/x-cursor");
|
||||||
}
|
}
|
||||||
@@ -69,7 +96,7 @@ public class CURImageReaderTest extends ImageReaderAbstractTest<CURImageReader>
|
|||||||
assertNotNull("Hotspot for cursor not present", hotspot);
|
assertNotNull("Hotspot for cursor not present", hotspot);
|
||||||
|
|
||||||
// Image weirdness
|
// Image weirdness
|
||||||
assertTrue("Hotspot for cursor undefined (java.awt.Image.UndefinedProperty)", Image.UndefinedProperty != hotspot);
|
assertNotSame("Hotspot for cursor undefined (java.awt.Image.UndefinedProperty)", Image.UndefinedProperty, hotspot);
|
||||||
|
|
||||||
assertTrue(String.format("Hotspot not a java.awt.Point: %s", hotspot.getClass()), hotspot instanceof Point);
|
assertTrue(String.format("Hotspot not a java.awt.Point: %s", hotspot.getClass()), hotspot instanceof Point);
|
||||||
assertEquals(pExpected, hotspot);
|
assertEquals(pExpected, hotspot);
|
||||||
|
|||||||
+30
@@ -1,3 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016, Harald Kuhr
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||||
|
|
||||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
||||||
|
|||||||
+39
-12
@@ -1,6 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009, Harald Kuhr
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||||
|
|
||||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||||
|
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@@ -19,6 +50,12 @@ import java.util.List;
|
|||||||
* @version $Id: ICOImageReaderTest.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$
|
* @version $Id: ICOImageReaderTest.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$
|
||||||
*/
|
*/
|
||||||
public class ICOImageReaderTest extends ImageReaderAbstractTest<ICOImageReader> {
|
public class ICOImageReaderTest extends ImageReaderAbstractTest<ICOImageReader> {
|
||||||
|
@Override
|
||||||
|
protected ImageReaderSpi createProvider() {
|
||||||
|
return new ICOImageReaderSpi();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
protected List<TestData> getTestData() {
|
protected List<TestData> getTestData() {
|
||||||
return Arrays.asList(
|
return Arrays.asList(
|
||||||
new TestData(
|
new TestData(
|
||||||
@@ -45,27 +82,17 @@ public class ICOImageReaderTest extends ImageReaderAbstractTest<ICOImageReader>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ImageReaderSpi createProvider() {
|
|
||||||
return new ICOImageReaderSpi();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ICOImageReader createReader() {
|
|
||||||
return new ICOImageReader();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Class<ICOImageReader> getReaderClass() {
|
|
||||||
return ICOImageReader.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected List<String> getFormatNames() {
|
protected List<String> getFormatNames() {
|
||||||
return Collections.singletonList("ico");
|
return Collections.singletonList("ico");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
protected List<String> getSuffixes() {
|
protected List<String> getSuffixes() {
|
||||||
return Collections.singletonList("ico");
|
return Collections.singletonList("ico");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
protected List<String> getMIMETypes() {
|
protected List<String> getMIMETypes() {
|
||||||
return Arrays.asList("image/vnd.microsoft.icon", "image/ico", "image/x-icon");
|
return Arrays.asList("image/vnd.microsoft.icon", "image/ico", "image/x-icon");
|
||||||
}
|
}
|
||||||
|
|||||||
+34
@@ -0,0 +1,34 @@
|
|||||||
|
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.imageio.util.ImageWriterAbstractTest;
|
||||||
|
|
||||||
|
import javax.imageio.spi.ImageWriterSpi;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.awt.image.RenderedImage;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ICOImageWriterTest.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
|
* @author last modified by : harald.kuhr$
|
||||||
|
* @version : ICOImageWriterTest.java,v 1.0 25/06/2020 harald.kuhr Exp$
|
||||||
|
*/
|
||||||
|
public class ICOImageWriterTest extends ImageWriterAbstractTest<ICOImageWriter> {
|
||||||
|
@Override
|
||||||
|
protected ImageWriterSpi createProvider() {
|
||||||
|
return new ICOImageWriterSpi();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<? extends RenderedImage> getTestData() {
|
||||||
|
return Arrays.asList(
|
||||||
|
new BufferedImage(8, 8, BufferedImage.TYPE_4BYTE_ABGR),
|
||||||
|
new BufferedImage(16, 16, BufferedImage.TYPE_4BYTE_ABGR),
|
||||||
|
new BufferedImage(32, 32, BufferedImage.TYPE_4BYTE_ABGR),
|
||||||
|
new BufferedImage(64, 64, BufferedImage.TYPE_4BYTE_ABGR),
|
||||||
|
new BufferedImage(128, 128, BufferedImage.TYPE_4BYTE_ABGR)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
+30
@@ -1,3 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016, Harald Kuhr
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||||
|
|
||||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
||||||
|
|||||||
+30
@@ -1,3 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009, Harald Kuhr
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||||
|
|
||||||
import com.twelvemonkeys.io.enc.Decoder;
|
import com.twelvemonkeys.io.enc.Decoder;
|
||||||
|
|||||||
+30
@@ -1,3 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009, Harald Kuhr
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||||
|
|
||||||
import com.twelvemonkeys.io.enc.Decoder;
|
import com.twelvemonkeys.io.enc.Decoder;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.4.4-SNAPSHOT</version>
|
<version>3.6.5-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-clippath</artifactId>
|
<artifactId>imageio-clippath</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: Photoshop Path Support</name>
|
<name>TwelveMonkeys :: ImageIO :: Photoshop Path Support</name>
|
||||||
@@ -21,6 +21,7 @@
|
|||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio-core</artifactId>
|
<artifactId>imageio-core</artifactId>
|
||||||
<type>test-jar</type>
|
<type>test-jar</type>
|
||||||
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
|
|||||||
Regular → Executable
+10
-188
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2014, Harald Kuhr
|
* Copyright (c) 2020, Harald Kuhr
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@@ -30,215 +30,37 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.imageio.path;
|
package com.twelvemonkeys.imageio.path;
|
||||||
|
|
||||||
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
|
|
||||||
|
|
||||||
import javax.imageio.IIOException;
|
|
||||||
import java.awt.geom.GeneralPath;
|
|
||||||
import java.awt.geom.Path2D;
|
import java.awt.geom.Path2D;
|
||||||
import java.io.DataInput;
|
import java.io.DataInput;
|
||||||
import java.io.EOFException;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static com.twelvemonkeys.lang.Validate.isTrue;
|
|
||||||
import static com.twelvemonkeys.lang.Validate.notNull;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@code Shape} object from an Adobe Photoshop Path resource.
|
* AdobePathBuilder.
|
||||||
*
|
*
|
||||||
* @see <a href="http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_17587">Adobe Photoshop Path resource format</a>
|
* @deprecated Use {@link AdobePathReader} instead. This class will be removed in a future release.
|
||||||
* @author <a href="mailto:jpalmer@itemmaster.com">Jason Palmer, itemMaster LLC</a>
|
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
|
||||||
*/
|
*/
|
||||||
public final class AdobePathBuilder {
|
public final class AdobePathBuilder {
|
||||||
final static boolean DEBUG = "true".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.imageio.path.debug"));
|
|
||||||
|
|
||||||
private final DataInput data;
|
private final AdobePathReader delegate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a path builder that will read its data from a {@code DataInput}, such as an
|
* @see AdobePathReader#AdobePathReader(DataInput)
|
||||||
* {@code ImageInputStream}.
|
|
||||||
* The data length is assumed to be a multiple of 26.
|
|
||||||
*
|
|
||||||
* @param data the input to read data from.
|
|
||||||
* @throws java.lang.IllegalArgumentException if {@code data} is {@code null}
|
|
||||||
*/
|
*/
|
||||||
public AdobePathBuilder(final DataInput data) {
|
public AdobePathBuilder(final DataInput data) {
|
||||||
notNull(data, "data");
|
this.delegate = new AdobePathReader(data);
|
||||||
this.data = data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a path builder that will read its data from a {@code byte} array.
|
* @see AdobePathReader#AdobePathReader(byte[])
|
||||||
* The array length must be a multiple of 26, and greater than 0.
|
|
||||||
*
|
|
||||||
* @param data the array to read data from.
|
|
||||||
* @throws java.lang.IllegalArgumentException if {@code data} is {@code null}, or not a multiple of 26.
|
|
||||||
*/
|
*/
|
||||||
public AdobePathBuilder(final byte[] data) {
|
public AdobePathBuilder(final byte[] data) {
|
||||||
this(new ByteArrayImageInputStream(
|
this.delegate = new AdobePathReader(data);
|
||||||
notNull(data, "data"), 0,
|
|
||||||
isTrue(data.length > 0 && data.length % 26 == 0, data.length, "data.length must be a multiple of 26: %d")
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds the path.
|
* @see AdobePathReader#readPath()
|
||||||
*
|
|
||||||
* @return the path
|
|
||||||
* @throws javax.imageio.IIOException if the input contains a bad path data.
|
|
||||||
* @throws IOException if a general I/O exception occurs during reading.
|
|
||||||
*/
|
*/
|
||||||
public Path2D path() throws IOException {
|
public Path2D path() throws IOException {
|
||||||
List<List<AdobePathSegment>> subPaths = new ArrayList<List<AdobePathSegment>>();
|
return delegate.readPath();
|
||||||
List<AdobePathSegment> currentPath = null;
|
|
||||||
int currentPathLength = 0;
|
|
||||||
|
|
||||||
AdobePathSegment segment;
|
|
||||||
while ((segment = nextSegment()) != null) {
|
|
||||||
|
|
||||||
if (DEBUG) {
|
|
||||||
System.out.println(segment);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (segment.selector == AdobePathSegment.OPEN_SUBPATH_LENGTH_RECORD || segment.selector == AdobePathSegment.CLOSED_SUBPATH_LENGTH_RECORD) {
|
|
||||||
if (currentPath != null) {
|
|
||||||
if (currentPathLength != currentPath.size()) {
|
|
||||||
throw new IIOException(String.format("Bad path, expected %d segments, found only %d", currentPathLength, currentPath.size()));
|
|
||||||
}
|
|
||||||
subPaths.add(currentPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
currentPath = new ArrayList<AdobePathSegment>(segment.length);
|
|
||||||
currentPathLength = segment.length;
|
|
||||||
}
|
|
||||||
else if (segment.selector == AdobePathSegment.OPEN_SUBPATH_BEZIER_LINKED
|
|
||||||
|| segment.selector == AdobePathSegment.OPEN_SUBPATH_BEZIER_UNLINKED
|
|
||||||
|| segment.selector == AdobePathSegment.CLOSED_SUBPATH_BEZIER_LINKED
|
|
||||||
|| segment.selector == AdobePathSegment.CLOSED_SUBPATH_BEZIER_UNLINKED) {
|
|
||||||
if (currentPath == null) {
|
|
||||||
throw new IIOException("Bad path, missing subpath length record");
|
|
||||||
}
|
|
||||||
if (currentPath.size() >= currentPathLength) {
|
|
||||||
throw new IIOException(String.format("Bad path, expected %d segments, found%d", currentPathLength, currentPath.size()));
|
|
||||||
}
|
|
||||||
|
|
||||||
currentPath.add(segment);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// now add the last one
|
|
||||||
if (currentPath != null) {
|
|
||||||
if (currentPathLength != currentPath.size()) {
|
|
||||||
throw new IIOException(String.format("Bad path, expected %d segments, found only %d", currentPathLength, currentPath.size()));
|
|
||||||
}
|
|
||||||
|
|
||||||
subPaths.add(currentPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
// now we have collected the PathPoints now create a Shape.
|
|
||||||
return pathToShape(subPaths);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Correct Order... P1, P2, P3, P4, P5, P6 (Closed) moveTo(P1)
|
|
||||||
* curveTo(P1.cpl, P2.cpp, P2.ap); curveTo(P2.cpl, P3.cppy, P3.ap);
|
|
||||||
* curveTo(P3.cpl, P4.cpp, P4.ap); curveTo(P4.cpl, P5.cpp, P5.ap);
|
|
||||||
* curveTo(P5.cply, P6.cpp, P6.ap); curveTo(P6.cpl, P1.cpp, P1.ap);
|
|
||||||
* closePath()
|
|
||||||
*/
|
|
||||||
private Path2D pathToShape(final List<List<AdobePathSegment>> paths) {
|
|
||||||
GeneralPath path = new GeneralPath(Path2D.WIND_EVEN_ODD, paths.size());
|
|
||||||
GeneralPath subpath = null;
|
|
||||||
|
|
||||||
for (List<AdobePathSegment> points : paths) {
|
|
||||||
int length = points.size();
|
|
||||||
|
|
||||||
for (int i = 0; i < points.size(); i++) {
|
|
||||||
AdobePathSegment current = points.get(i);
|
|
||||||
|
|
||||||
int step = i == 0 ? 0 : i == length - 1 ? 2 : 1;
|
|
||||||
|
|
||||||
switch (step) {
|
|
||||||
// begin
|
|
||||||
case 0: {
|
|
||||||
subpath = new GeneralPath(Path2D.WIND_EVEN_ODD, length);
|
|
||||||
subpath.moveTo(current.apx, current.apy);
|
|
||||||
|
|
||||||
if (length > 1) {
|
|
||||||
AdobePathSegment next = points.get((i + 1));
|
|
||||||
subpath.curveTo(current.cplx, current.cply, next.cppx, next.cppy, next.apx, next.apy);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
subpath.lineTo(current.apx, current.apy);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// middle
|
|
||||||
case 1: {
|
|
||||||
AdobePathSegment next = points.get((i + 1)); // we are always guaranteed one more.
|
|
||||||
subpath.curveTo(current.cplx, current.cply, next.cppx, next.cppy, next.apx, next.apy);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// end
|
|
||||||
case 2: {
|
|
||||||
AdobePathSegment first = points.get(0);
|
|
||||||
|
|
||||||
if (first.selector == AdobePathSegment.CLOSED_SUBPATH_BEZIER_LINKED || first.selector == AdobePathSegment.CLOSED_SUBPATH_BEZIER_UNLINKED) {
|
|
||||||
subpath.curveTo(current.cplx, current.cply, first.cppx, first.cppy, first.apx, first.apy);
|
|
||||||
subpath.closePath();
|
|
||||||
path.append(subpath, false);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
subpath.lineTo(current.apx, current.apy);
|
|
||||||
path.append(subpath, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
private AdobePathSegment nextSegment() throws IOException {
|
|
||||||
// Each segment is 26 bytes
|
|
||||||
int selector;
|
|
||||||
try {
|
|
||||||
selector = data.readUnsignedShort();
|
|
||||||
}
|
|
||||||
catch (EOFException eof) {
|
|
||||||
// No more data, we're done
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Spec says Fill rule is ignored by Photoshop... Probably not.. ;-)
|
|
||||||
// TODO: Replace with switch + handle all types!
|
|
||||||
// TODO: ...or Move logic to AdobePathSegment?
|
|
||||||
if (selector == AdobePathSegment.CLOSED_SUBPATH_LENGTH_RECORD || selector == AdobePathSegment.OPEN_SUBPATH_LENGTH_RECORD) {
|
|
||||||
int size = data.readUnsignedShort();
|
|
||||||
// data.position(data.position() + 22); // Skip remaining
|
|
||||||
data.skipBytes(22);
|
|
||||||
return new AdobePathSegment(selector, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new AdobePathSegment(
|
|
||||||
selector,
|
|
||||||
readFixedPoint(data.readInt()),
|
|
||||||
readFixedPoint(data.readInt()),
|
|
||||||
readFixedPoint(data.readInt()),
|
|
||||||
readFixedPoint(data.readInt()),
|
|
||||||
readFixedPoint(data.readInt()),
|
|
||||||
readFixedPoint(data.readInt())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static double readFixedPoint(final int fixed) {
|
|
||||||
return ((double) fixed / 0x1000000);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Executable
+243
@@ -0,0 +1,243 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014-2020, Harald Kuhr
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.twelvemonkeys.imageio.path;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
|
||||||
|
|
||||||
|
import javax.imageio.IIOException;
|
||||||
|
import java.awt.geom.Path2D;
|
||||||
|
import java.io.DataInput;
|
||||||
|
import java.io.EOFException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static com.twelvemonkeys.lang.Validate.isTrue;
|
||||||
|
import static com.twelvemonkeys.lang.Validate.notNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads a {@code Shape} object from an Adobe Photoshop Path resource.
|
||||||
|
*
|
||||||
|
* @see <a href="http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_17587">Adobe Photoshop Path resource format</a>
|
||||||
|
* @author <a href="mailto:jpalmer@itemmaster.com">Jason Palmer, itemMaster LLC</a>
|
||||||
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
|
*/
|
||||||
|
public final class AdobePathReader {
|
||||||
|
static final boolean DEBUG = "true".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.imageio.path.debug"));
|
||||||
|
|
||||||
|
private final DataInput data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a path reader that will read its data from a {@code DataInput},
|
||||||
|
* such as an {@code ImageInputStream}.
|
||||||
|
* The data length is assumed to be a multiple of 26.
|
||||||
|
*
|
||||||
|
* @param data the input to read data from.
|
||||||
|
* @throws java.lang.IllegalArgumentException if {@code data} is {@code null}
|
||||||
|
*/
|
||||||
|
public AdobePathReader(final DataInput data) {
|
||||||
|
notNull(data, "data");
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a path reader that will read its data from a {@code byte} array.
|
||||||
|
* The array length must be a multiple of 26, and greater than 0.
|
||||||
|
*
|
||||||
|
* @param data the array to read data from.
|
||||||
|
* @throws java.lang.IllegalArgumentException if {@code data} is {@code null}, or not a multiple of 26.
|
||||||
|
*/
|
||||||
|
public AdobePathReader(final byte[] data) {
|
||||||
|
this(new ByteArrayImageInputStream(
|
||||||
|
notNull(data, "data"), 0,
|
||||||
|
isTrue(data.length > 0 && data.length % 26 == 0, data.length, "data.length must be a multiple of 26: %d")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the path by reading from the supplied input.
|
||||||
|
*
|
||||||
|
* @return the path
|
||||||
|
* @throws javax.imageio.IIOException if the input contains a bad path data.
|
||||||
|
* @throws IOException if a general I/O exception occurs during reading.
|
||||||
|
*/
|
||||||
|
public Path2D readPath() throws IOException {
|
||||||
|
List<List<AdobePathSegment>> subPaths = new ArrayList<>();
|
||||||
|
List<AdobePathSegment> currentPath = null;
|
||||||
|
int currentPathLength = 0;
|
||||||
|
|
||||||
|
AdobePathSegment segment;
|
||||||
|
while ((segment = nextSegment()) != null) {
|
||||||
|
|
||||||
|
if (DEBUG) {
|
||||||
|
System.out.println(segment);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (segment.selector == AdobePathSegment.OPEN_SUBPATH_LENGTH_RECORD || segment.selector == AdobePathSegment.CLOSED_SUBPATH_LENGTH_RECORD) {
|
||||||
|
if (currentPath != null) {
|
||||||
|
if (currentPathLength != currentPath.size()) {
|
||||||
|
throw new IIOException(String.format("Bad path, expected %d segments, found only %d", currentPathLength, currentPath.size()));
|
||||||
|
}
|
||||||
|
subPaths.add(currentPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentPath = new ArrayList<>(segment.lengthOrRule);
|
||||||
|
currentPathLength = segment.lengthOrRule;
|
||||||
|
}
|
||||||
|
else if (segment.selector == AdobePathSegment.OPEN_SUBPATH_BEZIER_LINKED
|
||||||
|
|| segment.selector == AdobePathSegment.OPEN_SUBPATH_BEZIER_UNLINKED
|
||||||
|
|| segment.selector == AdobePathSegment.CLOSED_SUBPATH_BEZIER_LINKED
|
||||||
|
|| segment.selector == AdobePathSegment.CLOSED_SUBPATH_BEZIER_UNLINKED) {
|
||||||
|
if (currentPath == null) {
|
||||||
|
throw new IIOException("Bad path, missing subpath length record");
|
||||||
|
}
|
||||||
|
if (currentPath.size() >= currentPathLength) {
|
||||||
|
throw new IIOException(String.format("Bad path, expected %d segments, found%d", currentPathLength, currentPath.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
currentPath.add(segment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// now add the last one
|
||||||
|
if (currentPath != null) {
|
||||||
|
if (currentPathLength != currentPath.size()) {
|
||||||
|
throw new IIOException(String.format("Bad path, expected %d segments, found only %d", currentPathLength, currentPath.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
subPaths.add(currentPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have collected the Path points, now create a Shape
|
||||||
|
return pathToShape(subPaths);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Correct Order... P1, P2, P3, P4, P5, P6 (Closed) moveTo(P1)
|
||||||
|
* curveTo(P1.cpl, P2.cpp, P2.ap); curveTo(P2.cpl, P3.cpp, P3.ap);
|
||||||
|
* curveTo(P3.cpl, P4.cpp, P4.ap); curveTo(P4.cpl, P5.cpp, P5.ap);
|
||||||
|
* curveTo(P5.cpl, P6.cpp, P6.ap); curveTo(P6.cpl, P1.cpp, P1.ap);
|
||||||
|
* closePath()
|
||||||
|
*/
|
||||||
|
private Path2D pathToShape(final List<List<AdobePathSegment>> paths) {
|
||||||
|
Path2D path = new Path2D.Float(Path2D.WIND_EVEN_ODD, paths.size());
|
||||||
|
Path2D subpath = null;
|
||||||
|
|
||||||
|
for (List<AdobePathSegment> points : paths) {
|
||||||
|
int length = points.size();
|
||||||
|
|
||||||
|
for (int i = 0; i < points.size(); i++) {
|
||||||
|
AdobePathSegment current = points.get(i);
|
||||||
|
|
||||||
|
int step = i == 0 ? 0 : i == length - 1 ? 2 : 1;
|
||||||
|
|
||||||
|
switch (step) {
|
||||||
|
// Begin
|
||||||
|
case 0: {
|
||||||
|
subpath = new Path2D.Float(Path2D.WIND_EVEN_ODD, length);
|
||||||
|
subpath.moveTo(current.apx, current.apy);
|
||||||
|
|
||||||
|
if (length > 1) {
|
||||||
|
AdobePathSegment next = points.get((i + 1));
|
||||||
|
subpath.curveTo(current.cplx, current.cply, next.cppx, next.cppy, next.apx, next.apy);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
subpath.lineTo(current.apx, current.apy);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Middle
|
||||||
|
case 1: {
|
||||||
|
AdobePathSegment next = points.get((i + 1)); // We are always guaranteed one more.
|
||||||
|
subpath.curveTo(current.cplx, current.cply, next.cppx, next.cppy, next.apx, next.apy);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// End
|
||||||
|
case 2: {
|
||||||
|
AdobePathSegment first = points.get(0);
|
||||||
|
|
||||||
|
if (first.selector == AdobePathSegment.CLOSED_SUBPATH_BEZIER_LINKED || first.selector == AdobePathSegment.CLOSED_SUBPATH_BEZIER_UNLINKED) {
|
||||||
|
subpath.curveTo(current.cplx, current.cply, first.cppx, first.cppy, first.apx, first.apy);
|
||||||
|
subpath.closePath();
|
||||||
|
path.append(subpath, false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
subpath.lineTo(current.apx, current.apy);
|
||||||
|
path.append(subpath, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AdobePathSegment nextSegment() throws IOException {
|
||||||
|
// Each segment is 26 bytes
|
||||||
|
int selector;
|
||||||
|
try {
|
||||||
|
selector = data.readUnsignedShort();
|
||||||
|
}
|
||||||
|
catch (EOFException eof) {
|
||||||
|
// No more data, we're done
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (selector) {
|
||||||
|
case AdobePathSegment.INITIAL_FILL_RULE_RECORD:
|
||||||
|
case AdobePathSegment.PATH_FILL_RULE_RECORD:
|
||||||
|
// Spec says Fill rule is ignored by Photoshop, we'll read it anyway
|
||||||
|
case AdobePathSegment.CLOSED_SUBPATH_LENGTH_RECORD:
|
||||||
|
case AdobePathSegment.OPEN_SUBPATH_LENGTH_RECORD:
|
||||||
|
int lengthOrRule = data.readUnsignedShort();
|
||||||
|
data.skipBytes(22);
|
||||||
|
return new AdobePathSegment(selector, lengthOrRule);
|
||||||
|
default:
|
||||||
|
return new AdobePathSegment(
|
||||||
|
selector,
|
||||||
|
AdobePathSegment.fromFixedPoint(data.readInt()),
|
||||||
|
AdobePathSegment.fromFixedPoint(data.readInt()),
|
||||||
|
AdobePathSegment.fromFixedPoint(data.readInt()),
|
||||||
|
AdobePathSegment.fromFixedPoint(data.readInt()),
|
||||||
|
AdobePathSegment.fromFixedPoint(data.readInt()),
|
||||||
|
AdobePathSegment.fromFixedPoint(data.readInt())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Regular → Executable
+47
-28
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2014, Harald Kuhr
|
* Copyright (c) 2014-2020, Harald Kuhr
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.imageio.path;
|
package com.twelvemonkeys.imageio.path;
|
||||||
|
|
||||||
import com.twelvemonkeys.lang.Validate;
|
import static com.twelvemonkeys.lang.Validate.isTrue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adobe path segment.
|
* Adobe path segment.
|
||||||
@@ -40,17 +40,17 @@ import com.twelvemonkeys.lang.Validate;
|
|||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
*/
|
*/
|
||||||
final class AdobePathSegment {
|
final class AdobePathSegment {
|
||||||
public final static int CLOSED_SUBPATH_LENGTH_RECORD = 0;
|
static final int CLOSED_SUBPATH_LENGTH_RECORD = 0;
|
||||||
public final static int CLOSED_SUBPATH_BEZIER_LINKED = 1;
|
static final int CLOSED_SUBPATH_BEZIER_LINKED = 1;
|
||||||
public final static int CLOSED_SUBPATH_BEZIER_UNLINKED = 2;
|
static final int CLOSED_SUBPATH_BEZIER_UNLINKED = 2;
|
||||||
public final static int OPEN_SUBPATH_LENGTH_RECORD = 3;
|
static final int OPEN_SUBPATH_LENGTH_RECORD = 3;
|
||||||
public final static int OPEN_SUBPATH_BEZIER_LINKED = 4;
|
static final int OPEN_SUBPATH_BEZIER_LINKED = 4;
|
||||||
public final static int OPEN_SUBPATH_BEZIER_UNLINKED = 5;
|
static final int OPEN_SUBPATH_BEZIER_UNLINKED = 5;
|
||||||
public final static int PATH_FILL_RULE_RECORD = 6;
|
static final int PATH_FILL_RULE_RECORD = 6;
|
||||||
public final static int CLIPBOARD_RECORD = 7;
|
static final int CLIPBOARD_RECORD = 7;
|
||||||
public final static int INITIAL_FILL_RULE_RECORD = 8;
|
static final int INITIAL_FILL_RULE_RECORD = 8;
|
||||||
|
|
||||||
public final static String[] SELECTOR_NAMES = {
|
static final String[] SELECTOR_NAMES = {
|
||||||
"Closed subpath length record",
|
"Closed subpath length record",
|
||||||
"Closed subpath Bezier knot, linked",
|
"Closed subpath Bezier knot, linked",
|
||||||
"Closed subpath Bezier knot, unlinked",
|
"Closed subpath Bezier knot, unlinked",
|
||||||
@@ -63,12 +63,18 @@ final class AdobePathSegment {
|
|||||||
};
|
};
|
||||||
|
|
||||||
final int selector;
|
final int selector;
|
||||||
final int length;
|
final int lengthOrRule;
|
||||||
|
|
||||||
|
// TODO: Consider keeping these in 8.24FP format
|
||||||
|
// Control point preceding knot
|
||||||
final double cppy;
|
final double cppy;
|
||||||
final double cppx;
|
final double cppx;
|
||||||
|
|
||||||
|
// Anchor point
|
||||||
final double apy;
|
final double apy;
|
||||||
final double apx;
|
final double apx;
|
||||||
|
|
||||||
|
// Control point leaving knot
|
||||||
final double cply;
|
final double cply;
|
||||||
final double cplx;
|
final double cplx;
|
||||||
|
|
||||||
@@ -79,11 +85,14 @@ final class AdobePathSegment {
|
|||||||
this(selector, -1, cppy, cppx, apy, apx, cply, cplx);
|
this(selector, -1, cppy, cppx, apy, apx, cply, cplx);
|
||||||
}
|
}
|
||||||
|
|
||||||
AdobePathSegment(final int selector, final int length) {
|
AdobePathSegment(final int selector, final int lengthOrRule) {
|
||||||
this(selector, length, -1, -1, -1, -1, -1, -1);
|
this(isTrue(selector == CLOSED_SUBPATH_LENGTH_RECORD || selector == OPEN_SUBPATH_LENGTH_RECORD
|
||||||
|
|| selector == PATH_FILL_RULE_RECORD || selector == INITIAL_FILL_RULE_RECORD, selector, "Expected path length or fill rule record (0/3 or 6/8): %s"),
|
||||||
|
lengthOrRule,
|
||||||
|
-1, -1, -1, -1, -1, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private AdobePathSegment(final int selector, final int length,
|
private AdobePathSegment(final int selector, final int lengthOrRule,
|
||||||
final double cppy, final double cppx,
|
final double cppy, final double cppx,
|
||||||
final double apy, final double apx,
|
final double apy, final double apx,
|
||||||
final double cply, final double cplx) {
|
final double cply, final double cplx) {
|
||||||
@@ -91,27 +100,29 @@ final class AdobePathSegment {
|
|||||||
switch (selector) {
|
switch (selector) {
|
||||||
case CLOSED_SUBPATH_LENGTH_RECORD:
|
case CLOSED_SUBPATH_LENGTH_RECORD:
|
||||||
case OPEN_SUBPATH_LENGTH_RECORD:
|
case OPEN_SUBPATH_LENGTH_RECORD:
|
||||||
Validate.isTrue(length >= 0, length, "Bad size: %d");
|
isTrue(lengthOrRule >= 0, lengthOrRule, "Expected positive length: %d");
|
||||||
break;
|
break;
|
||||||
case CLOSED_SUBPATH_BEZIER_LINKED:
|
case CLOSED_SUBPATH_BEZIER_LINKED:
|
||||||
case CLOSED_SUBPATH_BEZIER_UNLINKED:
|
case CLOSED_SUBPATH_BEZIER_UNLINKED:
|
||||||
case OPEN_SUBPATH_BEZIER_LINKED:
|
case OPEN_SUBPATH_BEZIER_LINKED:
|
||||||
case OPEN_SUBPATH_BEZIER_UNLINKED:
|
case OPEN_SUBPATH_BEZIER_UNLINKED:
|
||||||
Validate.isTrue(
|
isTrue(
|
||||||
cppx >= 0 && cppx <= 1 && cppy >= 0 && cppy <= 1,
|
cppx >= -16 && cppx <= 16 && cppy >= -16 && cppy <= 16,
|
||||||
String.format("Unexpected point: [%f, %f]", cppx ,cppy)
|
String.format("Expected point in range [-16...16]: (%f, %f)", cppx ,cppy)
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case PATH_FILL_RULE_RECORD:
|
case PATH_FILL_RULE_RECORD:
|
||||||
case CLIPBOARD_RECORD:
|
|
||||||
case INITIAL_FILL_RULE_RECORD:
|
case INITIAL_FILL_RULE_RECORD:
|
||||||
|
isTrue(lengthOrRule == 0 || lengthOrRule == 1, lengthOrRule, "Expected rule (1 or 0): %d");
|
||||||
|
break;
|
||||||
|
case CLIPBOARD_RECORD:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Bad selector: " + selector);
|
throw new IllegalArgumentException("Unknown selector: " + selector);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.selector = selector;
|
this.selector = selector;
|
||||||
this.length = length;
|
this.lengthOrRule = lengthOrRule;
|
||||||
this.cppy = cppy;
|
this.cppy = cppy;
|
||||||
this.cppx = cppx;
|
this.cppx = cppx;
|
||||||
this.apy = apy;
|
this.apy = apy;
|
||||||
@@ -120,6 +131,14 @@ final class AdobePathSegment {
|
|||||||
this.cplx = cplx;
|
this.cplx = cplx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int toFixedPoint(final double value) {
|
||||||
|
return (int) Math.round(value * 0x1000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
static double fromFixedPoint(final int fixed) {
|
||||||
|
return ((double) fixed / 0x1000000);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(final Object other) {
|
public boolean equals(final Object other) {
|
||||||
if (this == other) {
|
if (this == other) {
|
||||||
@@ -139,7 +158,7 @@ final class AdobePathSegment {
|
|||||||
&& Double.compare(that.cppx, cppx) == 0
|
&& Double.compare(that.cppx, cppx) == 0
|
||||||
&& Double.compare(that.cppy, cppy) == 0
|
&& Double.compare(that.cppy, cppy) == 0
|
||||||
&& selector == that.selector
|
&& selector == that.selector
|
||||||
&& length == that.length;
|
&& lengthOrRule == that.lengthOrRule;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,7 +167,7 @@ final class AdobePathSegment {
|
|||||||
long tempBits;
|
long tempBits;
|
||||||
|
|
||||||
int result = selector;
|
int result = selector;
|
||||||
result = 31 * result + length;
|
result = 31 * result + lengthOrRule;
|
||||||
tempBits = Double.doubleToLongBits(cppy);
|
tempBits = Double.doubleToLongBits(cppy);
|
||||||
result = 31 * result + (int) (tempBits ^ (tempBits >>> 32));
|
result = 31 * result + (int) (tempBits ^ (tempBits >>> 32));
|
||||||
tempBits = Double.doubleToLongBits(cppx);
|
tempBits = Double.doubleToLongBits(cppx);
|
||||||
@@ -170,13 +189,13 @@ final class AdobePathSegment {
|
|||||||
switch (selector) {
|
switch (selector) {
|
||||||
case INITIAL_FILL_RULE_RECORD:
|
case INITIAL_FILL_RULE_RECORD:
|
||||||
case PATH_FILL_RULE_RECORD:
|
case PATH_FILL_RULE_RECORD:
|
||||||
return String.format("Rule(selector=%s, rule=%d)", SELECTOR_NAMES[selector], length);
|
return String.format("Rule(selector=%s, rule=%d)", SELECTOR_NAMES[selector], lengthOrRule);
|
||||||
case CLOSED_SUBPATH_LENGTH_RECORD:
|
case CLOSED_SUBPATH_LENGTH_RECORD:
|
||||||
case OPEN_SUBPATH_LENGTH_RECORD:
|
case OPEN_SUBPATH_LENGTH_RECORD:
|
||||||
return String.format("Len(selector=%s, totalPoints=%d)", SELECTOR_NAMES[selector], length);
|
return String.format("Len(selector=%s, length=%d)", SELECTOR_NAMES[selector], lengthOrRule);
|
||||||
default:
|
default:
|
||||||
// fall-through
|
// fall-through
|
||||||
}
|
}
|
||||||
return String.format("Pt(preX=%.3f, preY=%.3f, knotX=%.3f, knotY=%.3f, postX=%.3f, postY=%.3f, selector=%s)", cppx, cppy, apx, apy, cplx, cply, SELECTOR_NAMES[selector]);
|
return String.format("Pt(pre=(%.3f, %.3f), knot=(%.3f, %.3f), post=(%.3f, %.3f), selector=%s)", cppx, cppy, apx, apy, cplx, cply, SELECTOR_NAMES[selector]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Executable
+278
@@ -0,0 +1,278 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Harald Kuhr
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.twelvemonkeys.imageio.path;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.imageio.metadata.psd.PSD;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.geom.Path2D;
|
||||||
|
import java.awt.geom.PathIterator;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.DataOutput;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static com.twelvemonkeys.imageio.path.AdobePathReader.DEBUG;
|
||||||
|
import static com.twelvemonkeys.imageio.path.AdobePathSegment.*;
|
||||||
|
import static com.twelvemonkeys.lang.Validate.isTrue;
|
||||||
|
import static com.twelvemonkeys.lang.Validate.notNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a {@code Shape} object to an Adobe Photoshop Path or Path resource.
|
||||||
|
*
|
||||||
|
* @see <a href="http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_17587">Adobe Photoshop Path resource format</a>
|
||||||
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
|
*/
|
||||||
|
public final class AdobePathWriter {
|
||||||
|
|
||||||
|
// TODO: Might need to get hold of more real Photoshop samples to tune this threshold...
|
||||||
|
private static final double COLLINEARITY_THRESHOLD = 0.00000001;
|
||||||
|
|
||||||
|
private final List<AdobePathSegment> segments;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an AdobePathWriter for the given path.
|
||||||
|
* <p>
|
||||||
|
* NOTE: Photoshop paths are stored with the coordinates
|
||||||
|
* (0,0) representing the top left corner of the image,
|
||||||
|
* and (1,1) representing the bottom right corner,
|
||||||
|
* regardless of image dimensions.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param path A {@code Shape} instance that has {@link Path2D#WIND_EVEN_ODD WIND_EVEN_ODD} rule,
|
||||||
|
* is contained within the rectangle [x=0.0,y=0.0,w=1.0,h=1.0], and is closed.
|
||||||
|
* @throws IllegalArgumentException if {@code path} is {@code null},
|
||||||
|
* the paths winding rule is not @link Path2D#WIND_EVEN_ODD} or
|
||||||
|
* the paths bounding box is outside [x=0.0,y=0.0,w=1.0,h=1.0] or
|
||||||
|
* the path is not closed.
|
||||||
|
*/
|
||||||
|
public AdobePathWriter(final Shape path) {
|
||||||
|
notNull(path, "path");
|
||||||
|
isTrue(new Rectangle(0, 0, 1, 1).contains(path.getBounds2D()), path.getBounds2D(), "Path bounds must be within [x=0,y=0,w=1,h=1]: %s");
|
||||||
|
|
||||||
|
segments = pathToSegments(path.getPathIterator(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Look at the API so that conversion both ways are aligned. The read part builds a path from List<List<AdobePathSegment>...
|
||||||
|
private static List<AdobePathSegment> pathToSegments(final PathIterator pathIterator) {
|
||||||
|
// TODO: Test if PS really ignores winding rule as documented... Otherwise we could support writing non-zero too.
|
||||||
|
isTrue(pathIterator.getWindingRule() == Path2D.WIND_EVEN_ODD, pathIterator.getWindingRule(), "Only even/odd winding rule supported: %d");
|
||||||
|
|
||||||
|
double[] coords = new double[6];
|
||||||
|
AdobePathSegment prev = new AdobePathSegment(CLOSED_SUBPATH_BEZIER_LINKED, 0, 0, 0, 0, 0, 0);
|
||||||
|
|
||||||
|
List<AdobePathSegment> subpath = new ArrayList<>();
|
||||||
|
List<AdobePathSegment> segments = new ArrayList<>();
|
||||||
|
segments.add(new AdobePathSegment(PATH_FILL_RULE_RECORD, 0));
|
||||||
|
segments.add(new AdobePathSegment(INITIAL_FILL_RULE_RECORD, 0));
|
||||||
|
|
||||||
|
while (!pathIterator.isDone()) {
|
||||||
|
int segmentType = pathIterator.currentSegment(coords);
|
||||||
|
|
||||||
|
if (DEBUG) {
|
||||||
|
System.out.println("segmentType: " + segmentType);
|
||||||
|
System.out.println("coords: " + Arrays.toString(coords));
|
||||||
|
}
|
||||||
|
|
||||||
|
// We write collinear points as linked segments
|
||||||
|
boolean collinear = isCollinear(prev.cppx, prev.cppy, prev.apx, prev.apy, coords[0], coords[1]);
|
||||||
|
|
||||||
|
switch (segmentType) {
|
||||||
|
case PathIterator.SEG_MOVETO:
|
||||||
|
// TODO: What if we didn't close before the moveto? Start new segment here?
|
||||||
|
|
||||||
|
// Dummy starting point, will be updated later
|
||||||
|
prev = new AdobePathSegment(CLOSED_SUBPATH_BEZIER_LINKED, 0, 0, coords[1], coords[0], 0, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PathIterator.SEG_LINETO:
|
||||||
|
subpath.add(new AdobePathSegment(collinear ? CLOSED_SUBPATH_BEZIER_LINKED : CLOSED_SUBPATH_BEZIER_UNLINKED, prev.cppy, prev.cppx, prev.apy, prev.apx, coords[1], coords[0]));
|
||||||
|
prev = new AdobePathSegment(CLOSED_SUBPATH_BEZIER_LINKED, coords[1], coords[0], coords[1], coords[0], 0, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PathIterator.SEG_QUADTO:
|
||||||
|
subpath.add(new AdobePathSegment(collinear ? CLOSED_SUBPATH_BEZIER_LINKED : CLOSED_SUBPATH_BEZIER_UNLINKED, prev.cppy, prev.cppx, prev.apy, prev.apx, coords[1], coords[0]));
|
||||||
|
prev = new AdobePathSegment(CLOSED_SUBPATH_BEZIER_LINKED, coords[3], coords[2], coords[3], coords[2], 0, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PathIterator.SEG_CUBICTO:
|
||||||
|
subpath.add(new AdobePathSegment(collinear ? CLOSED_SUBPATH_BEZIER_LINKED : CLOSED_SUBPATH_BEZIER_UNLINKED, prev.cppy, prev.cppx, prev.apy, prev.apx, coords[1], coords[0]));
|
||||||
|
prev = new AdobePathSegment(CLOSED_SUBPATH_BEZIER_LINKED, coords[3], coords[2], coords[5], coords[4], 0, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PathIterator.SEG_CLOSE:
|
||||||
|
AdobePathSegment initial = subpath.get(0);
|
||||||
|
|
||||||
|
if (initial.apx != prev.apx || initial.apy != prev.apy) {
|
||||||
|
// Line back to initial if last anchor point does not equal initial anchor
|
||||||
|
collinear = isCollinear(prev.cppx, prev.cppy, initial.apx, initial.apy, initial.apx, initial.apy);
|
||||||
|
subpath.add(new AdobePathSegment(collinear ? CLOSED_SUBPATH_BEZIER_LINKED : CLOSED_SUBPATH_BEZIER_UNLINKED, prev.cppy, prev.cppx, prev.apy, prev.apx, initial.apy, initial.apx));
|
||||||
|
prev = new AdobePathSegment(CLOSED_SUBPATH_BEZIER_LINKED, initial.apy, initial.apx, initial.apy, initial.apx, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
close(initial, prev, subpath, segments);
|
||||||
|
subpath.clear();
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pathIterator.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If subpath is not empty at this point, there was no close segment...
|
||||||
|
// Wrap up if coordinates match, otherwise throw exception
|
||||||
|
if (!subpath.isEmpty()) {
|
||||||
|
AdobePathSegment initial = subpath.get(0);
|
||||||
|
|
||||||
|
if (initial.apx != prev.apx || initial.apy != prev.apy) {
|
||||||
|
throw new IllegalArgumentException("Path must be closed");
|
||||||
|
}
|
||||||
|
|
||||||
|
close(initial, prev, subpath, segments);
|
||||||
|
}
|
||||||
|
|
||||||
|
return segments;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void close(AdobePathSegment initial, AdobePathSegment prev, List<AdobePathSegment> subpath, List<AdobePathSegment> segments) {
|
||||||
|
// Replace initial point.
|
||||||
|
boolean collinear = isCollinear(prev.cppx, prev.cppy, initial.apx, initial.apy, initial.cplx, initial.cply);
|
||||||
|
subpath.set(0, new AdobePathSegment(collinear ? CLOSED_SUBPATH_BEZIER_LINKED : CLOSED_SUBPATH_BEZIER_UNLINKED, prev.cppy, prev.cppx, initial.apy, initial.apx, initial.cply, initial.cplx));
|
||||||
|
|
||||||
|
// Add to full path
|
||||||
|
segments.add(new AdobePathSegment(CLOSED_SUBPATH_LENGTH_RECORD, subpath.size()));
|
||||||
|
segments.addAll(subpath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isCollinear(double x1, double y1, double x2, double y2, double x3, double y3) {
|
||||||
|
// Photoshop seems to write as linked if all points are the same....
|
||||||
|
return (x1 == x2 && x2 == x3 && y1 == y2 && y2 == y3) ||
|
||||||
|
(x1 != x2 || y1 != y2) && (x2 != x3 || y2 != y3) &&
|
||||||
|
Math.abs(x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2)) <= COLLINEARITY_THRESHOLD; // With some slack...
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the path as a complete Adobe Photoshop clipping path resource to the given stream.
|
||||||
|
*
|
||||||
|
* @param resourceId the resource id, typically {@link PSD#RES_CLIPPING_PATH} (0x07D0).
|
||||||
|
* @param output the stream to write to.
|
||||||
|
* @throws IOException if an I/O exception happens during writing.
|
||||||
|
*/
|
||||||
|
public void writePathResource(int resourceId, final DataOutput output) throws IOException {
|
||||||
|
output.writeInt(PSD.RESOURCE_TYPE);
|
||||||
|
output.writeShort(resourceId);
|
||||||
|
output.writeShort(0); // Path name (Pascal string) empty + pad
|
||||||
|
output.writeInt(segments.size() * 26); // Resource size
|
||||||
|
|
||||||
|
writePath(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the path as a set of Adobe Photoshop path segments to the given stream.
|
||||||
|
*
|
||||||
|
* @param output the stream to write to.
|
||||||
|
* @throws IOException if an I/O exception happens during writing.
|
||||||
|
*/
|
||||||
|
public void writePath(final DataOutput output) throws IOException {
|
||||||
|
if (DEBUG) {
|
||||||
|
System.out.println("segments: " + segments.size());
|
||||||
|
System.out.println(segments);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (AdobePathSegment segment : segments) {
|
||||||
|
switch (segment.selector) {
|
||||||
|
case PATH_FILL_RULE_RECORD:
|
||||||
|
case INITIAL_FILL_RULE_RECORD:
|
||||||
|
// The first 26-byte path record contains a selector value of 6, path fill rule record.
|
||||||
|
// The remaining 24 bytes of the first record are zeroes. Paths use even/odd ruling.
|
||||||
|
output.writeShort(segment.selector);
|
||||||
|
output.write(new byte[24]);
|
||||||
|
break;
|
||||||
|
case OPEN_SUBPATH_LENGTH_RECORD:
|
||||||
|
case CLOSED_SUBPATH_LENGTH_RECORD:
|
||||||
|
output.writeShort(segment.selector);
|
||||||
|
output.writeShort(segment.lengthOrRule); // Subpath length
|
||||||
|
output.write(new byte[22]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
output.writeShort(segment.selector);
|
||||||
|
output.writeInt(toFixedPoint(segment.cppy));
|
||||||
|
output.writeInt(toFixedPoint(segment.cppx));
|
||||||
|
output.writeInt(toFixedPoint(segment.apy));
|
||||||
|
output.writeInt(toFixedPoint(segment.apx));
|
||||||
|
output.writeInt(toFixedPoint(segment.cply));
|
||||||
|
output.writeInt(toFixedPoint(segment.cplx));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms the path to a byte array, containing a complete Adobe Photoshop path resource.
|
||||||
|
*
|
||||||
|
* @param resourceId the resource id, typically {@link PSD#RES_CLIPPING_PATH} (0x07D0).
|
||||||
|
* @return a new byte array, containing the clipping path resource.
|
||||||
|
*/
|
||||||
|
public byte[] writePathResource(int resourceId) {
|
||||||
|
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
try (DataOutputStream stream = new DataOutputStream(bytes)) {
|
||||||
|
writePathResource(resourceId, stream);
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
throw new AssertionError("ByteArrayOutputStream threw IOException", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms the path to a byte array, containing a set of Adobe Photoshop path segments.
|
||||||
|
*
|
||||||
|
* @return a new byte array, containing the path segments.
|
||||||
|
*/
|
||||||
|
public byte[] writePath() {
|
||||||
|
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
try (DataOutputStream stream = new DataOutputStream(bytes)) {
|
||||||
|
writePath(stream);
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
throw new AssertionError("ByteArrayOutputStream threw IOException", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes.toByteArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
Regular → Executable
+172
-19
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2014, Harald Kuhr
|
* Copyright (c) 2014-2020, Harald Kuhr
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@@ -43,22 +43,31 @@ import com.twelvemonkeys.imageio.metadata.tiff.TIFFReader;
|
|||||||
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
|
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
|
||||||
import com.twelvemonkeys.imageio.stream.SubImageInputStream;
|
import com.twelvemonkeys.imageio.stream.SubImageInputStream;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.*;
|
||||||
|
import javax.imageio.metadata.IIOMetadata;
|
||||||
|
import javax.imageio.metadata.IIOMetadataNode;
|
||||||
import javax.imageio.stream.ImageInputStream;
|
import javax.imageio.stream.ImageInputStream;
|
||||||
|
import javax.imageio.stream.ImageOutputStream;
|
||||||
import javax.imageio.stream.MemoryCacheImageInputStream;
|
import javax.imageio.stream.MemoryCacheImageInputStream;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.geom.AffineTransform;
|
import java.awt.geom.AffineTransform;
|
||||||
import java.awt.geom.Path2D;
|
import java.awt.geom.Path2D;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.awt.image.RenderedImage;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.LinkedHashMap;
|
import java.io.InputStream;
|
||||||
|
import java.io.SequenceInputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static com.twelvemonkeys.lang.Validate.isTrue;
|
import static com.twelvemonkeys.lang.Validate.isTrue;
|
||||||
import static com.twelvemonkeys.lang.Validate.notNull;
|
import static com.twelvemonkeys.lang.Validate.notNull;
|
||||||
|
import static java.util.Arrays.asList;
|
||||||
import static java.util.Collections.singletonList;
|
import static java.util.Collections.singletonList;
|
||||||
|
import static java.util.Collections.singletonMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Support for various Adobe Photoshop Path related operations:
|
* Support for various Adobe Photoshop Path related operations:
|
||||||
@@ -66,10 +75,11 @@ import static java.util.Collections.singletonList;
|
|||||||
* <li>Extract a path from an image input stream, {@link #readPath}</li>
|
* <li>Extract a path from an image input stream, {@link #readPath}</li>
|
||||||
* <li>Apply a given path to a given {@code BufferedImage} {@link #applyClippingPath}</li>
|
* <li>Apply a given path to a given {@code BufferedImage} {@link #applyClippingPath}</li>
|
||||||
* <li>Read an image with path applied {@link #readClipped}</li>
|
* <li>Read an image with path applied {@link #readClipped}</li>
|
||||||
|
* <li>Write an image with embedded path {@link #writeClipped}</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @see <a href="http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_17587">Adobe Photoshop Path resource format</a>
|
* @see <a href="http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_17587">Adobe Photoshop Path resource format</a>
|
||||||
* @see com.twelvemonkeys.imageio.path.AdobePathBuilder
|
* @see AdobePathReader
|
||||||
* @author <a href="mailto:jpalmer@itemmaster.com">Jason Palmer, itemMaster LLC</a>
|
* @author <a href="mailto:jpalmer@itemmaster.com">Jason Palmer, itemMaster LLC</a>
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @author last modified by $Author: harald.kuhr$
|
* @author last modified by $Author: harald.kuhr$
|
||||||
@@ -90,7 +100,7 @@ public final class Paths {
|
|||||||
* @throws javax.imageio.IIOException if the input contains a bad path data.
|
* @throws javax.imageio.IIOException if the input contains a bad path data.
|
||||||
* @throws java.lang.IllegalArgumentException is {@code stream} is {@code null}.
|
* @throws java.lang.IllegalArgumentException is {@code stream} is {@code null}.
|
||||||
*
|
*
|
||||||
* @see com.twelvemonkeys.imageio.path.AdobePathBuilder
|
* @see AdobePathReader
|
||||||
*/
|
*/
|
||||||
public static Path2D readPath(final ImageInputStream stream) throws IOException {
|
public static Path2D readPath(final ImageInputStream stream) throws IOException {
|
||||||
notNull(stream, "stream");
|
notNull(stream, "stream");
|
||||||
@@ -99,7 +109,7 @@ public final class Paths {
|
|||||||
|
|
||||||
if (magic == PSD.RESOURCE_TYPE) {
|
if (magic == PSD.RESOURCE_TYPE) {
|
||||||
// This is a PSD Image Resource Block, we can parse directly
|
// This is a PSD Image Resource Block, we can parse directly
|
||||||
return buildPathFromPhotoshopResources(stream);
|
return readPathFromPhotoshopResources(stream);
|
||||||
}
|
}
|
||||||
else if (magic == PSD.SIGNATURE_8BPS) {
|
else if (magic == PSD.SIGNATURE_8BPS) {
|
||||||
// PSD version
|
// PSD version
|
||||||
@@ -115,17 +125,21 @@ public final class Paths {
|
|||||||
long imageResourcesLen = stream.readUnsignedInt();
|
long imageResourcesLen = stream.readUnsignedInt();
|
||||||
|
|
||||||
// Image resources
|
// Image resources
|
||||||
return buildPathFromPhotoshopResources(new SubImageInputStream(stream, imageResourcesLen));
|
return readPathFromPhotoshopResources(new SubImageInputStream(stream, imageResourcesLen));
|
||||||
}
|
}
|
||||||
else if (magic >>> 16 == JPEG.SOI && (magic & 0xff00) == 0xff00) {
|
else if (magic >>> 16 == JPEG.SOI && (magic & 0xff00) == 0xff00) {
|
||||||
// JPEG version
|
// JPEG version
|
||||||
Map<Integer, java.util.List<String>> segmentIdentifiers = new LinkedHashMap<>();
|
Map<Integer, List<String>> segmentIdentifiers = singletonMap(JPEG.APP13, singletonList("Photoshop 3.0"));
|
||||||
segmentIdentifiers.put(JPEG.APP13, singletonList("Photoshop 3.0"));
|
|
||||||
|
|
||||||
List<JPEGSegment> photoshop = JPEGSegmentUtil.readSegments(stream, segmentIdentifiers);
|
List<JPEGSegment> photoshop = JPEGSegmentUtil.readSegments(stream, segmentIdentifiers);
|
||||||
|
|
||||||
if (!photoshop.isEmpty()) {
|
if (!photoshop.isEmpty()) {
|
||||||
return buildPathFromPhotoshopResources(new MemoryCacheImageInputStream(photoshop.get(0).data()));
|
InputStream data = null;
|
||||||
|
|
||||||
|
for (JPEGSegment ps : photoshop) {
|
||||||
|
data = data == null ? ps.data() : new SequenceInputStream(data, ps.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
return readPathFromPhotoshopResources(new MemoryCacheImageInputStream(data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (magic >>> 16 == TIFF.BYTE_ORDER_MARK_BIG_ENDIAN && (magic & 0xffff) == TIFF.TIFF_MAGIC
|
else if (magic >>> 16 == TIFF.BYTE_ORDER_MARK_BIG_ENDIAN && (magic & 0xffff) == TIFF.TIFF_MAGIC
|
||||||
@@ -137,7 +151,7 @@ public final class Paths {
|
|||||||
Entry photoshop = directory.getEntryById(TIFF.TAG_PHOTOSHOP);
|
Entry photoshop = directory.getEntryById(TIFF.TAG_PHOTOSHOP);
|
||||||
|
|
||||||
if (photoshop != null) {
|
if (photoshop != null) {
|
||||||
return buildPathFromPhotoshopResources(new ByteArrayImageInputStream((byte[]) photoshop.getValue()));
|
return readPathFromPhotoshopResources(new ByteArrayImageInputStream((byte[]) photoshop.getValue()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,17 +170,17 @@ public final class Paths {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Path2D buildPathFromPhotoshopResources(final ImageInputStream stream) throws IOException {
|
private static Path2D readPathFromPhotoshopResources(final ImageInputStream stream) throws IOException {
|
||||||
Directory resourceBlocks = new PSDReader().read(stream);
|
Directory resourceBlocks = new PSDReader().read(stream);
|
||||||
|
|
||||||
if (AdobePathBuilder.DEBUG) {
|
if (AdobePathReader.DEBUG) {
|
||||||
System.out.println("resourceBlocks: " + resourceBlocks);
|
System.out.println("resourceBlocks: " + resourceBlocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
Entry resourceBlock = resourceBlocks.getEntryById(PSD.RES_CLIPPING_PATH);
|
Entry pathResource = resourceBlocks.getEntryById(PSD.RES_CLIPPING_PATH);
|
||||||
|
|
||||||
if (resourceBlock != null) {
|
if (pathResource != null) {
|
||||||
return new AdobePathBuilder((byte[]) resourceBlock.getValue()).path();
|
return new AdobePathReader((byte[]) pathResource.getValue()).readPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -254,9 +268,149 @@ public final class Paths {
|
|||||||
return applyClippingPath(clip, image);
|
return applyClippingPath(clip, image);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the image along with a clipping path resource, in the given format, to the supplied output.
|
||||||
|
* The image is written to the
|
||||||
|
* {@code ImageOutputStream} starting at the current stream
|
||||||
|
* pointer, overwriting existing stream data from that point
|
||||||
|
* forward, if present.
|
||||||
|
* <p>
|
||||||
|
* Note: As {@link ImageIO#write(RenderedImage, String, ImageOutputStream)}, this method does
|
||||||
|
* <em>not</em> close the output stream.
|
||||||
|
* It is the responsibility of the caller to close the stream, if desired.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* Implementation note: Only JPEG (using the "javax_imageio_jpeg_image_1.0" metadata format) and
|
||||||
|
* TIFF (using the "javax_imageio_tiff_image_1.0" or "com_sun_media_imageio_plugins_tiff_image_1.0" metadata formats)
|
||||||
|
* formats are currently supported.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param image the image to be written, may not be {@code null}.
|
||||||
|
* @param clipPath the clip path, may not be {@code null}.
|
||||||
|
* @param formatName the informal format name, may not be {@code null}.
|
||||||
|
* @param output the stream to write to, may not be {@code null}.
|
||||||
|
*
|
||||||
|
* @return {@code true} if the image was written,
|
||||||
|
* otherwise {@code false} (ie. no writer was found for the specified format).
|
||||||
|
*
|
||||||
|
* @exception IllegalArgumentException if any parameter is {@code null}.
|
||||||
|
* @exception IOException if an error occurs during writing.
|
||||||
|
*/
|
||||||
|
public static boolean writeClipped(final RenderedImage image, Shape clipPath, final String formatName, final ImageOutputStream output) throws IOException {
|
||||||
|
if (image == null) {
|
||||||
|
throw new IllegalArgumentException("image == null!");
|
||||||
|
}
|
||||||
|
if (formatName == null) {
|
||||||
|
throw new IllegalArgumentException("formatName == null!");
|
||||||
|
}
|
||||||
|
if (output == null) {
|
||||||
|
throw new IllegalArgumentException("output == null!");
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageTypeSpecifier type = ImageTypeSpecifier.createFromRenderedImage(image);
|
||||||
|
Iterator<ImageWriter> writers = ImageIO.getImageWriters(type, formatName);
|
||||||
|
|
||||||
|
if (writers.hasNext()) {
|
||||||
|
ImageWriter writer = writers.next();
|
||||||
|
|
||||||
|
ImageWriteParam param = writer.getDefaultWriteParam();
|
||||||
|
IIOMetadata metadata = writer.getDefaultImageMetadata(type, param);
|
||||||
|
List<String> metadataFormats = asList(metadata.getMetadataFormatNames());
|
||||||
|
|
||||||
|
byte[] pathResource = new AdobePathWriter(clipPath).writePathResource(PSD.RES_CLIPPING_PATH);
|
||||||
|
|
||||||
|
if (metadataFormats.contains("javax_imageio_tiff_image_1.0") || metadataFormats.contains("com_sun_media_imageio_plugins_tiff_image_1.0")) {
|
||||||
|
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
|
||||||
|
param.setCompressionType("Deflate");
|
||||||
|
|
||||||
|
// Check if the format is that of the bundled TIFF writer, otherwise use JAI format
|
||||||
|
String metadataFormat = metadataFormats.contains("javax_imageio_tiff_image_1.0")
|
||||||
|
? "javax_imageio_tiff_image_1.0"
|
||||||
|
: "com_sun_media_imageio_plugins_tiff_image_1.0"; // Fails in mergeTree, if not supported
|
||||||
|
IIOMetadataNode root = new IIOMetadataNode(metadataFormat);
|
||||||
|
IIOMetadataNode ifd = new IIOMetadataNode("TIFFIFD");
|
||||||
|
|
||||||
|
IIOMetadataNode pathField = new IIOMetadataNode("TIFFField");
|
||||||
|
pathField.setAttribute("number", String.valueOf(TIFF.TAG_PHOTOSHOP));
|
||||||
|
IIOMetadataNode pathValue = new IIOMetadataNode("TIFFUndefined"); // Use undefined for simplicity, could also use bytes
|
||||||
|
pathValue.setAttribute("value", arrayAsString(pathResource));
|
||||||
|
|
||||||
|
pathField.appendChild(pathValue);
|
||||||
|
ifd.appendChild(pathField);
|
||||||
|
root.appendChild(ifd);
|
||||||
|
|
||||||
|
metadata.mergeTree(metadataFormat, root);
|
||||||
|
|
||||||
|
writer.setOutput(output);
|
||||||
|
writer.write(null, new IIOImage(image, null, metadata), param);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (metadataFormats.contains("javax_imageio_jpeg_image_1.0")) {
|
||||||
|
String metadataFormat = "javax_imageio_jpeg_image_1.0";
|
||||||
|
IIOMetadataNode root = new IIOMetadataNode(metadataFormat);
|
||||||
|
|
||||||
|
root.appendChild(new IIOMetadataNode("JPEGvariety"));
|
||||||
|
|
||||||
|
IIOMetadataNode sequence = new IIOMetadataNode("markerSequence");
|
||||||
|
|
||||||
|
// App13/Photshop 3.0
|
||||||
|
IIOMetadataNode unknown = new IIOMetadataNode("unknown");
|
||||||
|
unknown.setAttribute("MarkerTag", Integer.toString(JPEG.APP13 & 0xFF));
|
||||||
|
|
||||||
|
byte[] identifier = "Photoshop 3.0".getBytes(StandardCharsets.US_ASCII);
|
||||||
|
byte[] data = new byte[identifier.length + 1 + pathResource.length];
|
||||||
|
System.arraycopy(identifier, 0, data, 0, identifier.length);
|
||||||
|
System.arraycopy(pathResource, 0, data, identifier.length + 1, pathResource.length);
|
||||||
|
|
||||||
|
unknown.setUserObject(data);
|
||||||
|
|
||||||
|
sequence.appendChild(unknown);
|
||||||
|
root.appendChild(sequence);
|
||||||
|
|
||||||
|
metadata.mergeTree(metadataFormat, root);
|
||||||
|
|
||||||
|
writer.setOutput(output);
|
||||||
|
writer.write(null, new IIOImage(image, null, metadata), param);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// TODO: Else if PSD... Requires PSD write + new metadata format...
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String arrayAsString(byte[] bytes) {
|
||||||
|
if (bytes == null || bytes.length == 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
for (int i = 0; ; i++) {
|
||||||
|
builder.append(bytes[i]);
|
||||||
|
|
||||||
|
if (i == bytes.length - 1) {
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.append(","); // NOTE: The javax_imageio_tiff_image_1.0 format does not allow whitespace here...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Test code
|
// Test code
|
||||||
public static void main(final String[] args) throws IOException, InterruptedException {
|
public static void main(final String[] args) throws IOException, InterruptedException {
|
||||||
BufferedImage destination = readClipped(ImageIO.createImageInputStream(new File(args[0])));
|
BufferedImage destination;
|
||||||
|
if (args.length == 1) {
|
||||||
|
// Embedded path
|
||||||
|
destination = readClipped(ImageIO.createImageInputStream(new File(args[0])));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Separate path and image
|
||||||
|
try (ImageInputStream input = ImageIO.createImageInputStream(new File(args[1]))) {
|
||||||
|
destination = applyClippingPath(readPath(input), ImageIO.read(new File(args[0])));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
File tempFile = File.createTempFile("clipped-", ".png");
|
File tempFile = File.createTempFile("clipped-", ".png");
|
||||||
tempFile.deleteOnExit();
|
tempFile.deleteOnExit();
|
||||||
@@ -270,5 +424,4 @@ public final class Paths {
|
|||||||
System.err.printf("%s not deleted\n", tempFile);
|
System.err.printf("%s not deleted\n", tempFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -43,6 +43,7 @@ import static com.twelvemonkeys.imageio.path.PathsTest.assertPathEquals;
|
|||||||
import static com.twelvemonkeys.imageio.path.PathsTest.readExpectedPath;
|
import static com.twelvemonkeys.imageio.path.PathsTest.readExpectedPath;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
public class AdobePathBuilderTest {
|
public class AdobePathBuilderTest {
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
|||||||
+165
@@ -0,0 +1,165 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Harald Kuhr
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
package com.twelvemonkeys.imageio.path;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import javax.imageio.IIOException;
|
||||||
|
import javax.imageio.stream.ImageInputStream;
|
||||||
|
import java.awt.geom.Path2D;
|
||||||
|
import java.io.DataInput;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import static com.twelvemonkeys.imageio.path.PathsTest.assertPathEquals;
|
||||||
|
import static com.twelvemonkeys.imageio.path.PathsTest.readExpectedPath;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
|
||||||
|
public class AdobePathReaderTest {
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void testCreateNullBytes() {
|
||||||
|
new AdobePathReader((byte[]) null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void testCreateNull() {
|
||||||
|
new AdobePathReader((DataInput) null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void testCreateEmpty() {
|
||||||
|
new AdobePathReader(new byte[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void testCreateShortPath() {
|
||||||
|
new AdobePathReader(new byte[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void testCreateImpossiblePath() {
|
||||||
|
new AdobePathReader(new byte[7]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreate() {
|
||||||
|
new AdobePathReader(new byte[52]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoPath() throws IOException {
|
||||||
|
Path2D path = new AdobePathReader(new byte[26]).readPath();
|
||||||
|
assertNotNull(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IIOException.class)
|
||||||
|
public void testShortPath() throws IOException {
|
||||||
|
byte[] data = new byte[26];
|
||||||
|
ByteBuffer buffer = ByteBuffer.wrap(data);
|
||||||
|
buffer.putShort((short) AdobePathSegment.CLOSED_SUBPATH_LENGTH_RECORD);
|
||||||
|
buffer.putShort((short) 1);
|
||||||
|
|
||||||
|
Path2D path = new AdobePathReader(data).readPath();
|
||||||
|
assertNotNull(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IIOException.class)
|
||||||
|
public void testShortPathToo() throws IOException {
|
||||||
|
byte[] data = new byte[52];
|
||||||
|
ByteBuffer buffer = ByteBuffer.wrap(data);
|
||||||
|
buffer.putShort((short) AdobePathSegment.CLOSED_SUBPATH_LENGTH_RECORD);
|
||||||
|
buffer.putShort((short) 2);
|
||||||
|
buffer.position(buffer.position() + 22);
|
||||||
|
buffer.putShort((short) AdobePathSegment.CLOSED_SUBPATH_BEZIER_LINKED);
|
||||||
|
|
||||||
|
Path2D path = new AdobePathReader(data).readPath();
|
||||||
|
assertNotNull(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IIOException.class)
|
||||||
|
public void testLongPath() throws IOException {
|
||||||
|
byte[] data = new byte[78];
|
||||||
|
ByteBuffer buffer = ByteBuffer.wrap(data);
|
||||||
|
buffer.putShort((short) AdobePathSegment.CLOSED_SUBPATH_LENGTH_RECORD);
|
||||||
|
buffer.putShort((short) 1);
|
||||||
|
buffer.position(buffer.position() + 22);
|
||||||
|
buffer.putShort((short) AdobePathSegment.CLOSED_SUBPATH_BEZIER_LINKED);
|
||||||
|
buffer.position(buffer.position() + 24);
|
||||||
|
buffer.putShort((short) AdobePathSegment.CLOSED_SUBPATH_BEZIER_LINKED);
|
||||||
|
|
||||||
|
Path2D path = new AdobePathReader(data).readPath();
|
||||||
|
assertNotNull(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IIOException.class)
|
||||||
|
public void testPathMissingLength() throws IOException {
|
||||||
|
byte[] data = new byte[26];
|
||||||
|
ByteBuffer buffer = ByteBuffer.wrap(data);
|
||||||
|
buffer.putShort((short) AdobePathSegment.CLOSED_SUBPATH_BEZIER_LINKED);
|
||||||
|
|
||||||
|
Path2D path = new AdobePathReader(data).readPath();
|
||||||
|
assertNotNull(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSimplePath() throws IOException {
|
||||||
|
// We'll read this from a real file, with hardcoded offsets for simplicity
|
||||||
|
// PSD IRB: offset: 34, length: 32598
|
||||||
|
// Clipping path: offset: 31146, length: 1248
|
||||||
|
ImageInputStream stream = PathsTest.resourceAsIIOStream("/psd/grape_with_path.psd");
|
||||||
|
stream.seek(34 + 31146);
|
||||||
|
byte[] data = new byte[1248];
|
||||||
|
stream.readFully(data);
|
||||||
|
|
||||||
|
Path2D path = new AdobePathReader(data).readPath();
|
||||||
|
|
||||||
|
assertNotNull(path);
|
||||||
|
assertPathEquals(path, readExpectedPath("/ser/grape-path.ser"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testComplexPath() throws IOException {
|
||||||
|
// We'll read this from a real file, with hardcoded offsets for simplicity
|
||||||
|
// PSD IRB: offset: 16970, length: 11152
|
||||||
|
// Clipping path: offset: 9250, length: 1534
|
||||||
|
ImageInputStream stream = PathsTest.resourceAsIIOStream("/tiff/big-endian-multiple-clips.tif");
|
||||||
|
stream.seek(16970 + 9250);
|
||||||
|
byte[] data = new byte[1534];
|
||||||
|
stream.readFully(data);
|
||||||
|
|
||||||
|
Path2D path = new AdobePathReader(data).readPath();
|
||||||
|
|
||||||
|
assertNotNull(path);
|
||||||
|
assertPathEquals(path, readExpectedPath("/ser/multiple-clips.ser"));
|
||||||
|
}
|
||||||
|
}
|
||||||
+38
-18
@@ -63,7 +63,7 @@ public class AdobePathSegmentTest {
|
|||||||
AdobePathSegment segment = new AdobePathSegment(AdobePathSegment.OPEN_SUBPATH_LENGTH_RECORD, 42);
|
AdobePathSegment segment = new AdobePathSegment(AdobePathSegment.OPEN_SUBPATH_LENGTH_RECORD, 42);
|
||||||
|
|
||||||
assertEquals(AdobePathSegment.OPEN_SUBPATH_LENGTH_RECORD, segment.selector);
|
assertEquals(AdobePathSegment.OPEN_SUBPATH_LENGTH_RECORD, segment.selector);
|
||||||
assertEquals(42, segment.length);
|
assertEquals(42, segment.lengthOrRule);
|
||||||
assertEquals(-1, segment.cppx, 0);
|
assertEquals(-1, segment.cppx, 0);
|
||||||
assertEquals(-1, segment.cppy, 0);
|
assertEquals(-1, segment.cppy, 0);
|
||||||
assertEquals(-1, segment.apx, 0);
|
assertEquals(-1, segment.apx, 0);
|
||||||
@@ -82,7 +82,7 @@ public class AdobePathSegmentTest {
|
|||||||
AdobePathSegment segment = new AdobePathSegment(AdobePathSegment.CLOSED_SUBPATH_LENGTH_RECORD, 27);
|
AdobePathSegment segment = new AdobePathSegment(AdobePathSegment.CLOSED_SUBPATH_LENGTH_RECORD, 27);
|
||||||
|
|
||||||
assertEquals(AdobePathSegment.CLOSED_SUBPATH_LENGTH_RECORD, segment.selector);
|
assertEquals(AdobePathSegment.CLOSED_SUBPATH_LENGTH_RECORD, segment.selector);
|
||||||
assertEquals(27, segment.length);
|
assertEquals(27, segment.lengthOrRule);
|
||||||
assertEquals(-1, segment.cppx, 0);
|
assertEquals(-1, segment.cppx, 0);
|
||||||
assertEquals(-1, segment.cppy, 0);
|
assertEquals(-1, segment.cppy, 0);
|
||||||
assertEquals(-1, segment.apx, 0);
|
assertEquals(-1, segment.apx, 0);
|
||||||
@@ -98,7 +98,7 @@ public class AdobePathSegmentTest {
|
|||||||
AdobePathSegment segment = new AdobePathSegment(AdobePathSegment.OPEN_SUBPATH_BEZIER_LINKED, .5, .5, 0, 0, 1, 1);
|
AdobePathSegment segment = new AdobePathSegment(AdobePathSegment.OPEN_SUBPATH_BEZIER_LINKED, .5, .5, 0, 0, 1, 1);
|
||||||
|
|
||||||
assertEquals(AdobePathSegment.OPEN_SUBPATH_BEZIER_LINKED, segment.selector);
|
assertEquals(AdobePathSegment.OPEN_SUBPATH_BEZIER_LINKED, segment.selector);
|
||||||
assertEquals(-1, segment.length);
|
assertEquals(-1, segment.lengthOrRule);
|
||||||
assertEquals(.5, segment.cppx, 0);
|
assertEquals(.5, segment.cppx, 0);
|
||||||
assertEquals(.5, segment.cppy, 0);
|
assertEquals(.5, segment.cppy, 0);
|
||||||
assertEquals(0, segment.apx, 0);
|
assertEquals(0, segment.apx, 0);
|
||||||
@@ -113,8 +113,13 @@ public class AdobePathSegmentTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void testCreateOpenLinkedRecordNegative() {
|
public void testCreateOpenLinkedRecordOutOfRangeNegative() {
|
||||||
new AdobePathSegment(AdobePathSegment.OPEN_SUBPATH_BEZIER_LINKED, -.5, -.5, 0, 0, 1, 1);
|
new AdobePathSegment(AdobePathSegment.OPEN_SUBPATH_BEZIER_LINKED, -16.1, -16.1, 0, 0, 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void testCreateOpenLinkedRecordOutOfRangePositive() {
|
||||||
|
new AdobePathSegment(AdobePathSegment.OPEN_SUBPATH_BEZIER_LINKED, 16.1, 16.1, 0, 0, 1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -122,7 +127,7 @@ public class AdobePathSegmentTest {
|
|||||||
AdobePathSegment segment = new AdobePathSegment(AdobePathSegment.OPEN_SUBPATH_BEZIER_UNLINKED, .5, .5, 0, 0, 1, 1);
|
AdobePathSegment segment = new AdobePathSegment(AdobePathSegment.OPEN_SUBPATH_BEZIER_UNLINKED, .5, .5, 0, 0, 1, 1);
|
||||||
|
|
||||||
assertEquals(AdobePathSegment.OPEN_SUBPATH_BEZIER_UNLINKED, segment.selector);
|
assertEquals(AdobePathSegment.OPEN_SUBPATH_BEZIER_UNLINKED, segment.selector);
|
||||||
assertEquals(-1, segment.length);
|
assertEquals(-1, segment.lengthOrRule);
|
||||||
assertEquals(.5, segment.cppx, 0);
|
assertEquals(.5, segment.cppx, 0);
|
||||||
assertEquals(.5, segment.cppy, 0);
|
assertEquals(.5, segment.cppy, 0);
|
||||||
assertEquals(0, segment.apx, 0);
|
assertEquals(0, segment.apx, 0);
|
||||||
@@ -138,8 +143,13 @@ public class AdobePathSegmentTest {
|
|||||||
|
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void testCreateOpenUnlinkedRecordNegative() {
|
public void testCreateOpenUnlinkedRecordOutOfRangeNegative() {
|
||||||
new AdobePathSegment(AdobePathSegment.OPEN_SUBPATH_BEZIER_UNLINKED, -.5, -.5, 0, 0, 1, 1);
|
new AdobePathSegment(AdobePathSegment.OPEN_SUBPATH_BEZIER_UNLINKED, -16.5, 0, 0, 0, 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void testCreateOpenUnlinkedRecorOutOfRangePositive() {
|
||||||
|
new AdobePathSegment(AdobePathSegment.OPEN_SUBPATH_BEZIER_UNLINKED, 0, -17, 0, 0, 16.5, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Closed subpath
|
/// Closed subpath
|
||||||
@@ -149,7 +159,7 @@ public class AdobePathSegmentTest {
|
|||||||
AdobePathSegment segment = new AdobePathSegment(AdobePathSegment.CLOSED_SUBPATH_BEZIER_LINKED, .5, .5, 0, 0, 1, 1);
|
AdobePathSegment segment = new AdobePathSegment(AdobePathSegment.CLOSED_SUBPATH_BEZIER_LINKED, .5, .5, 0, 0, 1, 1);
|
||||||
|
|
||||||
assertEquals(AdobePathSegment.CLOSED_SUBPATH_BEZIER_LINKED, segment.selector);
|
assertEquals(AdobePathSegment.CLOSED_SUBPATH_BEZIER_LINKED, segment.selector);
|
||||||
assertEquals(-1, segment.length);
|
assertEquals(-1, segment.lengthOrRule);
|
||||||
assertEquals(.5, segment.cppx, 0);
|
assertEquals(.5, segment.cppx, 0);
|
||||||
assertEquals(.5, segment.cppy, 0);
|
assertEquals(.5, segment.cppy, 0);
|
||||||
assertEquals(0, segment.apx, 0);
|
assertEquals(0, segment.apx, 0);
|
||||||
@@ -164,8 +174,13 @@ public class AdobePathSegmentTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void testCreateClosedLinkedRecordNegative() {
|
public void testCreateClosedLinkedRecordOutOfRangeNegative() {
|
||||||
new AdobePathSegment(AdobePathSegment.CLOSED_SUBPATH_BEZIER_LINKED, -.5, -.5, 0, 0, 1, 1);
|
new AdobePathSegment(AdobePathSegment.CLOSED_SUBPATH_BEZIER_LINKED, -16.5, -.5, 0, 0, 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void testCreateClosedLinkedRecordOutOfRangePositive() {
|
||||||
|
new AdobePathSegment(AdobePathSegment.CLOSED_SUBPATH_BEZIER_LINKED, .5, 16.5, 0, 0, 1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -173,7 +188,7 @@ public class AdobePathSegmentTest {
|
|||||||
AdobePathSegment segment = new AdobePathSegment(AdobePathSegment.CLOSED_SUBPATH_BEZIER_UNLINKED, .5, .5, 0, 0, 1, 1);
|
AdobePathSegment segment = new AdobePathSegment(AdobePathSegment.CLOSED_SUBPATH_BEZIER_UNLINKED, .5, .5, 0, 0, 1, 1);
|
||||||
|
|
||||||
assertEquals(AdobePathSegment.CLOSED_SUBPATH_BEZIER_UNLINKED, segment.selector);
|
assertEquals(AdobePathSegment.CLOSED_SUBPATH_BEZIER_UNLINKED, segment.selector);
|
||||||
assertEquals(-1, segment.length);
|
assertEquals(-1, segment.lengthOrRule);
|
||||||
assertEquals(.5, segment.cppx, 0);
|
assertEquals(.5, segment.cppx, 0);
|
||||||
assertEquals(.5, segment.cppy, 0);
|
assertEquals(.5, segment.cppy, 0);
|
||||||
assertEquals(0, segment.apx, 0);
|
assertEquals(0, segment.apx, 0);
|
||||||
@@ -189,17 +204,22 @@ public class AdobePathSegmentTest {
|
|||||||
|
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void testCreateClosedUnlinkedRecordNegative() {
|
public void testCreateClosedUnlinkedRecordOutOfRangeNegative() {
|
||||||
new AdobePathSegment(AdobePathSegment.CLOSED_SUBPATH_BEZIER_UNLINKED, -.5, -.5, 0, 0, 1, 1);
|
new AdobePathSegment(AdobePathSegment.CLOSED_SUBPATH_BEZIER_UNLINKED, -.5, -16.5, 0, 0, 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void testCreateClosedUnlinkedRecordOutOfRangePositive() {
|
||||||
|
new AdobePathSegment(AdobePathSegment.CLOSED_SUBPATH_BEZIER_UNLINKED, 16.5, .5, 0, 0, 1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testToStringRule() {
|
public void testToStringRule() {
|
||||||
String string = new AdobePathSegment(AdobePathSegment.INITIAL_FILL_RULE_RECORD, 2).toString();
|
String string = new AdobePathSegment(AdobePathSegment.INITIAL_FILL_RULE_RECORD, 0).toString();
|
||||||
assertTrue(string, string.startsWith("Rule"));
|
assertTrue(string, string.startsWith("Rule"));
|
||||||
assertTrue(string, string.contains("Initial"));
|
assertTrue(string, string.contains("Initial"));
|
||||||
assertTrue(string, string.contains("fill"));
|
assertTrue(string, string.contains("fill"));
|
||||||
assertTrue(string, string.contains("rule=2"));
|
assertTrue(string, string.contains("rule=0"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -208,13 +228,13 @@ public class AdobePathSegmentTest {
|
|||||||
assertTrue(string, string.startsWith("Len"));
|
assertTrue(string, string.startsWith("Len"));
|
||||||
assertTrue(string, string.contains("Closed"));
|
assertTrue(string, string.contains("Closed"));
|
||||||
assertTrue(string, string.contains("subpath"));
|
assertTrue(string, string.contains("subpath"));
|
||||||
assertTrue(string, string.contains("totalPoints=2"));
|
assertTrue(string, string.contains("length=2"));
|
||||||
|
|
||||||
string = new AdobePathSegment(AdobePathSegment.OPEN_SUBPATH_LENGTH_RECORD, 42).toString();
|
string = new AdobePathSegment(AdobePathSegment.OPEN_SUBPATH_LENGTH_RECORD, 42).toString();
|
||||||
assertTrue(string, string.startsWith("Len"));
|
assertTrue(string, string.startsWith("Len"));
|
||||||
assertTrue(string, string.contains("Open"));
|
assertTrue(string, string.contains("Open"));
|
||||||
assertTrue(string, string.contains("subpath"));
|
assertTrue(string, string.contains("subpath"));
|
||||||
assertTrue(string, string.contains("totalPoints=42"));
|
assertTrue(string, string.contains("length=42"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
+416
@@ -0,0 +1,416 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Harald Kuhr
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.twelvemonkeys.imageio.path;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import javax.imageio.stream.ImageInputStream;
|
||||||
|
import javax.imageio.stream.ImageOutputStream;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.geom.*;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import static com.twelvemonkeys.imageio.path.AdobePathSegment.*;
|
||||||
|
import static com.twelvemonkeys.imageio.path.PathsTest.assertPathEquals;
|
||||||
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AdobePathWriterTest.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
|
* @author last modified by haraldk: harald.kuhr$
|
||||||
|
* @version : AdobePathWriterTest.java,v 1.0 2020-01-02 harald.kuhr Exp$
|
||||||
|
*/
|
||||||
|
public class AdobePathWriterTest {
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void testCreateWriterNull() {
|
||||||
|
new AdobePathWriter(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void testCreateWriterInvalid() {
|
||||||
|
new AdobePathWriter(new Path2D.Double(Path2D.WIND_NON_ZERO));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void testCreateWriterOutOfBounds() {
|
||||||
|
Path2D path = new Path2D.Float(Path2D.WIND_EVEN_ODD);
|
||||||
|
path.append(new Ellipse2D.Double(.5, 0.5, 2, 2), false);
|
||||||
|
|
||||||
|
new AdobePathWriter(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateWriterValid() {
|
||||||
|
Path2D path = new Path2D.Float(Path2D.WIND_EVEN_ODD);
|
||||||
|
path.append(new Ellipse2D.Double(.25, .25, .5, .5), false);
|
||||||
|
|
||||||
|
new AdobePathWriter(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateWriterMulti() {
|
||||||
|
Path2D path = new GeneralPath(Path2D.WIND_EVEN_ODD);
|
||||||
|
path.append(new Ellipse2D.Float(.25f, .25f, .5f, .5f), false);
|
||||||
|
path.append(new Rectangle2D.Double(0, 0, 1, .5), false);
|
||||||
|
path.append(new Polygon(new int[] {1, 2, 0, 1}, new int[] {0, 2, 2, 0}, 4)
|
||||||
|
.getPathIterator(AffineTransform.getScaleInstance(1 / 2.0, 1 / 2.0)), false);
|
||||||
|
|
||||||
|
new AdobePathWriter(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void testCreateNotClosed() {
|
||||||
|
GeneralPath path = new GeneralPath(Path2D.WIND_EVEN_ODD);
|
||||||
|
path.moveTo(.5, .5);
|
||||||
|
path.lineTo(1, .5);
|
||||||
|
path.curveTo(1, 1, 1, 1, .5, 1);
|
||||||
|
|
||||||
|
new AdobePathWriter(path).writePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateClosed() {
|
||||||
|
GeneralPath path = new GeneralPath(Path2D.WIND_EVEN_ODD);
|
||||||
|
path.moveTo(.5, .5);
|
||||||
|
path.lineTo(1, .5);
|
||||||
|
path.curveTo(1, 1, 1, 1, .5, 1);
|
||||||
|
path.closePath();
|
||||||
|
|
||||||
|
byte[] bytes = new AdobePathWriter(path).writePath();
|
||||||
|
|
||||||
|
assertEquals(6 * 26, bytes.length);
|
||||||
|
|
||||||
|
int off = 0;
|
||||||
|
|
||||||
|
// Path/initial fill rule: Even-Odd (0)
|
||||||
|
assertArrayEquals(new byte[] {0, PATH_FILL_RULE_RECORD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
assertArrayEquals(new byte[] {0, INITIAL_FILL_RULE_RECORD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
|
||||||
|
// Rectangle 1: 0, 0, 1, .5
|
||||||
|
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_LENGTH_RECORD, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_UNLINKED, 0, -128, 0, 0, 0, -128, 0, 0, 0, -128, 0, 0, 0, -128, 0, 0, 0, -128, 0, 0, 1, 0, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_UNLINKED, 0, -128, 0, 0, 1, 0, 0, 0, 0, -128, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_UNLINKED, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, -128, 0, 0, 0, -128, 0, 0, 0, -128, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
|
||||||
|
// Sanity
|
||||||
|
assertEquals(bytes.length, off);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateImplicitClosed() {
|
||||||
|
GeneralPath path = new GeneralPath(Path2D.WIND_EVEN_ODD);
|
||||||
|
path.moveTo(.5, .5);
|
||||||
|
path.lineTo(1, .5);
|
||||||
|
path.curveTo(1, 1, 1, 1, .5, 1);
|
||||||
|
path.lineTo(.5, .5);
|
||||||
|
|
||||||
|
byte[] bytes = new AdobePathWriter(path).writePath();
|
||||||
|
|
||||||
|
assertEquals(6 * 26, bytes.length);
|
||||||
|
|
||||||
|
int off = 0;
|
||||||
|
|
||||||
|
// Path/initial fill rule: Even-Odd (0)
|
||||||
|
assertArrayEquals(new byte[] {0, PATH_FILL_RULE_RECORD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
assertArrayEquals(new byte[] {0, INITIAL_FILL_RULE_RECORD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
|
||||||
|
// Rectangle 1: 0, 0, 1, .5
|
||||||
|
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_LENGTH_RECORD, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_UNLINKED, 0, -128, 0, 0, 0, -128, 0, 0, 0, -128, 0, 0, 0, -128, 0, 0, 0, -128, 0, 0, 1, 0, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_UNLINKED, 0, -128, 0, 0, 1, 0, 0, 0, 0, -128, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_UNLINKED, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, -128, 0, 0, 0, -128, 0, 0, 0, -128, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
|
||||||
|
// Sanity
|
||||||
|
assertEquals(bytes.length, off);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateDoubleClosed() {
|
||||||
|
GeneralPath path = new GeneralPath(Path2D.WIND_EVEN_ODD);
|
||||||
|
path.moveTo(.5, .5);
|
||||||
|
path.lineTo(1, .5);
|
||||||
|
path.curveTo(1, 1, 1, 1, .5, 1);
|
||||||
|
path.lineTo(.5, .5);
|
||||||
|
path.closePath();
|
||||||
|
|
||||||
|
byte[] bytes = new AdobePathWriter(path).writePath();
|
||||||
|
|
||||||
|
assertEquals(6 * 26, bytes.length);
|
||||||
|
|
||||||
|
int off = 0;
|
||||||
|
|
||||||
|
// Path/initial fill rule: Even-Odd (0)
|
||||||
|
assertArrayEquals(new byte[] {0, PATH_FILL_RULE_RECORD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
assertArrayEquals(new byte[] {0, INITIAL_FILL_RULE_RECORD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
|
||||||
|
// Rectangle 1: 0, 0, 1, .5
|
||||||
|
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_LENGTH_RECORD, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_UNLINKED, 0, -128, 0, 0, 0, -128, 0, 0, 0, -128, 0, 0, 0, -128, 0, 0, 0, -128, 0, 0, 1, 0, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_UNLINKED, 0, -128, 0, 0, 1, 0, 0, 0, 0, -128, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_UNLINKED, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, -128, 0, 0, 0, -128, 0, 0, 0, -128, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
|
||||||
|
// Sanity
|
||||||
|
assertEquals(bytes.length, off);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWriteToStream() throws IOException {
|
||||||
|
Path2D path = new GeneralPath(Path2D.WIND_EVEN_ODD);
|
||||||
|
path.append(new Ellipse2D.Double(0, 0, 1, 1), false);
|
||||||
|
path.append(new Ellipse2D.Double(.5, .5, .5, .5), false);
|
||||||
|
path.append(new Ellipse2D.Double(.25, .25, .5, .5), false);
|
||||||
|
|
||||||
|
AdobePathWriter pathCreator = new AdobePathWriter(path);
|
||||||
|
|
||||||
|
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
|
||||||
|
try (ImageOutputStream output = ImageIO.createImageOutputStream(byteStream)) {
|
||||||
|
pathCreator.writePath(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(17 * 26, byteStream.size());
|
||||||
|
|
||||||
|
byte[] bytes = byteStream.toByteArray();
|
||||||
|
|
||||||
|
int off = 0;
|
||||||
|
|
||||||
|
// Path/initial fill rule: Even-Odd (0)
|
||||||
|
assertArrayEquals(new byte[] {0, PATH_FILL_RULE_RECORD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
assertArrayEquals(new byte[] {0, INITIAL_FILL_RULE_RECORD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
|
||||||
|
// Elipse 1: 0, 0, 1, 1
|
||||||
|
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_LENGTH_RECORD, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_LINKED, 0, 57, 78, -68, 1, 0, 0, 0, 0, -128, 0, 0, 1, 0, 0, 0, 0, -58, -79, 68, 1, 0, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_LINKED, 1, 0, 0, 0, 0, -58, -79, 68, 1, 0, 0, 0, 0, -128, 0, 0, 1, 0, 0, 0, 0, 57, 78, -68},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_LINKED, 0, -58, -79, 68, 0, 0, 0, 0, 0, -128, 0, 0, 0, 0, 0, 0, 0, 57, 78, -68, 0, 0, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_LINKED, 0, 0, 0, 0, 0, 57, 78, -68, 0, 0, 0, 0, 0, -128, 0, 0, 0, 0, 0, 0, 0, -58, -79, 68},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
|
||||||
|
// Elipse 2: .5, .5, .5, .5
|
||||||
|
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_LENGTH_RECORD, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_LINKED, 0, -100, -89, 94, 1, 0, 0, 0, 0, -64, 0, 0, 1, 0, 0, 0, 0, -29, 88, -94, 1, 0, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_LINKED, 1, 0, 0, 0, 0, -29, 88, -94, 1, 0, 0, 0, 0, -64, 0, 0, 1, 0, 0, 0, 0, -100, -89, 94},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_LINKED, 0, -29, 88, -94, 0, -128, 0, 0, 0, -64, 0, 0, 0, -128, 0, 0, 0, -100, -89, 94, 0, -128, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_LINKED, 0, -128, 0, 0, 0, -100, -89, 94, 0, -128, 0, 0, 0, -64, 0, 0, 0, -128, 0, 0, 0, -29, 88, -94},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
|
||||||
|
// Elipse32: .25, .25, .5, .5
|
||||||
|
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_LENGTH_RECORD, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_LINKED, 0, 92, -89, 94, 0, -64, 0, 0, 0, -128, 0, 0, 0, -64, 0, 0, 0, -93, 88, -94, 0, -64, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_LINKED, 0, -64, 0, 0, 0, -93, 88, -94, 0, -64, 0, 0, 0, -128, 0, 0, 0, -64, 0, 0, 0, 92, -89, 94},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_LINKED, 0, -93, 88, -94, 0, 64, 0, 0, 0, -128, 0, 0, 0, 64, 0, 0, 0, 92, -89, 94, 0, 64, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_LINKED, 0, 64, 0, 0, 0, 92, -89, 94, 0, 64, 0, 0, 0, -128, 0, 0, 0, 64, 0, 0, 0, -93, 88, -94},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
|
||||||
|
// Sanity
|
||||||
|
assertEquals(bytes.length, off);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateArray() {
|
||||||
|
Path2D path = new GeneralPath(Path2D.WIND_EVEN_ODD);
|
||||||
|
path.append(new Rectangle2D.Double(0, 0, 1, .5), false);
|
||||||
|
path.append(new Rectangle2D.Double(.25, .25, .5, .5), false);
|
||||||
|
|
||||||
|
AdobePathWriter pathCreator = new AdobePathWriter(path);
|
||||||
|
|
||||||
|
byte[] bytes = pathCreator.writePath();
|
||||||
|
|
||||||
|
assertEquals(12 * 26, bytes.length);
|
||||||
|
|
||||||
|
int off = 0;
|
||||||
|
|
||||||
|
// Path/initial fill rule: Even-Odd (0)
|
||||||
|
assertArrayEquals(new byte[] {0, PATH_FILL_RULE_RECORD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
assertArrayEquals(new byte[] {0, INITIAL_FILL_RULE_RECORD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
|
||||||
|
// Rectangle 1: 0, 0, 1, .5
|
||||||
|
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_LENGTH_RECORD, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_UNLINKED, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_UNLINKED, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, -128, 0, 0, 1, 0, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_UNLINKED, 0, -128, 0, 0, 1, 0, 0, 0, 0, -128, 0, 0, 1, 0, 0, 0, 0, -128, 0, 0, 0, 0, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_UNLINKED, 0, -128, 0, 0, 0, 0, 0, 0, 0, -128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
|
||||||
|
// Rectangle 2: .25, .25, .5, .5
|
||||||
|
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_LENGTH_RECORD, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_UNLINKED, 0, 64, 0, 0, 0, 64, 0, 0, 0, 64, 0, 0, 0, 64, 0, 0, 0, 64, 0, 0, 0, -64, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_UNLINKED, 0, 64, 0, 0, 0, -64, 0, 0, 0, 64, 0, 0, 0, -64, 0, 0, 0, -64, 0, 0, 0, -64, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_UNLINKED, 0, -64, 0, 0, 0, -64, 0, 0, 0, -64, 0, 0, 0, -64, 0, 0, 0, -64, 0, 0, 0, 64, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_UNLINKED, 0, -64, 0, 0, 0, 64, 0, 0, 0, -64, 0, 0, 0, 64, 0, 0, 0, 64, 0, 0, 0, 64, 0, 0},
|
||||||
|
Arrays.copyOfRange(bytes, off, off += 26));
|
||||||
|
|
||||||
|
// Sanity
|
||||||
|
assertEquals(bytes.length, off);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRoundtrip0() throws IOException {
|
||||||
|
Path2D path = new GeneralPath(Path2D.WIND_EVEN_ODD);
|
||||||
|
path.append(new Rectangle2D.Double(0, 0, 1, .5), false);
|
||||||
|
path.append(new Rectangle2D.Double(.25, .25, .5, .5), false);
|
||||||
|
|
||||||
|
byte[] bytes = new AdobePathWriter(path).writePath();
|
||||||
|
Path2D readPath = new AdobePathReader(new ByteArrayImageInputStream(bytes)).readPath();
|
||||||
|
|
||||||
|
assertEquals(path.getWindingRule(), readPath.getWindingRule());
|
||||||
|
assertEquals(path.getBounds2D(), readPath.getBounds2D());
|
||||||
|
|
||||||
|
// TODO: Would be nice, but hard to do, as we convert all points to cubic...
|
||||||
|
// assertPathEquals(path, readPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRoundtrip1() throws IOException {
|
||||||
|
// We'll read this from a real file, with hardcoded offsets for simplicity
|
||||||
|
// PSD IRB: offset: 34, length: 32598
|
||||||
|
// Clipping path: offset: 31146, length: 1248
|
||||||
|
ImageInputStream stream = PathsTest.resourceAsIIOStream("/psd/grape_with_path.psd");
|
||||||
|
stream.seek(34 + 31146);
|
||||||
|
byte[] data = new byte[1248];
|
||||||
|
stream.readFully(data);
|
||||||
|
|
||||||
|
Path2D path = new AdobePathReader(data).readPath();
|
||||||
|
byte[] bytes = new AdobePathWriter(path).writePath();
|
||||||
|
|
||||||
|
Path2D readPath = new AdobePathReader(new ByteArrayImageInputStream(bytes)).readPath();
|
||||||
|
assertEquals(path.getWindingRule(), readPath.getWindingRule());
|
||||||
|
assertEquals(path.getBounds2D(), readPath.getBounds2D());
|
||||||
|
|
||||||
|
assertPathEquals(path, readPath);
|
||||||
|
|
||||||
|
assertEquals(data.length, bytes.length);
|
||||||
|
|
||||||
|
// Path segment 3 contains some unknown bits in the filler bytes, we'll ignore those...
|
||||||
|
cleanLengthRecords(data);
|
||||||
|
|
||||||
|
assertEquals(formatSegments(data), formatSegments(bytes));
|
||||||
|
assertArrayEquals(data, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void cleanLengthRecords(byte[] data) {
|
||||||
|
for (int i = 0; i < data.length; i += 26) {
|
||||||
|
if (data[i + 1] == CLOSED_SUBPATH_LENGTH_RECORD) {
|
||||||
|
// Clean everything after record type and length field
|
||||||
|
for (int j = 4; j < 26; j++) {
|
||||||
|
data[i + j] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String formatSegments(byte[] data) {
|
||||||
|
StringBuilder builder = new StringBuilder(data.length * 5);
|
||||||
|
|
||||||
|
for (int i = 0; i < data.length; i += 26) {
|
||||||
|
builder.append(Arrays.toString(Arrays.copyOfRange(data, i, i + 26))).append('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRoundtrip2() throws IOException {
|
||||||
|
// We'll read this from a real file, with hardcoded offsets for simplicity
|
||||||
|
// PSD IRB: offset: 16970, length: 11152
|
||||||
|
// Clipping path: offset: 9250, length: 1534
|
||||||
|
ImageInputStream stream = PathsTest.resourceAsIIOStream("/tiff/big-endian-multiple-clips.tif");
|
||||||
|
stream.seek(16970 + 9250);
|
||||||
|
byte[] data = new byte[1534];
|
||||||
|
stream.readFully(data);
|
||||||
|
|
||||||
|
Path2D path = new AdobePathReader(data).readPath();
|
||||||
|
byte[] bytes = new AdobePathWriter(path).writePath();
|
||||||
|
|
||||||
|
Path2D readPath = new AdobePathReader(new ByteArrayImageInputStream(bytes)).readPath();
|
||||||
|
assertEquals(path.getWindingRule(), readPath.getWindingRule());
|
||||||
|
assertEquals(path.getBounds2D(), readPath.getBounds2D());
|
||||||
|
|
||||||
|
assertPathEquals(path, readPath);
|
||||||
|
|
||||||
|
assertEquals(data.length, bytes.length);
|
||||||
|
|
||||||
|
// Path segment 3 and 48 contains some unknown bits in the filler bytes, we'll ignore that:
|
||||||
|
cleanLengthRecords(data);
|
||||||
|
|
||||||
|
assertEquals(formatSegments(data), formatSegments(bytes));
|
||||||
|
assertArrayEquals(data, bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
+54
-14
@@ -38,15 +38,18 @@ import org.junit.Test;
|
|||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import javax.imageio.spi.IIORegistry;
|
import javax.imageio.spi.IIORegistry;
|
||||||
import javax.imageio.stream.ImageInputStream;
|
import javax.imageio.stream.ImageInputStream;
|
||||||
|
import javax.imageio.stream.ImageOutputStream;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.geom.GeneralPath;
|
import java.awt.geom.GeneralPath;
|
||||||
import java.awt.geom.Path2D;
|
import java.awt.geom.Path2D;
|
||||||
import java.awt.geom.PathIterator;
|
import java.awt.geom.PathIterator;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.ObjectInputStream;
|
import java.io.ObjectInputStream;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
import static org.junit.Assume.assumeTrue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PathsTest.
|
* PathsTest.
|
||||||
@@ -125,12 +128,12 @@ public class PathsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void testApplyClippingPathNullPath() throws IOException {
|
public void testApplyClippingPathNullPath() {
|
||||||
Paths.applyClippingPath(null, new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY));
|
Paths.applyClippingPath(null, new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void testApplyClippingPathNullSource() throws IOException {
|
public void testApplyClippingPathNullSource() {
|
||||||
Paths.applyClippingPath(new GeneralPath(), null);
|
Paths.applyClippingPath(new GeneralPath(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,7 +150,7 @@ public class PathsTest {
|
|||||||
assertEquals(source.getWidth(), image.getWidth());
|
assertEquals(source.getWidth(), image.getWidth());
|
||||||
assertEquals(source.getHeight(), image.getHeight());
|
assertEquals(source.getHeight(), image.getHeight());
|
||||||
// Transparent
|
// Transparent
|
||||||
assertTrue(image.getColorModel().getTransparency() == Transparency.TRANSLUCENT);
|
assertEquals(Transparency.TRANSLUCENT, image.getColorModel().getTransparency());
|
||||||
|
|
||||||
// Corners (at least) should be transparent
|
// Corners (at least) should be transparent
|
||||||
assertEquals(0, image.getRGB(0, 0));
|
assertEquals(0, image.getRGB(0, 0));
|
||||||
@@ -161,8 +164,9 @@ public class PathsTest {
|
|||||||
// TODO: Mor sophisticated test that tests all pixels outside path...
|
// TODO: Mor sophisticated test that tests all pixels outside path...
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ConstantConditions")
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void testApplyClippingPathNullDestination() throws IOException {
|
public void testApplyClippingPathNullDestination() {
|
||||||
Paths.applyClippingPath(new GeneralPath(), new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY), null);
|
Paths.applyClippingPath(new GeneralPath(), new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,7 +213,7 @@ public class PathsTest {
|
|||||||
assertEquals(857, image.getWidth());
|
assertEquals(857, image.getWidth());
|
||||||
assertEquals(1800, image.getHeight());
|
assertEquals(1800, image.getHeight());
|
||||||
// Transparent
|
// Transparent
|
||||||
assertTrue(image.getColorModel().getTransparency() == Transparency.TRANSLUCENT);
|
assertEquals(Transparency.TRANSLUCENT, image.getColorModel().getTransparency());
|
||||||
|
|
||||||
// Corners (at least) should be transparent
|
// Corners (at least) should be transparent
|
||||||
assertEquals(0, image.getRGB(0, 0));
|
assertEquals(0, image.getRGB(0, 0));
|
||||||
@@ -230,34 +234,70 @@ public class PathsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Path2D readExpectedPath(final String resource) throws IOException {
|
static Path2D readExpectedPath(final String resource) throws IOException {
|
||||||
ObjectInputStream ois = new ObjectInputStream(PathsTest.class.getResourceAsStream(resource));
|
try (ObjectInputStream ois = new ObjectInputStream(PathsTest.class.getResourceAsStream(resource))) {
|
||||||
|
|
||||||
try {
|
|
||||||
return (Path2D) ois.readObject();
|
return (Path2D) ois.readObject();
|
||||||
}
|
}
|
||||||
catch (ClassNotFoundException e) {
|
catch (ClassNotFoundException e) {
|
||||||
throw new IOException(e);
|
throw new IOException(e);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
ois.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void assertPathEquals(final Path2D expectedPath, final Path2D actualPath) {
|
static void assertPathEquals(final Path2D expectedPath, final Path2D actualPath) {
|
||||||
|
assertNotNull("Expected path is null, check your tests...", expectedPath);
|
||||||
|
assertNotNull(actualPath);
|
||||||
|
|
||||||
PathIterator expectedIterator = expectedPath.getPathIterator(null);
|
PathIterator expectedIterator = expectedPath.getPathIterator(null);
|
||||||
PathIterator actualIterator = actualPath.getPathIterator(null);
|
PathIterator actualIterator = actualPath.getPathIterator(null);
|
||||||
|
|
||||||
float[] expectedCoords = new float[6];
|
float[] expectedCoords = new float[6];
|
||||||
float[] actualCoords = new float[6];
|
float[] actualCoords = new float[6];
|
||||||
|
|
||||||
while(!actualIterator.isDone()) {
|
while(!expectedIterator.isDone()) {
|
||||||
|
assertFalse("Less points than expected", actualIterator.isDone());
|
||||||
|
|
||||||
int expectedType = expectedIterator.currentSegment(expectedCoords);
|
int expectedType = expectedIterator.currentSegment(expectedCoords);
|
||||||
int actualType = actualIterator.currentSegment(actualCoords);
|
int actualType = actualIterator.currentSegment(actualCoords);
|
||||||
|
|
||||||
assertEquals(expectedType, actualType);
|
assertEquals("Unexpected segment type", expectedType, actualType);
|
||||||
assertArrayEquals(expectedCoords, actualCoords, 0);
|
assertArrayEquals("Unexpected coordinates", expectedCoords, actualCoords, 0);
|
||||||
|
|
||||||
actualIterator.next();
|
actualIterator.next();
|
||||||
expectedIterator.next();
|
expectedIterator.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assertTrue("More points than expected", actualIterator.isDone());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWriteJPEG() throws IOException {
|
||||||
|
Path2D originalPath = readExpectedPath("/ser/multiple-clips.ser");
|
||||||
|
|
||||||
|
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||||
|
BufferedImage image = new BufferedImage(2, 2, BufferedImage.TYPE_3BYTE_BGR);
|
||||||
|
try (ImageOutputStream stream = ImageIO.createImageOutputStream(bytes)) {
|
||||||
|
boolean written = Paths.writeClipped(image, originalPath, "JPEG", stream);
|
||||||
|
assertTrue(written);
|
||||||
|
}
|
||||||
|
assertTrue(bytes.size() > 1024); // Actual size may be plugin specific...
|
||||||
|
|
||||||
|
Path2D actualPath = Paths.readPath(new ByteArrayImageInputStream(bytes.toByteArray()));
|
||||||
|
assertPathEquals(originalPath, actualPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWriteTIFF() throws IOException {
|
||||||
|
Path2D originalPath = readExpectedPath("/ser/grape-path.ser");
|
||||||
|
|
||||||
|
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||||
|
BufferedImage image = new BufferedImage(2, 2, BufferedImage.TYPE_INT_ARGB);
|
||||||
|
try (ImageOutputStream stream = ImageIO.createImageOutputStream(bytes)) {
|
||||||
|
boolean written = Paths.writeClipped(image, originalPath, "TIFF", stream);
|
||||||
|
assumeTrue(written); // TIFF support is optional
|
||||||
|
}
|
||||||
|
|
||||||
|
assertTrue(bytes.size() > 1024); // Actual size may be plugin specific...
|
||||||
|
|
||||||
|
Path2D actualPath = Paths.readPath(new ByteArrayImageInputStream(bytes.toByteArray()));
|
||||||
|
assertPathEquals(originalPath, actualPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.4.4-SNAPSHOT</version>
|
<version>3.6.5-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-core</artifactId>
|
<artifactId>imageio-core</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: Core</name>
|
<name>TwelveMonkeys :: ImageIO :: Core</name>
|
||||||
|
|||||||
@@ -314,12 +314,12 @@ public abstract class ImageReaderBase extends ImageReader {
|
|||||||
|
|
||||||
long dimension = (long) destWidth * destHeight;
|
long dimension = (long) destWidth * destHeight;
|
||||||
if (dimension > Integer.MAX_VALUE) {
|
if (dimension > Integer.MAX_VALUE) {
|
||||||
throw new IllegalArgumentException(String.format("destination width * height > Integer.MAX_VALUE: %d", dimension));
|
throw new IIOException(String.format("destination width * height > Integer.MAX_VALUE: %d", dimension));
|
||||||
}
|
}
|
||||||
|
|
||||||
long size = dimension * imageType.getSampleModel().getNumDataElements();
|
long size = dimension * imageType.getSampleModel().getNumDataElements();
|
||||||
if (size > Integer.MAX_VALUE) {
|
if (size > Integer.MAX_VALUE) {
|
||||||
throw new IllegalArgumentException(String.format("destination width * height * samplesPerPixel > Integer.MAX_VALUE: %d", size));
|
throw new IIOException(String.format("destination width * height * samplesPerPixel > Integer.MAX_VALUE: %d", size));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new image based on the type specifier
|
// Create a new image based on the type specifier
|
||||||
@@ -440,6 +440,7 @@ public abstract class ImageReaderBase extends ImageReader {
|
|||||||
static final String ZOOM_IN = "zoom-in";
|
static final String ZOOM_IN = "zoom-in";
|
||||||
static final String ZOOM_OUT = "zoom-out";
|
static final String ZOOM_OUT = "zoom-out";
|
||||||
static final String ZOOM_ACTUAL = "zoom-actual";
|
static final String ZOOM_ACTUAL = "zoom-actual";
|
||||||
|
static final String ZOOM_FIT = "zoom-fit";
|
||||||
|
|
||||||
private BufferedImage image;
|
private BufferedImage image;
|
||||||
|
|
||||||
@@ -515,9 +516,20 @@ public abstract class ImageReaderBase extends ImageReader {
|
|||||||
|
|
||||||
private void setupActions() {
|
private void setupActions() {
|
||||||
// Mac weirdness... VK_MINUS/VK_PLUS seems to map to english key map always...
|
// Mac weirdness... VK_MINUS/VK_PLUS seems to map to english key map always...
|
||||||
bindAction(new ZoomAction("Zoom in", 2), ZOOM_IN, KeyStroke.getKeyStroke('+'), KeyStroke.getKeyStroke(KeyEvent.VK_ADD, 0));
|
bindAction(new ZoomAction("Zoom in", 2), ZOOM_IN,
|
||||||
bindAction(new ZoomAction("Zoom out", .5), ZOOM_OUT, KeyStroke.getKeyStroke('-'), KeyStroke.getKeyStroke(KeyEvent.VK_SUBTRACT, 0));
|
KeyStroke.getKeyStroke('+'),
|
||||||
bindAction(new ZoomAction("Zoom actual"), ZOOM_ACTUAL, KeyStroke.getKeyStroke('0'), KeyStroke.getKeyStroke(KeyEvent.VK_0, 0));
|
KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
|
||||||
|
KeyStroke.getKeyStroke(KeyEvent.VK_ADD, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
|
||||||
|
bindAction(new ZoomAction("Zoom out", .5), ZOOM_OUT,
|
||||||
|
KeyStroke.getKeyStroke('-'),
|
||||||
|
KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
|
||||||
|
KeyStroke.getKeyStroke(KeyEvent.VK_SUBTRACT, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
|
||||||
|
bindAction(new ZoomAction("Zoom actual"), ZOOM_ACTUAL,
|
||||||
|
KeyStroke.getKeyStroke('0'),
|
||||||
|
KeyStroke.getKeyStroke(KeyEvent.VK_0, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
|
||||||
|
bindAction(new ZoomToFitAction("Zoom fit"), ZOOM_FIT,
|
||||||
|
KeyStroke.getKeyStroke('9'),
|
||||||
|
KeyStroke.getKeyStroke(KeyEvent.VK_9, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
|
||||||
|
|
||||||
bindAction(TransferHandler.getCopyAction(), (String) TransferHandler.getCopyAction().getValue(Action.NAME), KeyStroke.getKeyStroke(KeyEvent.VK_C, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
|
bindAction(TransferHandler.getCopyAction(), (String) TransferHandler.getCopyAction().getValue(Action.NAME), KeyStroke.getKeyStroke(KeyEvent.VK_C, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
|
||||||
bindAction(TransferHandler.getPasteAction(), (String) TransferHandler.getPasteAction().getValue(Action.NAME), KeyStroke.getKeyStroke(KeyEvent.VK_V, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
|
bindAction(TransferHandler.getPasteAction(), (String) TransferHandler.getPasteAction().getValue(Action.NAME), KeyStroke.getKeyStroke(KeyEvent.VK_V, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
|
||||||
@@ -534,6 +546,7 @@ public abstract class ImageReaderBase extends ImageReader {
|
|||||||
private JPopupMenu createPopupMenu() {
|
private JPopupMenu createPopupMenu() {
|
||||||
JPopupMenu popup = new JPopupMenu();
|
JPopupMenu popup = new JPopupMenu();
|
||||||
|
|
||||||
|
popup.add(getActionMap().get(ZOOM_FIT));
|
||||||
popup.add(getActionMap().get(ZOOM_ACTUAL));
|
popup.add(getActionMap().get(ZOOM_ACTUAL));
|
||||||
popup.add(getActionMap().get(ZOOM_IN));
|
popup.add(getActionMap().get(ZOOM_IN));
|
||||||
popup.add(getActionMap().get(ZOOM_OUT));
|
popup.add(getActionMap().get(ZOOM_OUT));
|
||||||
@@ -554,7 +567,7 @@ public abstract class ImageReaderBase extends ImageReader {
|
|||||||
addCheckBoxItem(new ChangeBackgroundAction("Dark", Color.DARK_GRAY), background, group);
|
addCheckBoxItem(new ChangeBackgroundAction("Dark", Color.DARK_GRAY), background, group);
|
||||||
addCheckBoxItem(new ChangeBackgroundAction("Black", Color.BLACK), background, group);
|
addCheckBoxItem(new ChangeBackgroundAction("Black", Color.BLACK), background, group);
|
||||||
background.addSeparator();
|
background.addSeparator();
|
||||||
ChooseBackgroundAction chooseBackgroundAction = new ChooseBackgroundAction("Choose...", defaultBG != null ? defaultBG : Color.BLUE);
|
ChooseBackgroundAction chooseBackgroundAction = new ChooseBackgroundAction("Choose...", defaultBG != null ? defaultBG : new Color(0xFF6600));
|
||||||
chooseBackgroundAction.putValue(Action.SELECTED_KEY, backgroundPaint == defaultBG);
|
chooseBackgroundAction.putValue(Action.SELECTED_KEY, backgroundPaint == defaultBG);
|
||||||
addCheckBoxItem(chooseBackgroundAction, background, group);
|
addCheckBoxItem(chooseBackgroundAction, background, group);
|
||||||
|
|
||||||
@@ -668,14 +681,41 @@ public abstract class ImageReaderBase extends ImageReader {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Icon current = getIcon();
|
Icon current = getIcon();
|
||||||
int w = (int) Math.max(Math.min(current.getIconWidth() * zoomFactor, image.getWidth() * 16), image.getWidth() / 16);
|
int w = Math.max(Math.min((int) (current.getIconWidth() * zoomFactor), image.getWidth() * 16), image.getWidth() / 16);
|
||||||
int h = (int) Math.max(Math.min(current.getIconHeight() * zoomFactor, image.getHeight() * 16), image.getHeight() / 16);
|
int h = Math.max(Math.min((int) (current.getIconHeight() * zoomFactor), image.getHeight() * 16), image.getHeight() / 16);
|
||||||
|
|
||||||
setIcon(new BufferedImageIcon(image, Math.max(w, 2), Math.max(h, 2), w > image.getWidth() || h > image.getHeight()));
|
setIcon(new BufferedImageIcon(image, Math.max(w, 2), Math.max(h, 2), w > image.getWidth() || h > image.getHeight()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class ZoomToFitAction extends ZoomAction {
|
||||||
|
public ZoomToFitAction(final String name) {
|
||||||
|
super(name, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void actionPerformed(final ActionEvent e) {
|
||||||
|
JComponent source = (JComponent) e.getSource();
|
||||||
|
|
||||||
|
if (source instanceof JMenuItem) {
|
||||||
|
JPopupMenu menu = (JPopupMenu) SwingUtilities.getAncestorOfClass(JPopupMenu.class, source);
|
||||||
|
source = (JComponent) menu.getInvoker();
|
||||||
|
}
|
||||||
|
|
||||||
|
Container container = SwingUtilities.getAncestorOfClass(JViewport.class, source);
|
||||||
|
|
||||||
|
double ratioX = container.getWidth() / (double) image.getWidth();
|
||||||
|
double ratioY = container.getHeight() / (double) image.getHeight();
|
||||||
|
|
||||||
|
double zoomFactor = Math.min(ratioX, ratioY);
|
||||||
|
|
||||||
|
int w = Math.max(Math.min((int) (image.getWidth() * zoomFactor), image.getWidth() * 16), image.getWidth() / 16);
|
||||||
|
int h = Math.max(Math.min((int) (image.getHeight() * zoomFactor), image.getHeight() * 16), image.getHeight() / 16);
|
||||||
|
|
||||||
|
setIcon(new BufferedImageIcon(image, w, h, zoomFactor > 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static class ImageTransferable implements Transferable {
|
private static class ImageTransferable implements Transferable {
|
||||||
private final BufferedImage image;
|
private final BufferedImage image;
|
||||||
|
|
||||||
@@ -694,7 +734,7 @@ public abstract class ImageReaderBase extends ImageReader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getTransferData(final DataFlavor flavor) throws UnsupportedFlavorException, IOException {
|
public Object getTransferData(final DataFlavor flavor) throws UnsupportedFlavorException {
|
||||||
if (isDataFlavorSupported(flavor)) {
|
if (isDataFlavorSupported(flavor)) {
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-17
@@ -57,8 +57,10 @@ public final class BufferedImageInputStream extends ImageInputStreamImpl impleme
|
|||||||
|
|
||||||
private ImageInputStream stream;
|
private ImageInputStream stream;
|
||||||
|
|
||||||
private ByteBuffer buffer;
|
private ByteBuffer buffer;
|
||||||
private ByteBuffer integralCache = ByteBuffer.allocate(8);
|
|
||||||
|
private final ByteBuffer integralCache = ByteBuffer.allocate(8);
|
||||||
|
private final byte[] integralCacheArray = integralCache.array();
|
||||||
|
|
||||||
public BufferedImageInputStream(final ImageInputStream pStream) throws IOException {
|
public BufferedImageInputStream(final ImageInputStream pStream) throws IOException {
|
||||||
this(pStream, DEFAULT_BUFFER_SIZE);
|
this(pStream, DEFAULT_BUFFER_SIZE);
|
||||||
@@ -97,10 +99,10 @@ public final class BufferedImageInputStream extends ImageInputStreamImpl impleme
|
|||||||
|
|
||||||
if (!buffer.hasRemaining()) {
|
if (!buffer.hasRemaining()) {
|
||||||
fillBuffer();
|
fillBuffer();
|
||||||
}
|
|
||||||
|
|
||||||
if (!buffer.hasRemaining()) {
|
if (!buffer.hasRemaining()) {
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bitOffset = 0;
|
bitOffset = 0;
|
||||||
@@ -172,21 +174,21 @@ public final class BufferedImageInputStream extends ImageInputStreamImpl impleme
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public short readShort() throws IOException {
|
public short readShort() throws IOException {
|
||||||
readFully(integralCache.array(), 0, 2);
|
readFully(integralCacheArray, 0, 2);
|
||||||
|
|
||||||
return integralCache.getShort(0);
|
return integralCache.getShort(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int readInt() throws IOException {
|
public int readInt() throws IOException {
|
||||||
readFully(integralCache.array(), 0, 4);
|
readFully(integralCacheArray, 0, 4);
|
||||||
|
|
||||||
return integralCache.getInt(0);
|
return integralCache.getInt(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long readLong() throws IOException {
|
public long readLong() throws IOException {
|
||||||
readFully(integralCache.array(), 0, 8);
|
readFully(integralCacheArray, 0, 8);
|
||||||
|
|
||||||
return integralCache.getLong(0);
|
return integralCache.getLong(0);
|
||||||
}
|
}
|
||||||
@@ -253,6 +255,7 @@ public final class BufferedImageInputStream extends ImageInputStreamImpl impleme
|
|||||||
}
|
}
|
||||||
|
|
||||||
int val = buffer.get() & 0xff;
|
int val = buffer.get() & 0xff;
|
||||||
|
streamPos++;
|
||||||
|
|
||||||
accum <<= 8;
|
accum <<= 8;
|
||||||
accum |= val;
|
accum |= val;
|
||||||
@@ -262,9 +265,7 @@ public final class BufferedImageInputStream extends ImageInputStreamImpl impleme
|
|||||||
// Move byte position back if in the middle of a byte
|
// Move byte position back if in the middle of a byte
|
||||||
if (newBitOffset != 0) {
|
if (newBitOffset != 0) {
|
||||||
buffer.position(buffer.position() - 1);
|
buffer.position(buffer.position() - 1);
|
||||||
}
|
streamPos--;
|
||||||
else {
|
|
||||||
streamPos++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.bitOffset = newBitOffset;
|
this.bitOffset = newBitOffset;
|
||||||
@@ -279,26 +280,26 @@ public final class BufferedImageInputStream extends ImageInputStreamImpl impleme
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void seek(long pPosition) throws IOException {
|
public void seek(long position) throws IOException {
|
||||||
checkClosed();
|
checkClosed();
|
||||||
bitOffset = 0;
|
bitOffset = 0;
|
||||||
|
|
||||||
if (streamPos == pPosition) {
|
if (streamPos == position) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optimized to not invalidate buffer if new position is within current buffer
|
// Optimized to not invalidate buffer if new position is within current buffer
|
||||||
long newBufferPos = buffer.position() + pPosition - streamPos;
|
long newBufferPos = buffer.position() + position - streamPos;
|
||||||
if (newBufferPos >= 0 && newBufferPos <= buffer.limit()) {
|
if (newBufferPos >= 0 && newBufferPos <= buffer.limit()) {
|
||||||
buffer.position((int) newBufferPos);
|
buffer.position((int) newBufferPos);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Will invalidate buffer
|
// Will invalidate buffer
|
||||||
buffer.limit(0);
|
buffer.limit(0);
|
||||||
stream.seek(pPosition);
|
stream.seek(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
streamPos = pPosition;
|
streamPos = position;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -330,7 +331,9 @@ public final class BufferedImageInputStream extends ImageInputStreamImpl impleme
|
|||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
if (stream != null) {
|
if (stream != null) {
|
||||||
//stream.close();
|
// TODO: FixMe: Need to close underlying stream here!
|
||||||
|
// For call sites that relies on not closing, we should instead not close the buffered stream.
|
||||||
|
// stream.close();
|
||||||
stream = null;
|
stream = null;
|
||||||
buffer = null;
|
buffer = null;
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-3
@@ -30,10 +30,11 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.imageio.stream;
|
package com.twelvemonkeys.imageio.stream;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.imageio.spi.ProviderInfo;
|
||||||
|
|
||||||
import javax.imageio.spi.ImageInputStreamSpi;
|
import javax.imageio.spi.ImageInputStreamSpi;
|
||||||
import javax.imageio.stream.ImageInputStream;
|
import javax.imageio.stream.ImageInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -47,10 +48,14 @@ import java.util.Locale;
|
|||||||
public class ByteArrayImageInputStreamSpi extends ImageInputStreamSpi {
|
public class ByteArrayImageInputStreamSpi extends ImageInputStreamSpi {
|
||||||
|
|
||||||
public ByteArrayImageInputStreamSpi() {
|
public ByteArrayImageInputStreamSpi() {
|
||||||
super("TwelveMonkeys", "1.0 BETA", byte[].class);
|
this(new StreamProviderInfo());
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImageInputStream createInputStreamInstance(Object pInput, boolean pUseCache, File pCacheDir) throws IOException {
|
private ByteArrayImageInputStreamSpi(ProviderInfo providerInfo) {
|
||||||
|
super(providerInfo.getVendorName(), providerInfo.getVersion(), byte[].class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImageInputStream createInputStreamInstance(Object pInput, boolean pUseCache, File pCacheDir) {
|
||||||
if (pInput instanceof byte[]) {
|
if (pInput instanceof byte[]) {
|
||||||
return new ByteArrayImageInputStream((byte[]) pInput);
|
return new ByteArrayImageInputStream((byte[]) pInput);
|
||||||
}
|
}
|
||||||
|
|||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
package com.twelvemonkeys.imageio.stream;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.imageio.spi.ProviderInfo;
|
||||||
|
|
||||||
|
final class StreamProviderInfo extends ProviderInfo {
|
||||||
|
StreamProviderInfo() {
|
||||||
|
super(StreamProviderInfo.class.getPackage());
|
||||||
|
}
|
||||||
|
}
|
||||||
+12
-1
@@ -30,6 +30,8 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.imageio.stream;
|
package com.twelvemonkeys.imageio.stream;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.imageio.spi.ProviderInfo;
|
||||||
|
|
||||||
import javax.imageio.spi.ImageInputStreamSpi;
|
import javax.imageio.spi.ImageInputStreamSpi;
|
||||||
import javax.imageio.stream.FileCacheImageInputStream;
|
import javax.imageio.stream.FileCacheImageInputStream;
|
||||||
import javax.imageio.stream.FileImageInputStream;
|
import javax.imageio.stream.FileImageInputStream;
|
||||||
@@ -53,7 +55,11 @@ import java.util.Locale;
|
|||||||
// TODO: URI instead of URL?
|
// TODO: URI instead of URL?
|
||||||
public class URLImageInputStreamSpi extends ImageInputStreamSpi {
|
public class URLImageInputStreamSpi extends ImageInputStreamSpi {
|
||||||
public URLImageInputStreamSpi() {
|
public URLImageInputStreamSpi() {
|
||||||
super("TwelveMonkeys", "1.0 BETA", URL.class);
|
this(new StreamProviderInfo());
|
||||||
|
}
|
||||||
|
|
||||||
|
private URLImageInputStreamSpi(ProviderInfo providerInfo) {
|
||||||
|
super(providerInfo.getVendorName(), providerInfo.getVersion(), URL.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Create a URI or URLImageInputStream class, with a getUR[I|L] method, to allow for multiple file formats
|
// TODO: Create a URI or URLImageInputStream class, with a getUR[I|L] method, to allow for multiple file formats
|
||||||
@@ -108,6 +114,11 @@ public class URLImageInputStreamSpi extends ImageInputStreamSpi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canUseCacheFile() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public String getDescription(final Locale pLocale) {
|
public String getDescription(final Locale pLocale) {
|
||||||
return "Service provider that instantiates an ImageInputStream from a URL";
|
return "Service provider that instantiates an ImageInputStream from a URL";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -291,4 +291,21 @@ public final class IIOUtil {
|
|||||||
System.arraycopy(srcRow, srcPos + x, destRow, destPos + x / samplePeriod, pixelStride);
|
System.arraycopy(srcRow, srcPos + x, destRow, destPos + x / samplePeriod, pixelStride);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void subsampleRow(float[] srcRow, int srcPos, int srcWidth,
|
||||||
|
float[] destRow, int destPos,
|
||||||
|
int samplesPerPixel, int bitsPerSample, int samplePeriod) {
|
||||||
|
Validate.isTrue(samplePeriod > 1, "samplePeriod must be > 1"); // Period == 1 could be a no-op...
|
||||||
|
Validate.isTrue(bitsPerSample > 0 && bitsPerSample <= 32 && (bitsPerSample == 1 || bitsPerSample % 2 == 0),
|
||||||
|
"bitsPerSample must be > 0 and <= 32 and a power of 2");
|
||||||
|
Validate.isTrue(samplesPerPixel > 0, "samplesPerPixel must be > 0");
|
||||||
|
Validate.isTrue(samplesPerPixel * bitsPerSample <= 32 || samplesPerPixel * bitsPerSample % 32 == 0,
|
||||||
|
"samplesPerPixel * bitsPerSample must be < 32 or a multiple of 32 ");
|
||||||
|
|
||||||
|
int pixelStride = bitsPerSample * samplesPerPixel / 32;
|
||||||
|
for (int x = 0; x < srcWidth * pixelStride; x += samplePeriod * pixelStride) {
|
||||||
|
// System.arraycopy should be intrinsic, but consider using direct array access for pixelStride == 1
|
||||||
|
System.arraycopy(srcRow, srcPos + x, destRow, destPos + x / samplePeriod, pixelStride);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+12
-9
@@ -30,11 +30,11 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.imageio;
|
package com.twelvemonkeys.imageio;
|
||||||
|
|
||||||
import org.junit.Test;
|
import static java.util.Collections.singleton;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import javax.imageio.IIOException;
|
|
||||||
import javax.imageio.ImageReadParam;
|
|
||||||
import javax.imageio.ImageTypeSpecifier;
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.color.ColorSpace;
|
import java.awt.color.ColorSpace;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
@@ -44,8 +44,11 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import static java.util.Collections.singleton;
|
import javax.imageio.IIOException;
|
||||||
import static org.junit.Assert.*;
|
import javax.imageio.ImageReadParam;
|
||||||
|
import javax.imageio.ImageTypeSpecifier;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ImageReaderBaseTest
|
* ImageReaderBaseTest
|
||||||
@@ -212,19 +215,19 @@ public class ImageReaderBaseTest {
|
|||||||
assertEquals(TYPES.get(0).getBufferedImageType(), destination.getType());
|
assertEquals(TYPES.get(0).getBufferedImageType(), destination.getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = IIOException.class)
|
||||||
public void testGetDestinationParamDestinationExceedsIntegerMax() throws IIOException {
|
public void testGetDestinationParamDestinationExceedsIntegerMax() throws IIOException {
|
||||||
ImageReadParam param = new ImageReadParam();
|
ImageReadParam param = new ImageReadParam();
|
||||||
param.setSourceRegion(new Rectangle(3 * Short.MAX_VALUE, 2 * Short.MAX_VALUE)); // 6 442 057 734 pixels
|
param.setSourceRegion(new Rectangle(3 * Short.MAX_VALUE, 2 * Short.MAX_VALUE)); // 6 442 057 734 pixels
|
||||||
ImageReaderBase.getDestination(param, TYPES.iterator(), 6 * Short.MAX_VALUE, 4 * Short.MAX_VALUE); // 25 768 230 936 pixels
|
ImageReaderBase.getDestination(param, TYPES.iterator(), 6 * Short.MAX_VALUE, 4 * Short.MAX_VALUE); // 25 768 230 936 pixels
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = IIOException.class)
|
||||||
public void testGetDestinationDimensionExceedsIntegerMax() throws IIOException {
|
public void testGetDestinationDimensionExceedsIntegerMax() throws IIOException {
|
||||||
ImageReaderBase.getDestination(null, TYPES.iterator(), 3 * Short.MAX_VALUE, 2 * Short.MAX_VALUE); // 6 442 057 734 pixels
|
ImageReaderBase.getDestination(null, TYPES.iterator(), 3 * Short.MAX_VALUE, 2 * Short.MAX_VALUE); // 6 442 057 734 pixels
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = IIOException.class)
|
||||||
public void testGetDestinationStorageExceedsIntegerMax() throws IIOException {
|
public void testGetDestinationStorageExceedsIntegerMax() throws IIOException {
|
||||||
Set<ImageTypeSpecifier> byteTypes = singleton(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR));
|
Set<ImageTypeSpecifier> byteTypes = singleton(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR));
|
||||||
ImageReaderBase.getDestination(null, byteTypes.iterator(), Short.MAX_VALUE, Short.MAX_VALUE); // 1 073 676 289 pixels
|
ImageReaderBase.getDestination(null, byteTypes.iterator(), Short.MAX_VALUE, Short.MAX_VALUE); // 1 073 676 289 pixels
|
||||||
|
|||||||
+4
-2
@@ -35,6 +35,8 @@ import org.junit.Test;
|
|||||||
|
|
||||||
import java.awt.image.*;
|
import java.awt.image.*;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
public class DiscreteAlphaIndexColorModelTest {
|
public class DiscreteAlphaIndexColorModelTest {
|
||||||
@@ -162,7 +164,7 @@ public class DiscreteAlphaIndexColorModelTest {
|
|||||||
assertEquals(2, sampleModel.getHeight());
|
assertEquals(2, sampleModel.getHeight());
|
||||||
|
|
||||||
assertTrue(colorModel.isCompatibleSampleModel(sampleModel));
|
assertTrue(colorModel.isCompatibleSampleModel(sampleModel));
|
||||||
assertThat(sampleModel, CoreMatchers.is(PixelInterleavedSampleModel.class));
|
assertThat(sampleModel, instanceOf(PixelInterleavedSampleModel.class));
|
||||||
assertThat(sampleModel.getDataType(), CoreMatchers.equalTo(DataBuffer.TYPE_BYTE));
|
assertThat(sampleModel.getDataType(), CoreMatchers.equalTo(DataBuffer.TYPE_BYTE));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,7 +182,7 @@ public class DiscreteAlphaIndexColorModelTest {
|
|||||||
assertEquals(2, sampleModel.getHeight());
|
assertEquals(2, sampleModel.getHeight());
|
||||||
|
|
||||||
assertTrue(colorModel.isCompatibleSampleModel(sampleModel));
|
assertTrue(colorModel.isCompatibleSampleModel(sampleModel));
|
||||||
assertThat(sampleModel, CoreMatchers.is(PixelInterleavedSampleModel.class));
|
assertThat(sampleModel, instanceOf(PixelInterleavedSampleModel.class));
|
||||||
assertThat(sampleModel.getDataType(), CoreMatchers.equalTo(DataBuffer.TYPE_USHORT));
|
assertThat(sampleModel.getDataType(), CoreMatchers.equalTo(DataBuffer.TYPE_USHORT));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -45,7 +45,7 @@ import static org.junit.Assert.*;
|
|||||||
*/
|
*/
|
||||||
public class ProviderInfoTest {
|
public class ProviderInfoTest {
|
||||||
@Test
|
@Test
|
||||||
public void testCreateNorma() {
|
public void testCreateNormal() {
|
||||||
new ProviderInfo(Package.getPackage("java.util"));
|
new ProviderInfo(Package.getPackage("java.util"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+15
-14
@@ -31,8 +31,8 @@
|
|||||||
package com.twelvemonkeys.imageio.spi;
|
package com.twelvemonkeys.imageio.spi;
|
||||||
|
|
||||||
import org.hamcrest.Description;
|
import org.hamcrest.Description;
|
||||||
|
import org.hamcrest.TypeSafeMatcher;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.internal.matchers.TypeSafeMatcher;
|
|
||||||
|
|
||||||
import javax.imageio.ImageReader;
|
import javax.imageio.ImageReader;
|
||||||
import javax.imageio.ImageWriter;
|
import javax.imageio.ImageWriter;
|
||||||
@@ -42,6 +42,7 @@ import javax.imageio.spi.ImageWriterSpi;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -62,52 +63,52 @@ public abstract class ReaderWriterProviderInfoTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void readerClassName() throws Exception {
|
public void readerClassName() {
|
||||||
assertClassExists(providerInfo.readerClassName(), ImageReader.class);
|
assertClassExists(providerInfo.readerClassName(), ImageReader.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void readerSpiClassNames() throws Exception {
|
public void readerSpiClassNames() {
|
||||||
assertClassesExist(providerInfo.readerSpiClassNames(), ImageReaderSpi.class);
|
assertClassesExist(providerInfo.readerSpiClassNames(), ImageReaderSpi.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void inputTypes() throws Exception {
|
public void inputTypes() {
|
||||||
assertNotNull(providerInfo.inputTypes());
|
assertNotNull(providerInfo.inputTypes());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writerClassName() throws Exception {
|
public void writerClassName() {
|
||||||
assertClassExists(providerInfo.writerClassName(), ImageWriter.class);
|
assertClassExists(providerInfo.writerClassName(), ImageWriter.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writerSpiClassNames() throws Exception {
|
public void writerSpiClassNames() {
|
||||||
assertClassesExist(providerInfo.writerSpiClassNames(), ImageWriterSpi.class);
|
assertClassesExist(providerInfo.writerSpiClassNames(), ImageWriterSpi.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void outputTypes() throws Exception {
|
public void outputTypes() {
|
||||||
assertNotNull(providerInfo.outputTypes());
|
assertNotNull(providerInfo.outputTypes());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void nativeStreamMetadataFormatClassName() throws Exception {
|
public void nativeStreamMetadataFormatClassName() {
|
||||||
assertClassExists(providerInfo.nativeStreamMetadataFormatClassName(), IIOMetadataFormat.class);
|
assertClassExists(providerInfo.nativeStreamMetadataFormatClassName(), IIOMetadataFormat.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void extraStreamMetadataFormatClassNames() throws Exception {
|
public void extraStreamMetadataFormatClassNames() {
|
||||||
assertClassesExist(providerInfo.extraStreamMetadataFormatClassNames(), IIOMetadataFormat.class);
|
assertClassesExist(providerInfo.extraStreamMetadataFormatClassNames(), IIOMetadataFormat.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void nativeImageMetadataFormatClassName() throws Exception {
|
public void nativeImageMetadataFormatClassName() {
|
||||||
assertClassExists(providerInfo.nativeImageMetadataFormatClassName(), IIOMetadataFormat.class);
|
assertClassExists(providerInfo.nativeImageMetadataFormatClassName(), IIOMetadataFormat.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void extraImageMetadataFormatClassNames() throws Exception {
|
public void extraImageMetadataFormatClassNames() {
|
||||||
assertClassesExist(providerInfo.extraImageMetadataFormatClassNames(), IIOMetadataFormat.class);
|
assertClassesExist(providerInfo.extraImageMetadataFormatClassNames(), IIOMetadataFormat.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,7 +116,7 @@ public abstract class ReaderWriterProviderInfoTest {
|
|||||||
public void formatNames() {
|
public void formatNames() {
|
||||||
String[] names = providerInfo.formatNames();
|
String[] names = providerInfo.formatNames();
|
||||||
assertNotNull(names);
|
assertNotNull(names);
|
||||||
assertFalse(names.length == 0);
|
assertNotEquals(0, names.length);
|
||||||
|
|
||||||
List<String> list = asList(names);
|
List<String> list = asList(names);
|
||||||
|
|
||||||
@@ -132,7 +133,7 @@ public abstract class ReaderWriterProviderInfoTest {
|
|||||||
public void suffixes() {
|
public void suffixes() {
|
||||||
String[] suffixes = providerInfo.suffixes();
|
String[] suffixes = providerInfo.suffixes();
|
||||||
assertNotNull(suffixes);
|
assertNotNull(suffixes);
|
||||||
assertFalse(suffixes.length == 0);
|
assertNotEquals(0, suffixes.length);
|
||||||
|
|
||||||
for (String suffix : suffixes) {
|
for (String suffix : suffixes) {
|
||||||
assertNotNull(suffix);
|
assertNotNull(suffix);
|
||||||
@@ -144,7 +145,7 @@ public abstract class ReaderWriterProviderInfoTest {
|
|||||||
public void mimeTypes() {
|
public void mimeTypes() {
|
||||||
String[] mimeTypes = providerInfo.mimeTypes();
|
String[] mimeTypes = providerInfo.mimeTypes();
|
||||||
assertNotNull(mimeTypes);
|
assertNotNull(mimeTypes);
|
||||||
assertFalse(mimeTypes.length == 0);
|
assertNotEquals(0, mimeTypes.length);
|
||||||
|
|
||||||
for (String mimeType : mimeTypes) {
|
for (String mimeType : mimeTypes) {
|
||||||
assertNotNull(mimeType);
|
assertNotNull(mimeType);
|
||||||
|
|||||||
+254
@@ -32,16 +32,19 @@ package com.twelvemonkeys.imageio.stream;
|
|||||||
|
|
||||||
import com.twelvemonkeys.io.ole2.CompoundDocument;
|
import com.twelvemonkeys.io.ole2.CompoundDocument;
|
||||||
import com.twelvemonkeys.io.ole2.Entry;
|
import com.twelvemonkeys.io.ole2.Entry;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import javax.imageio.stream.ImageInputStream;
|
import javax.imageio.stream.ImageInputStream;
|
||||||
import javax.imageio.stream.MemoryCacheImageInputStream;
|
import javax.imageio.stream.MemoryCacheImageInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
import static java.util.Arrays.fill;
|
import static java.util.Arrays.fill;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BufferedImageInputStreamTest
|
* BufferedImageInputStreamTest
|
||||||
@@ -72,6 +75,257 @@ public class BufferedImageInputStreamTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadBit() throws IOException {
|
||||||
|
byte[] bytes = new byte[] {(byte) 0xF0, (byte) 0x0F};
|
||||||
|
|
||||||
|
// Create wrapper stream
|
||||||
|
BufferedImageInputStream stream = new BufferedImageInputStream(new ByteArrayImageInputStream(bytes));
|
||||||
|
|
||||||
|
// Read all bits
|
||||||
|
assertEquals(1, stream.readBit());
|
||||||
|
assertEquals(1, stream.getBitOffset());
|
||||||
|
assertEquals(0, stream.getStreamPosition());
|
||||||
|
|
||||||
|
assertEquals(1, stream.readBit());
|
||||||
|
assertEquals(2, stream.getBitOffset());
|
||||||
|
assertEquals(0, stream.getStreamPosition());
|
||||||
|
|
||||||
|
assertEquals(1, stream.readBit());
|
||||||
|
assertEquals(3, stream.getBitOffset());
|
||||||
|
assertEquals(0, stream.getStreamPosition());
|
||||||
|
|
||||||
|
assertEquals(1, stream.readBit());
|
||||||
|
assertEquals(4, stream.getBitOffset());
|
||||||
|
assertEquals(0, stream.getStreamPosition());
|
||||||
|
|
||||||
|
assertEquals(0, stream.readBit());
|
||||||
|
assertEquals(5, stream.getBitOffset());
|
||||||
|
assertEquals(0, stream.getStreamPosition());
|
||||||
|
|
||||||
|
assertEquals(0, stream.readBit());
|
||||||
|
assertEquals(6, stream.getBitOffset());
|
||||||
|
assertEquals(0, stream.getStreamPosition());
|
||||||
|
|
||||||
|
assertEquals(0, stream.readBit());
|
||||||
|
assertEquals(7, stream.getBitOffset());
|
||||||
|
assertEquals(0, stream.getStreamPosition());
|
||||||
|
|
||||||
|
assertEquals(0, stream.readBit()); // last bit
|
||||||
|
assertEquals(0, stream.getBitOffset());
|
||||||
|
assertEquals(1, stream.getStreamPosition());
|
||||||
|
|
||||||
|
// Full reset, read same sequence again
|
||||||
|
stream.seek(0);
|
||||||
|
assertEquals(0, stream.getBitOffset());
|
||||||
|
assertEquals(0, stream.getStreamPosition());
|
||||||
|
|
||||||
|
assertEquals(1, stream.readBit());
|
||||||
|
assertEquals(1, stream.readBit());
|
||||||
|
assertEquals(1, stream.readBit());
|
||||||
|
assertEquals(1, stream.readBit());
|
||||||
|
assertEquals(0, stream.readBit());
|
||||||
|
assertEquals(0, stream.readBit());
|
||||||
|
assertEquals(0, stream.readBit());
|
||||||
|
assertEquals(0, stream.readBit());
|
||||||
|
|
||||||
|
assertEquals(0, stream.getBitOffset());
|
||||||
|
assertEquals(1, stream.getStreamPosition());
|
||||||
|
|
||||||
|
// Full reset, read partial
|
||||||
|
stream.seek(0);
|
||||||
|
|
||||||
|
assertEquals(1, stream.readBit());
|
||||||
|
assertEquals(1, stream.readBit());
|
||||||
|
|
||||||
|
// Byte reset, read same sequence again
|
||||||
|
stream.setBitOffset(0);
|
||||||
|
|
||||||
|
assertEquals(1, stream.readBit());
|
||||||
|
assertEquals(1, stream.readBit());
|
||||||
|
assertEquals(1, stream.readBit());
|
||||||
|
assertEquals(1, stream.readBit());
|
||||||
|
assertEquals(0, stream.readBit());
|
||||||
|
|
||||||
|
// Byte reset, read partial sequence again
|
||||||
|
stream.setBitOffset(3);
|
||||||
|
|
||||||
|
assertEquals(1, stream.readBit());
|
||||||
|
assertEquals(0, stream.readBit());
|
||||||
|
assertEquals(0, stream.getStreamPosition());
|
||||||
|
|
||||||
|
// Byte reset, read partial sequence again
|
||||||
|
stream.setBitOffset(6);
|
||||||
|
|
||||||
|
assertEquals(0, stream.readBit());
|
||||||
|
assertEquals(0, stream.readBit());
|
||||||
|
assertEquals(1, stream.getStreamPosition());
|
||||||
|
|
||||||
|
// Read all bits, second byte
|
||||||
|
assertEquals(0, stream.readBit());
|
||||||
|
assertEquals(1, stream.getBitOffset());
|
||||||
|
assertEquals(1, stream.getStreamPosition());
|
||||||
|
|
||||||
|
assertEquals(0, stream.readBit());
|
||||||
|
assertEquals(2, stream.getBitOffset());
|
||||||
|
assertEquals(1, stream.getStreamPosition());
|
||||||
|
|
||||||
|
assertEquals(0, stream.readBit());
|
||||||
|
assertEquals(3, stream.getBitOffset());
|
||||||
|
assertEquals(1, stream.getStreamPosition());
|
||||||
|
|
||||||
|
assertEquals(0, stream.readBit());
|
||||||
|
assertEquals(4, stream.getBitOffset());
|
||||||
|
assertEquals(1, stream.getStreamPosition());
|
||||||
|
|
||||||
|
assertEquals(1, stream.readBit());
|
||||||
|
assertEquals(5, stream.getBitOffset());
|
||||||
|
assertEquals(1, stream.getStreamPosition());
|
||||||
|
|
||||||
|
assertEquals(1, stream.readBit());
|
||||||
|
assertEquals(6, stream.getBitOffset());
|
||||||
|
assertEquals(1, stream.getStreamPosition());
|
||||||
|
|
||||||
|
assertEquals(1, stream.readBit());
|
||||||
|
assertEquals(7, stream.getBitOffset());
|
||||||
|
assertEquals(1, stream.getStreamPosition());
|
||||||
|
|
||||||
|
assertEquals(1, stream.readBit()); // last bit
|
||||||
|
assertEquals(0, stream.getBitOffset());
|
||||||
|
assertEquals(2, stream.getStreamPosition());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadBits() throws IOException {
|
||||||
|
byte[] bytes = new byte[] {(byte) 0xF0, (byte) 0xCC, (byte) 0xAA};
|
||||||
|
|
||||||
|
// Create wrapper stream
|
||||||
|
BufferedImageInputStream stream = new BufferedImageInputStream(new ByteArrayImageInputStream(bytes));
|
||||||
|
|
||||||
|
// Read all bits, first byte
|
||||||
|
assertEquals(3, stream.readBits(2));
|
||||||
|
assertEquals(2, stream.getBitOffset());
|
||||||
|
assertEquals(0, stream.getStreamPosition());
|
||||||
|
|
||||||
|
assertEquals(3, stream.readBits(2));
|
||||||
|
assertEquals(4, stream.getBitOffset());
|
||||||
|
assertEquals(0, stream.getStreamPosition());
|
||||||
|
|
||||||
|
assertEquals(0, stream.readBits(2));
|
||||||
|
assertEquals(6, stream.getBitOffset());
|
||||||
|
assertEquals(0, stream.getStreamPosition());
|
||||||
|
|
||||||
|
assertEquals(0, stream.readBits(2));
|
||||||
|
assertEquals(0, stream.getBitOffset());
|
||||||
|
assertEquals(1, stream.getStreamPosition());
|
||||||
|
|
||||||
|
// Read all bits, second byte
|
||||||
|
assertEquals(3, stream.readBits(2));
|
||||||
|
assertEquals(2, stream.getBitOffset());
|
||||||
|
assertEquals(1, stream.getStreamPosition());
|
||||||
|
|
||||||
|
assertEquals(0, stream.readBits(2));
|
||||||
|
assertEquals(4, stream.getBitOffset());
|
||||||
|
assertEquals(1, stream.getStreamPosition());
|
||||||
|
|
||||||
|
assertEquals(3, stream.readBits(2));
|
||||||
|
assertEquals(6, stream.getBitOffset());
|
||||||
|
assertEquals(1, stream.getStreamPosition());
|
||||||
|
|
||||||
|
assertEquals(0, stream.readBits(2));
|
||||||
|
assertEquals(0, stream.getBitOffset());
|
||||||
|
assertEquals(2, stream.getStreamPosition());
|
||||||
|
|
||||||
|
// Read all bits, third byte
|
||||||
|
assertEquals(2, stream.readBits(2));
|
||||||
|
assertEquals(2, stream.getBitOffset());
|
||||||
|
assertEquals(2, stream.getStreamPosition());
|
||||||
|
|
||||||
|
assertEquals(2, stream.readBits(2));
|
||||||
|
assertEquals(4, stream.getBitOffset());
|
||||||
|
assertEquals(2, stream.getStreamPosition());
|
||||||
|
|
||||||
|
assertEquals(2, stream.readBits(2));
|
||||||
|
assertEquals(6, stream.getBitOffset());
|
||||||
|
assertEquals(2, stream.getStreamPosition());
|
||||||
|
|
||||||
|
assertEquals(2, stream.readBits(2));
|
||||||
|
assertEquals(0, stream.getBitOffset());
|
||||||
|
assertEquals(3, stream.getStreamPosition());
|
||||||
|
|
||||||
|
// Full reset, read same sequence again
|
||||||
|
stream.seek(0);
|
||||||
|
assertEquals(0, stream.getBitOffset());
|
||||||
|
assertEquals(0, stream.getStreamPosition());
|
||||||
|
|
||||||
|
// Read all bits, increasing size
|
||||||
|
assertEquals(7, stream.readBits(3)); // 111
|
||||||
|
assertEquals(3, stream.getBitOffset());
|
||||||
|
assertEquals(0, stream.getStreamPosition());
|
||||||
|
|
||||||
|
assertEquals(8, stream.readBits(4)); // 1000
|
||||||
|
assertEquals(7, stream.getBitOffset());
|
||||||
|
assertEquals(0, stream.getStreamPosition());
|
||||||
|
|
||||||
|
assertEquals(12, stream.readBits(5)); // 01100
|
||||||
|
assertEquals(4, stream.getBitOffset());
|
||||||
|
assertEquals(1, stream.getStreamPosition());
|
||||||
|
|
||||||
|
assertEquals(50, stream.readBits(6)); // 110010
|
||||||
|
assertEquals(2, stream.getBitOffset());
|
||||||
|
assertEquals(2, stream.getStreamPosition());
|
||||||
|
|
||||||
|
assertEquals(42, stream.readBits(6)); // 101010
|
||||||
|
assertEquals(0, stream.getBitOffset());
|
||||||
|
assertEquals(3, stream.getStreamPosition());
|
||||||
|
|
||||||
|
// Full reset, read same sequence again
|
||||||
|
stream.seek(0);
|
||||||
|
assertEquals(0, stream.getBitOffset());
|
||||||
|
assertEquals(0, stream.getStreamPosition());
|
||||||
|
|
||||||
|
// Read all bits multi-byte
|
||||||
|
assertEquals(0xF0C, stream.readBits(12)); // 111100001100
|
||||||
|
assertEquals(4, stream.getBitOffset());
|
||||||
|
assertEquals(1, stream.getStreamPosition());
|
||||||
|
|
||||||
|
assertEquals(0xCAA, stream.readBits(12)); // 110010101010
|
||||||
|
assertEquals(0, stream.getBitOffset());
|
||||||
|
assertEquals(3, stream.getStreamPosition());
|
||||||
|
|
||||||
|
// Full reset, read same sequence again, all bits in one go
|
||||||
|
stream.seek(0);
|
||||||
|
assertEquals(0, stream.getBitOffset());
|
||||||
|
assertEquals(0, stream.getStreamPosition());
|
||||||
|
|
||||||
|
assertEquals(0xF0CCAA, stream.readBits(24));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadBitsRandom() throws IOException {
|
||||||
|
long value = random.nextLong();
|
||||||
|
byte[] bytes = new byte[8];
|
||||||
|
ByteBuffer.wrap(bytes).putLong(value);
|
||||||
|
|
||||||
|
// Create wrapper stream
|
||||||
|
BufferedImageInputStream stream = new BufferedImageInputStream(new ByteArrayImageInputStream(bytes));
|
||||||
|
|
||||||
|
for (int i = 1; i < 64; i++) {
|
||||||
|
stream.seek(0);
|
||||||
|
assertEquals(i + " bits differ", value >>> (64L - i), stream.readBits(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testClose() throws IOException {
|
||||||
|
// Create wrapper stream
|
||||||
|
ImageInputStream mock = mock(ImageInputStream.class);
|
||||||
|
BufferedImageInputStream stream = new BufferedImageInputStream(mock);
|
||||||
|
|
||||||
|
stream.close();
|
||||||
|
verify(mock, never()).close();
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Write other tests
|
// TODO: Write other tests
|
||||||
|
|
||||||
// TODO: Create test that exposes read += -1 (eof) bug
|
// TODO: Create test that exposes read += -1 (eof) bug
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user