Compare commits

..

304 Commits

Author SHA1 Message Date
Harald Kuhr 4b2ebec429 Added missing CRW pom 2021-05-13 15:13:55 +02:00
Harald Kuhr 0498a1a095 Added CRW read 2021-05-13 15:09:59 +02:00
Harald Kuhr 4fd20f7fa0 Updated to latest. 2021-05-13 15:08:51 +02:00
Harald Kuhr c9bd68623a Merge branch 'master' into camera-raw 2021-05-13 14:58:40 +02:00
Harald Kuhr 4d2326c18d Update issue templates 2021-05-09 18:12:34 +02:00
Harald Kuhr 94eac2d6e5 XXX: Remove another old servlet class. 2021-05-09 18:08:56 +02:00
Harald Kuhr f63a33d541 HTTPS links in README.md 2021-05-09 18:08:43 +02:00
Harald Kuhr 00f8d87f36 PR template + rename issue template. 2021-05-09 18:08:16 +02:00
Harald Kuhr 4c2ab6da7b Update issue templates 2021-05-08 20:41:13 +02:00
Harald Kuhr b5088312e2 Update issue templates 2021-05-07 19:39:41 +02:00
Harald Kuhr f04f968f12 Update issue templates 2021-05-07 19:35:02 +02:00
Harald Kuhr 8896092e31 Update issue templates 2021-05-07 19:29:52 +02:00
Harald Kuhr 2f9768a1d4 XXX: Remove old servlet class. 2021-05-06 00:14:21 +02:00
Harald Kuhr 06bcf22242 #483 Minor optimization 2021-05-06 00:12:19 +02:00
Harald Kuhr 20c7f8e60e #483 Add license headers. 2021-05-06 00:08:27 +02:00
Harald Kuhr 15a9ad0a9b #483 Initial PSD Write support 2021-05-06 00:01:54 +02:00
Harald Kuhr 7ae2d636dc Merge pull request #607 from haraldk/snyk-upgrade-b72dc0a35c77c27ccf914333993bfcb1
[Snyk] Upgrade com.github.jai-imageio:jai-imageio-core from 1.3.0 to 1.4.0
2021-04-30 08:45:03 +02:00
snyk-bot 12e756b23c fix: upgrade com.github.jai-imageio:jai-imageio-core from 1.3.0 to 1.4.0
Snyk has created this PR to upgrade com.github.jai-imageio:jai-imageio-core from 1.3.0 to 1.4.0.

See this package in Maven Repository:
https://mvnrepository.com/artifact/com.github.jai-imageio/jai-imageio-core/

See this project in Snyk:
https://app.snyk.io/org/haraldk/project/33153897-db54-44bf-a11a-efd834cecdae?utm_source=github&utm_medium=upgrade-pr
2021-04-30 00:42:19 +00:00
Harald Kuhr 4e2bf131d2 #606: Fix bug introduced by more aggressive readDirect. 2021-04-29 20:06:36 +02:00
Harald Kuhr d0c4a07556 #606: Workaround for broken JDK WBMPImageReader 2021-04-29 16:55:24 +02:00
Harald Kuhr 21059c8d5a Cleaner tests for Java 6 or later... A little late. :-) 2021-04-29 16:46:16 +02:00
Harald Kuhr fa7b530809 Adding GitHub sponsors link. 2021-04-28 14:21:37 +02:00
Harald Kuhr 790cf3b32e Test clean-up. 2021-04-26 22:42:44 +02:00
Harald Kuhr b1baaad23b Rename file, add missing file extension. 2021-04-25 19:55:33 +02:00
Harald Kuhr 7fa704ace5 Bump plugins and stop deploying useless (internal) artifacts. 2021-04-24 19:26:32 +02:00
Harald Kuhr 8d07f4fe90 Updated versions to 3.7.0. 2021-04-24 13:20:40 +02:00
Harald Kuhr 32bba6857b [maven-release-plugin] prepare for next development iteration 2021-04-24 12:31:07 +02:00
Harald Kuhr ab7b08dfa9 [maven-release-plugin] prepare release twelvemonkeys-3.7.0 2021-04-24 12:31:00 +02:00
Harald Kuhr e0d6fa0d84 Better JPMS automatic module names. 2021-04-24 12:26:36 +02:00
Harald Kuhr 51bdd370da Fix some JavaDoc issues that broke the release build... 2021-04-24 12:05:49 +02:00
Harald Kuhr ee2be3f88f [maven-release-plugin] rollback the release of twelvemonkeys-3.7.0 2021-04-24 11:24:15 +02:00
Harald Kuhr c5511833cc Fix SemVer issue. 2021-04-24 11:12:09 +02:00
Harald Kuhr 6ac8a5d8b4 Minor optimization for standard case with only one image. 2021-04-20 22:41:41 +02:00
Harald Kuhr 3f7cb24407 #395 Removed WebP lossless and extended format (until it's implemented). 2021-04-15 19:24:52 +02:00
Harald Kuhr 8bf9f7a8f0 Minor clean-up. 2021-04-10 18:25:27 +02:00
Harald Kuhr 03ab9558a0 #573: And error message. 2021-04-10 18:17:34 +02:00
Harald Kuhr 715bde8358 Comment too. 2021-04-10 18:17:01 +02:00
Harald Kuhr 0151efb5f6 #573: License headers too. 2021-04-10 18:15:27 +02:00
Harald Kuhr bd796429c5 #573: Better naming: LuminanceToGray 2021-04-10 18:14:09 +02:00
Harald Kuhr b4ef5823f3 #417: Half precision support (clean-up) 2021-04-10 18:13:04 +02:00
Harald Kuhr 9adf0f4da3 #417: Half precision support (clean-up) 2021-04-10 14:17:38 +02:00
Harald Kuhr 01a4e55185 Merge pull request #602 from haraldk/webp
We have Webp
2021-04-10 12:10:45 +02:00
Harald Kuhr 2e2ab11091 Merge branch 'master' into webp 2021-04-10 11:46:28 +02:00
Harald Kuhr 419ffc9373 #573: Always return RAWImageType for JPEG.
+ Bonus: Fix luma to gray conversion
2021-04-10 11:44:09 +02:00
Harald Kuhr b67975eef7 Better naming. 2021-04-09 17:19:15 +02:00
Harald Kuhr b32a861b2d #443 Automatic-Module-Name in each JARs MANIFEST.MF 2021-04-09 16:56:26 +02:00
Harald Kuhr 6930168c93 #362: JPEG metadata names in ProviderInfo. 2021-04-08 19:37:03 +02:00
Harald Kuhr fac9f1a927 #532 Write TGA with RLE compression. 2021-04-08 19:31:25 +02:00
Harald Kuhr 913a03608c #600 TimeoutMap now longer gets Long.MAX_VALUE as next expiry time if map is empty when removeExpiredEntries() is invoked. 2021-04-07 22:31:13 +02:00
Harald Kuhr 46ce99e10f Added links to metadata formats. 2021-04-07 13:21:31 +02:00
Harald Kuhr 3e4460ac41 Removed TODO (that has been done for some time). 2021-03-30 17:13:00 +02:00
Harald Kuhr 5b7fc25520 #584 Fix "No SOF segment in stream" for JPEG in TIFF
+ interoperability testing for JEP-262, JAI and JDK readers.
2021-03-30 16:26:48 +02:00
Harald Kuhr 42196e8513 No longer unregister the old Apple provided spi. 2021-03-30 12:31:56 +02:00
Harald Kuhr bc07524e7a Simplified/optimized TIFF file recognition code. 2021-03-30 11:31:34 +02:00
Harald Kuhr 0011b9a480 #417: TIFF 16 bit FP 2021-03-29 10:26:47 +02:00
Harald Kuhr 7b09ec8919 Merge branch 'master' into webp 2021-03-27 15:29:23 +01:00
Harald Kuhr 9c8977062d #554, #416: Relaxed custom metadata restrictions. 2021-03-27 15:20:59 +01:00
Harald Kuhr b01b820ec8 Remove unintended debug output. 2021-03-27 15:19:15 +01:00
Harald Kuhr b61f2c179c Merge branch 'master' into webp 2021-03-27 14:44:23 +01:00
Harald Kuhr 967f8e6984 PICT metadata + PNTG support 2021-03-27 14:39:59 +01:00
Harald Kuhr bb650e5280 Easier subsampling with xSub == 1 as no-op 2021-03-27 14:37:33 +01:00
Harald Kuhr 3b34d6e7ce General clean-up 2021-03-27 14:37:11 +01:00
Harald Kuhr db782cfe9e Merge pull request #599 from Schmidor/svg_data_url
Allow embedded resource URLs if external resources are disabled
2021-03-26 17:26:19 +01:00
Oliver Schmidtmer 96223f9f9f Allow embedded resource URLs if external resources are disabled 2021-03-26 16:05:21 +01:00
Harald Kuhr da45c5783d Forgot we actually had PICT write support.. ;-) 2021-03-23 13:15:06 +01:00
Harald Kuhr 5b6c819ac4 JPEG Lossless in non-bold :-) 2021-03-08 21:42:38 +01:00
Harald Kuhr 6d41f2db86 Updated version numbers for latest release. 2021-03-08 14:54:26 +01:00
Harald Kuhr ba0bb7b903 #595 Avoid infinite loop on corrupted JPEG stream 2021-03-06 14:15:44 +01:00
Harald Kuhr d03dc28764 Readme updates, mentioning JPEG lossless and built-in support (closes #471). 2021-03-01 18:42:59 +01:00
Harald Kuhr 20a785ea5e Updated version numbers. 2021-02-26 19:36:49 +01:00
Harald Kuhr 0286fa4268 JPEG Exif/thumbnail refactoring pt II. 2021-02-26 18:27:58 +01:00
Harald Kuhr 85fb9e6af3 JPEG Exif/thumbnail refactoring 2021-02-26 17:13:16 +01:00
Harald Kuhr 97a8806bfb Better name for source y... 2021-02-26 17:13:16 +01:00
Harald Kuhr 970f4f3a7e #588 Clipping path from JPEG with multiple APP13 segments 2021-02-26 17:13:16 +01:00
Harald Kuhr 6d192968d1 Fix SGI source subsampling + test optimizations. 2021-02-26 17:13:16 +01:00
Harald Kuhr f5959af2e1 New stream SPIs now behave more like the built-in SPIs. 2021-02-26 17:13:16 +01:00
Harald Kuhr ea74ac2714 JPEG Exif/thumbnail fixes pt II. 2021-02-26 17:13:16 +01:00
Harald Kuhr 80c595cea8 No longer reads thumbnails, as part of the readWithOrientation method. 2021-02-26 17:13:16 +01:00
Harald Kuhr fbc738f2d4 JPEG Exif/thumbnail fixes. 2021-02-26 17:13:16 +01:00
Harald Kuhr 3e3acf3332 More standard key mapping, more correct fit size. Nicer color! 2021-02-26 17:13:16 +01:00
Harald Kuhr 0a77520d67 Merge pull request #591 from KoenDG/batik_upgrade
Upgraded the Apache Batik library from 1.12 to 1.14 due to fixed CVEs.
2021-02-25 14:22:56 +01:00
Koen De Groote 72cd3aade3 Upgraded the Apache Batik library from 1.12 to 1.14 due to fixed CVEs. 2021-02-24 14:54:46 +01:00
Harald Kuhr 88bd9cd2ba Update README.md
Removed JDK 7 from recommended build.
2021-02-04 19:47:45 +01:00
Harald Kuhr 5ee8678a29 Removed XWD plugin that will be in 3.7.
Fixed some incorrect code escaping.
2021-01-23 18:13:18 +01:00
Harald Kuhr fb1937ae63 Updated README with latest version numbers. 2021-01-23 17:53:28 +01:00
Harald Kuhr de02e3d7e0 #582: Fix for missing Exif thumbnail, now only issues warning. 2021-01-11 22:10:25 +01:00
Harald Kuhr ebaa69713f Deprecate for BufferedImageInputStream, now using buffered streams directly in all readers. 2021-01-11 22:07:31 +01:00
Harald Kuhr 8a1a90dafd Fix some corner cases in BufferedImageInputStream. 2021-01-11 21:44:14 +01:00
Harald Kuhr 6f6e65be12 Added zoom to fit option. 2021-01-11 21:18:11 +01:00
Harald Kuhr 253f04066b #579 More reliable CCITT compression type detection 2020-12-23 11:46:58 +01:00
Harald Kuhr 74902b3fb4 StandardCharsets.US_ASCII instead of Charset.forName("ascii") 2020-12-21 17:30:34 +01:00
Harald Kuhr af1a6492d4 #577 Fix TGA subsampling + bonus metadata fix and palette conversion. 2020-12-15 22:19:04 +01:00
Harald Kuhr 0da007ec8c Minor clean-up. 2020-12-11 18:32:02 +01:00
Harald Kuhr 9053fb3816 Minor clean-up. 2020-12-11 18:28:48 +01:00
Harald Kuhr c1d4e474f0 Fix source region reading in VP8Frame. 2020-12-09 09:24:19 +01:00
Harald Kuhr 6bac13eb84 Merge branch 'master' into webp 2020-12-07 17:05:17 +01:00
Harald Kuhr 0e48ddd306 #292 Add legacy CMM option only for JDK 8. 2020-12-03 10:15:26 +01:00
Harald Kuhr 8682decbbc #292 Remove legacy CMM option. 2020-12-03 09:33:08 +01:00
Harald Kuhr bb615b90bf Merge branch 'master' into webp 2020-12-03 08:47:53 +01:00
Harald Kuhr cb0c320b45 #292 Build on Java 8, 11 and 15. 2020-12-03 07:55:30 +01:00
Harald Kuhr 73044bea58 #292 Now builds on Java 8, 11 and 15. 2020-12-02 22:08:40 +01:00
Harald Kuhr 3bb312e9e1 WebP source subsampling. 2020-11-30 22:10:54 +01:00
Harald Kuhr c7d2f422b8 BufferedImageInputStream performance optimizations. 2020-11-30 17:54:21 +01:00
Harald Kuhr 4dedf76ebc WebP performance optimizations & clean up. 2020-11-30 17:10:35 +01:00
Harald Kuhr 2376d16ffd WebP initial commit 2020-11-23 09:34:34 +01:00
Harald Kuhr 1fe0bdd41f Updated code samples to use more modern try-with-resource syntax. 2020-11-23 08:58:43 +01:00
Harald Kuhr 1b4d25342f Minor readme tweaks. 2020-11-22 13:19:43 +01:00
Harald Kuhr bc391550fb Minor readme tweaks. 2020-11-22 13:18:22 +01:00
Harald Kuhr b563f573de Better input validation. 2020-11-21 20:49:21 +01:00
Harald Kuhr 25150b421c Updated links to latest version. 2020-11-21 15:28:51 +01:00
Harald Kuhr 94031a2913 Add XWD to BOM. 2020-11-21 15:26:39 +01:00
Harald Kuhr 64fb421b38 IFF format clean-up + standard metadata support 2020-11-21 15:23:18 +01:00
Harald Kuhr 78af95d747 Merge branch 'master' of github.com:haraldk/TwelveMonkeys 2020-11-19 21:24:23 +01:00
Harald Kuhr 1d4f681b8f #574 Better test data. 2020-11-19 20:59:12 +01:00
Harald Kuhr eda2cd76db #574 Fix for possible OOME in Exif metadata. 2020-11-19 20:42:10 +01:00
Harald Kuhr 4adc60a6c6 Some minor code clean-up. 2020-11-19 20:35:37 +01:00
Harald Kuhr 0d5577a9a4 #330 ImageReaderBase.getDestination now throws IIOException for too large dimension/size. 2020-11-17 23:07:09 +01:00
Harald Kuhr 918f92aba7 #330 Now correctly calculates scanline for 1 & 4 bits 2020-11-17 22:57:25 +01:00
Harald Kuhr 7a24d55be7 #330 Now correctly uses USHORT instead of SHORT for 16 bit DIB. 2020-11-17 22:40:23 +01:00
Harald Kuhr a84cc1c060 #330 Now guards against buffer overruns in RLE decoder. 2020-11-17 22:33:50 +01:00
Harald Kuhr 31cb79d2b9 #330 Minor improvements to avoid RuntimeExceptions. 2020-11-17 22:11:31 +01:00
Harald Kuhr d995e7baa0 Fixed Maven Central link URL to more relevant URL. 2020-11-17 09:15:19 +01:00
Harald Kuhr e7fe6d5c22 Fixed Maven Central link URL 2020-11-16 19:44:41 +01:00
Harald Kuhr 918b698e50 Release notes already on the Github page. 2020-11-16 19:42:47 +01:00
Harald Kuhr 2427b2323f ...and again. 2020-11-16 19:36:40 +01:00
Harald Kuhr 0a8222fea3 ...and again. 2020-11-16 19:36:06 +01:00
Harald Kuhr 60a00b89ae Fixed metadata support (not all formats have it yet). 2020-11-16 19:31:58 +01:00
Harald Kuhr 4c88efa19d Removed empty lines. Added missing BMP info. 2020-11-16 19:18:58 +01:00
Harald Kuhr 17d65a1f6f New & improved README with tables and link to Wiki! 2020-11-16 19:13:36 +01:00
Harald Kuhr fcd03eb903 Added PayPal donation link. Go use it! :-) 2020-11-13 18:29:15 +01:00
Harald Kuhr 4e69efce28 Now correctly uses Image*Input*Stream instead of ImageOutputStream... 2020-11-13 15:52:17 +01:00
Harald Kuhr 16caec4a22 ...and fix the broken test. 2020-10-28 18:48:16 +01:00
Harald Kuhr 08282ea09d Minor improvements and better test cases. 2020-10-28 17:07:59 +01:00
Harald Kuhr c04fed1aff Fixed URL now works, ideally should point to correct branch... 2020-10-28 17:07:59 +01:00
Harald Kuhr 97e788883a More standard way for getting vendor name and version info. 2020-10-28 17:07:59 +01:00
Harald Kuhr a16fce0749 Update README.md 2020-10-27 14:42:18 +01:00
Harald Kuhr 26e2fa0168 Fixed URL now works, ideally should point to correct branch... 2020-10-26 15:49:46 +01:00
Harald Kuhr 120deb3ad4 More standard way for getting vendor name and version info. 2020-10-26 15:40:38 +01:00
Harald Kuhr 0a9e2df5de NetBPM clean-up, fixes and better tests. 2020-10-23 19:25:54 +02:00
Harald Kuhr 6ffcb88872 Verify that RGB data is correct. 2020-10-16 18:21:40 +02:00
Harald Kuhr 960e764c7b Added test to verify how to write CMYK JPEG without ICC profile. 2020-10-16 18:19:08 +02:00
Harald Kuhr d88f27b251 Code clean-up. 2020-10-16 18:17:51 +02:00
Harald Kuhr e5b3e9755e Added missing tests. 2020-10-14 19:13:28 +02:00
Harald Kuhr 6c34fb211f ImageWriterAbstractTest refactorings. 2020-10-14 18:54:46 +02:00
Harald Kuhr 9fdbc3b1fc ImageReaderAbstractTest refactorings. 2020-10-14 17:06:35 +02:00
Harald Kuhr 622c6f40d4 Getting rid of more JUnit deprecation. 2020-10-13 19:29:58 +02:00
Harald Kuhr 107da17ca9 Dependabot broke my build... 2020-10-13 19:16:58 +02:00
Harald Kuhr f9871b73a3 Merge pull request #568 from haraldk/dependabot/maven/sandbox/junit-junit-4.13.1
Bump junit from 4.7 to 4.13.1 in /sandbox
2020-10-13 10:36:50 +02:00
Harald Kuhr 7605b646fe Merge pull request #567 from haraldk/dependabot/maven/common/junit-junit-4.13.1
Bump junit from 4.7 to 4.13.1 in /common
2020-10-13 10:36:37 +02:00
dependabot[bot] 19c62ac7da Bump junit from 4.7 to 4.13.1 in /sandbox
Bumps [junit](https://github.com/junit-team/junit4) from 4.7 to 4.13.1.
- [Release notes](https://github.com/junit-team/junit4/releases)
- [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.13.1.md)
- [Commits](https://github.com/junit-team/junit4/compare/r4.7...r4.13.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-10-13 08:36:22 +00:00
Harald Kuhr a5e4412d1a Merge pull request #566 from haraldk/dependabot/maven/imageio/junit-junit-4.13.1
Bump junit from 4.7 to 4.13.1 in /imageio
2020-10-13 10:36:20 +02:00
Harald Kuhr 651246566a Merge pull request #564 from haraldk/dependabot/maven/servlet/junit-junit-4.13.1
Bump junit from 4.7 to 4.13.1 in /servlet
2020-10-13 10:36:00 +02:00
Harald Kuhr fe8f854b17 Merge pull request #565 from haraldk/dependabot/maven/contrib/junit-junit-4.13.1
Bump junit from 4.7 to 4.13.1 in /contrib
2020-10-13 10:35:47 +02:00
dependabot[bot] a4d20a4af4 Bump junit from 4.7 to 4.13.1 in /common
Bumps [junit](https://github.com/junit-team/junit4) from 4.7 to 4.13.1.
- [Release notes](https://github.com/junit-team/junit4/releases)
- [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.13.1.md)
- [Commits](https://github.com/junit-team/junit4/compare/r4.7...r4.13.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-10-13 07:58:59 +00:00
dependabot[bot] 0643d5910a Bump junit from 4.7 to 4.13.1 in /imageio
Bumps [junit](https://github.com/junit-team/junit4) from 4.7 to 4.13.1.
- [Release notes](https://github.com/junit-team/junit4/releases)
- [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.13.1.md)
- [Commits](https://github.com/junit-team/junit4/compare/r4.7...r4.13.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-10-13 07:58:58 +00:00
dependabot[bot] c78a456985 Bump junit from 4.7 to 4.13.1 in /contrib
Bumps [junit](https://github.com/junit-team/junit4) from 4.7 to 4.13.1.
- [Release notes](https://github.com/junit-team/junit4/releases)
- [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.13.1.md)
- [Commits](https://github.com/junit-team/junit4/compare/r4.7...r4.13.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-10-13 07:49:27 +00:00
dependabot[bot] 27017576d3 Bump junit from 4.7 to 4.13.1 in /servlet
Bumps [junit](https://github.com/junit-team/junit4) from 4.7 to 4.13.1.
- [Release notes](https://github.com/junit-team/junit4/releases)
- [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.13.1.md)
- [Commits](https://github.com/junit-team/junit4/compare/r4.7...r4.13.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-10-13 07:39:02 +00:00
Harald Kuhr f1810be10a X Window dump support. 2020-10-07 19:50:00 +02:00
Harald Kuhr 021aba1a98 Better output when debugging readers. 2020-10-07 19:47:09 +02:00
Harald Kuhr a0b68adff3 Removed work in progress... 2020-10-02 09:58:34 +02:00
Harald Kuhr fa4586663c Fixed a minor dependency issue. All test-jar dependencies now has correct test scope. 2020-10-02 09:41:39 +02:00
Harald Kuhr 623d13a517 Better PFM support. 2020-09-25 19:35:55 +02:00
Harald Kuhr a7ebc1b52f Code clean-up. 2020-09-24 17:12:12 +02:00
Harald Kuhr f54f4370c0 Added PNMImageWriterTest 2020-09-24 17:11:19 +02:00
Harald Kuhr 5040e9fe8a Add missing tests. 2020-09-24 15:07:40 +02:00
Harald Kuhr fc72cd34a1 Minor language fix. 2020-09-24 09:54:14 +02:00
Harald Kuhr 6d71a3d306 Added section about re-packaging and Shade plugin. 2020-09-24 09:51:30 +02:00
Harald Kuhr 86f8cf52a5 Comment fix 2020-08-07 16:36:08 +02:00
Harald Kuhr bda6544a5f #556 PICTImageReaderSpi no longer claim to decode known formats 2020-08-07 16:31:29 +02:00
Harald Kuhr 49c7cd1979 #466 TGA extension size fix for 3ds max files 2020-08-07 11:24:55 +02:00
Harald Kuhr 9dae58d5a6 Code clean-up. 2020-08-07 08:49:37 +02:00
Harald Kuhr ed14b97199 Update readme to 3.6. 2020-07-10 23:51:15 +02:00
Harald Kuhr b94135a91c [maven-release-plugin] prepare for next development iteration 2020-07-10 22:49:24 +02:00
Harald Kuhr 7384118357 [maven-release-plugin] prepare release twelvemonkeys-3.6 2020-07-10 22:49:16 +02:00
Harald Kuhr 4d833a50e5 TIFF constants. 2020-07-10 22:41:47 +02:00
Harald Kuhr 57b0fdac0b Fix JPEG tests mk II. 2020-07-10 22:29:23 +02:00
Harald Kuhr e6bd94025f JPEG Metadata clean-up 2020-07-10 22:26:53 +02:00
Harald Kuhr 330a0414f0 Fix JPEG tests 2020-07-10 22:26:12 +02:00
Harald Kuhr 5cc201b46d JPEG Exif rotation in metadata + support 2020-07-10 22:05:46 +02:00
Harald Kuhr 7e55d7765d #550 Adobe path points now constrained to a more robust [-16...16] range 2020-07-10 19:29:50 +02:00
Harald Kuhr 8f942922fd #547 BMPImageWriterSpi now only claims to write TYPE_4BYTE_ABGR, and registers with low pri.
Better exception message for other image types.
2020-06-28 11:50:17 +02:00
Harald Kuhr db5635e844 #535: Detect incorrect compression in TIFF CCITT stream. 2020-06-16 21:54:16 +02:00
Harald Kuhr 8bc952ba66 #464/#465 Collection fixes for forward compatibility. 2020-04-15 16:03:12 +02:00
Harald Kuhr 96cb3a07f4 #525: Fix for negative arrays size in old style JPEG in TIFF. 2020-04-15 13:28:56 +02:00
Harald Kuhr cd6a6258b6 Formatting and proper comments 2020-04-15 12:26:41 +02:00
Harald Kuhr a0fa2c08ac Merge pull request #529 from actinium15/issue/526
#526 Preventing SSRF due to external resource refs in SVGs
2020-04-03 13:57:57 +02:00
Ashish Chopra 6642b1647a #526 Incorporating review comments
* moved the system-property-evaluation to static-block for more
  reliability
* updated impacted existing tests (which relied on external-resources) to
  leverage the new API
* set the value of system-property controlling external-resource-access
  to true in the test JVM for the sake of existing tests using
  SVGImageReader APIs not backed by SVRReadParams (.getWidth/.getHeight
  and such)
2020-03-30 18:29:34 +05:30
Ashish Chopra 5315caf830 #526 Fixed unnecessary 'overenginnering' in previous commit 🤦 2020-03-30 10:38:52 +05:30
Ashish Chopra 872523b0f0 #526 Incorporating review comments
* renaming accessors
* changing the default to disallow external resources
* introduced system-property for backwards compatibility
* honor system-property (if present) and SVGReadParams.isAllowExternalResources hasn't been called
  (as against ignoring system-property and reverting to 'block-by-default' if SVGReadParams.isAllowExternalResources invoked)
* added + updated test cases
2020-03-24 18:40:01 +05:30
Ashish Chopra 7bf99fb496 #526 Preventing SSRF due to external resource refs in SVGs 2020-02-25 11:26:16 +05:30
Harald Kuhr a1047edddb Merge pull request #522 from Schmidor/svg_size
#518 Parsing SVG width/height attributes
2020-01-31 15:50:24 +01:00
Oliver Schmidtmer e956176872 #518 Fallbacks for aspect ratio, if only one dimension is given 2020-01-30 18:40:11 +01:00
Oliver Schmidtmer 6d2947b080 #518 Parsing SVG width/height attributes 2020-01-30 15:54:59 +01:00
Harald Kuhr fb304d6c27 #520: Fix for incorrect serialization of single element arrays in metadata. 2020-01-29 20:58:34 +01:00
Harald Kuhr 903289caa4 Merge pull request #517 from KoenDG/small_cleanup
Minor code cleanup: Intellij suggested changes from static code analysis.
2020-01-29 09:38:57 +01:00
Koen De Groote aff31ebd1b Intellij suggested changes from static code analysis. 2020-01-28 16:18:07 +01:00
Harald Kuhr b6773f6983 Updated versions. 2020-01-22 22:43:42 +01:00
Harald Kuhr 0d28eb31d2 [maven-release-plugin] prepare for next development iteration 2020-01-22 21:47:25 +01:00
Harald Kuhr 4922ccf80b [maven-release-plugin] prepare release twelvemonkeys-3.5 2020-01-22 21:47:17 +01:00
Harald Kuhr f7ce772f84 Merge pull request #516 from haraldk/adobe-path-write
Adobe Path write support
2020-01-22 21:38:43 +01:00
Harald Kuhr 3d4bc0e69d #490: License. 2020-01-22 21:03:02 +01:00
Harald Kuhr c721291a78 #490: Last minute API changes... 2020-01-22 20:51:39 +01:00
Harald Kuhr 5f9ea2e7c2 #490: Doc. 2020-01-22 20:49:18 +01:00
Harald Kuhr c48af5acc7 #490: Minor API clean-up and documentation. 2020-01-22 20:35:41 +01:00
Harald Kuhr 8b86b57e63 #490: Fix for "incomplete" paths with implicit line back to start. 2020-01-22 20:06:30 +01:00
Harald Kuhr 420f78be88 #490: Fix for "incomplete" paths with implicit line back to start. 2020-01-17 16:44:42 +01:00
Harald Kuhr d2b58ed20e #490: Now allows writing using standard TIFF writer in Java 9+ 2020-01-17 16:43:27 +01:00
Harald Kuhr 278ce6ef33 #490: Now allows writing paths in TIFF and JPEG. 2020-01-16 19:31:54 +01:00
Harald Kuhr 768bc30653 Merge branch 'master' into adobe-path-write 2020-01-15 15:11:31 +01:00
Harald Kuhr d9d8419803 #514: Fix integer overflow in stripbytecounts computation. 2020-01-14 21:18:13 +01:00
Harald Kuhr 7a9523690c Update LICENSE.txt 2020-01-14 20:40:38 +01:00
Harald Kuhr e5c6832ec0 #490: Allow writing more TIFF fields. 2020-01-13 21:05:04 +01:00
Harald Kuhr 167686bdea #490: Minor debug cleanup. 2020-01-09 21:28:37 +01:00
Harald Kuhr d3249dc3d5 #490: Refactorings. 2020-01-09 21:25:41 +01:00
Harald Kuhr 5c1f51f3ca #490: Fixed roundtrip tests and tuned collinearity threshold. 2020-01-09 21:23:20 +01:00
Harald Kuhr fde3a5c2ab Updated version numbers. 2020-01-09 20:24:22 +01:00
Harald Kuhr ae213dcf5e log4j removal 2020-01-09 19:18:15 +01:00
Harald Kuhr 51ace4ca7f #490: Refactorings, added initial detection of linked/unlinked segments, more tests. 2020-01-09 19:17:35 +01:00
Harald Kuhr f15bcc7df9 log4j removal 2020-01-07 13:44:40 +01:00
Harald Kuhr 6be86affd6 #490: Fixed tests 2020-01-02 15:32:06 +01:00
Harald Kuhr eec8268eb9 #490: Adobe Path PoC 2020-01-02 15:26:48 +01:00
Harald Kuhr 859b232f64 #490: Initial commit Adobe Path write. 2020-01-02 15:24:22 +01:00
Harald Kuhr 5501c0e709 #510: Fixes possible hang situation 2020-01-02 14:55:16 +01:00
Harald Kuhr 306d8ac5f6 Merge pull request #505 from KoenDG/intellij_performance_analysis
Changes suggested by Intellij in the category "Performance".
2019-11-04 08:43:22 +01:00
Koen De Groote d41f6a4465 Update BMPImageReader.java
Not using this change for now
2019-11-02 21:09:25 +01:00
Koen De Groote 8ed5f06151 Changes suggested by Intellij in the category "Performance".
Updated.
2019-10-22 11:51:24 +02:00
Harald Kuhr 42e17f2063 #501: Fix for TYPE_USHORT_555/565_RGB, ColorModel now 16 bits 2019-10-11 16:56:00 +02:00
Harald Kuhr f449a50d64 TIFF tile support. 2019-08-29 19:11:14 +02:00
Harald Kuhr 0c2433dc9f #289, #493: Finally implemented subsampling for < 8 bit samples 2019-08-29 19:11:14 +02:00
Harald Kuhr a5def1ba3d Merge pull request #495 from Schmidor/svg_rescale_viewbox
Svg scale by ViewBox
2019-08-22 19:59:07 +02:00
Oliver Schmidtmer 810b7197ab Test correction for ViewBox based svg sizes 2019-08-22 16:51:34 +02:00
Oliver Schmidtmer 24a0786d64 When rescaling SVGs use the ViewBox, if defined, for default sizes 2019-08-22 16:35:40 +02:00
Harald Kuhr c1ce4b066d Updated version numbers. 2019-08-12 21:54:15 +02:00
Harald Kuhr b6aec25f00 Stick to the git user, when using ssh 2019-08-12 21:12:17 +02:00
Harald Kuhr c1834dc78d Switched Maven SCM to use ssh. 2019-08-12 20:40:50 +02:00
Harald Kuhr 9e23413456 Fixed JavaDoc errors to enable Java 8 build. 2019-08-10 00:41:36 +02:00
Harald Kuhr 7d2c692663 Fixed encoding issues in readme. 2019-08-09 20:58:56 +02:00
Harald Kuhr 9ce1a86cae #398: Better exception message when BufferedImage size > Integer.MAX 2019-08-09 19:52:07 +02:00
Harald Kuhr b0b5de5fa4 #477: Better tests to for cyclic loops in IFDs. 2019-08-09 19:32:52 +02:00
Harald Kuhr 64178bd636 #474: Fix java.awt.color.CMMException: Can not access specified profile 2019-08-08 23:33:08 +02:00
Harald Kuhr 7985cdd4a7 449# Missing test to verify that we ignore bogus 0 byte counts. 2019-08-08 23:17:08 +02:00
Harald Kuhr 8247861095 #492: Now correctly parses multiple strings for ASCII fields. 2019-08-08 23:03:13 +02:00
Harald Kuhr 8480c929c1 #477: Avoid cyclic loops in IFDs. 2019-08-08 22:41:41 +02:00
Harald Kuhr e6f437f55b Moving travis build to trusty, to fix JDK8 build 2019-08-08 09:25:29 +02:00
Harald Kuhr ee049d9465 #488: Fix for AIOOBE in getEmbeddedICCProfile when ICC profile is empty 2019-08-07 16:18:01 +02:00
Harald Kuhr f6aa810f8b #489 Fix for Unexpected End of PackBits Stream for padded streams. 2019-08-07 16:04:46 +02:00
Harald Kuhr e8d1b999a2 #469 Fix for OOME while reading Exif with corrupted IFDs/counts 2019-08-07 15:55:24 +02:00
Harald Kuhr d7fbd6594e JPEG Exif parsing optimization. 2019-08-07 15:47:33 +02:00
Harald Kuhr caef9b6a9e XMLSerializer speedup. 2019-02-12 20:44:34 +01:00
Harald Kuhr 7fad4d5cd8 #475: Fix support for writing TIFF > 2 GB 2019-02-12 20:40:08 +01:00
Harald Kuhr 3e4b14f984 #473: Fix for ColorMap (Indexed) TIFF with non-alpha ExtraSamples. 2019-02-12 20:34:31 +01:00
Harald Kuhr 5ade293445 Merge pull request #458 from Schmidor/455
Fix for #455: Ignore profiles PhotometricInterpretation MinIsWhite wi…
2019-02-12 20:29:31 +01:00
Harald Kuhr a0fc5f560d Merge pull request #472 from paulvi/patch-2
colorize code snippets in README using GFM ```java, ```xml
2019-01-30 10:58:08 +01:00
Paul Verest 51b0734462 colorize code snippets in README using GFM ``java, ``xml 2019-01-28 15:31:42 +08:00
Harald Kuhr 8e3cd5aedb Updated commons-fileupload dependency to safe version, even if in scope provided, just in case. 2019-01-08 21:47:05 +01:00
Oliver Schmidtmer cbaa0dc688 Fix for #455: Ignore profiles PhotometricInterpretation MinIsWhite with 1bit/pixel 2018-11-22 12:20:58 +01:00
Harald Kuhr 61bac97933 Fix test case for #446 in case of "Gerenic CMYK" profile not being available. 2018-10-26 22:28:31 +02:00
Harald Kuhr 3e9be32279 Better assertion failed messages. 2018-10-26 22:14:57 +02:00
Harald Kuhr d41acaee05 Remnoved System.err.println 2018-10-26 22:13:25 +02:00
Harald Kuhr a0955875d8 Test case for #446. 2018-10-26 20:34:57 +02:00
Harald Kuhr 38ea0989d4 Merge pull request #446 from bernhardf-ro/master
Fix for corrupted output from non-RGB TIFF images with extra samples
2018-10-26 20:21:12 +02:00
Harald Kuhr 7e0de14783 Various Java 9 and beyond fixes:
- ExtraSamplesColorModel now overrides getComponentSize for correct size
 - TIFFImageReader/WriterSpi now recognizes the JEP 262 TIFF plugin
 - CCITTFaxEncoderStreamTest now directly creates writer (not JDK one)
2018-10-26 19:48:31 +02:00
Harald Kuhr de34d4642e Various Java 9 and beyond fixes. 2018-10-26 19:40:00 +02:00
Harald Kuhr b86bad2bf9 Equals method for DiscreteAlphaIndexColorModel, needed for Java 10. 2018-10-11 22:36:41 +02:00
Harald Kuhr cc5af0134c #442 SVG BaseURI issue fix 2018-10-11 21:38:10 +02:00
Bernhard Fey edcac25f5e Added test file for non-alpha extra channel
Added test file for CMYK TIFF with non-alpha extra channel
2018-09-27 10:07:57 +02:00
Bernhard Fey 5a5c1f996b Modified new create[ImageTypeSpecifier] method according to suggestions by @haraldk
Renamed "create" method to "createImageTypeSpecifier".
Added exception thrown for unsupported planarConfiguration values.
2018-09-26 10:56:49 +02:00
Bernhard Fey f9b8cae0ea Fix for non-RGB images with extra sample
Added "create" method to TIFFImageReader that centralizes the use of "createBanded" and "createInterleaved" as well as the "extra samples" special case and fixes non-RGB images with non-alpha extra samples
2018-09-25 10:24:20 +02:00
Harald Kuhr 62538c3db9 Updated readme 2018-09-10 13:37:29 +02:00
Harald Kuhr 48aa504be4 Fixed release date. 2018-09-08 17:32:09 +02:00
Harald Kuhr ed2eb7642a Updated versions from 3.4 to 3.4.1. 2018-09-08 17:23:01 +02:00
Harald Kuhr 24ec5ae34d #408 Added servlet JAR to dependency example. 2018-09-08 17:21:55 +02:00
Harald Kuhr 0cd250646a #438 CompoundDocument file descriptor fix
(cherry picked from commit 04f27a1)
2018-09-08 16:23:43 +02:00
Harald Kuhr 586359e7ab #379 TGA write 2018-09-08 16:04:06 +02:00
Harald Kuhr 5a3945c411 Merge remote-tracking branch 'origin/master' 2018-09-07 21:07:23 +02:00
Harald Kuhr a7b374e51a #311 Updated some more license headers that was left out in the initial change. 2018-09-07 21:07:08 +02:00
Harald Kuhr 1a1067a6bd #82 ICO write 2018-09-07 21:02:12 +02:00
Harald Kuhr d8d0131806 #437 Catching exception from static init, prints a warning and moves on. 2018-08-30 15:02:04 +02:00
Harald Kuhr 60180938d2 #439: Unclear exception message when passing image metadata as stream metadata 2018-08-30 14:46:55 +02:00
Harald Kuhr c74630656f Removed a deprecated method. 2018-08-26 10:35:53 +02:00
Harald Kuhr 96901e020f #83 ICNS write 2018-08-25 15:34:07 +02:00
Harald Kuhr 7ff74465d3 Merge pull request #426 from ckovorodkin/camera-raw-cr2-slice
CR2ImageReader: unslice result of JPEGLosslessDecoder
2018-08-21 17:50:57 +02:00
Harald Kuhr 5e3229c9b7 Merge pull request #425 from ckovorodkin/jpeg-lossless-decoder-components
JPEGLosslessDecoder: Support for various component count.
2018-08-21 17:50:24 +02:00
Harald Kuhr 1f5bf80f7e Merge pull request #424 from ckovorodkin/camera-raw
Updated code base for raw modules
2018-08-21 17:48:45 +02:00
Harald Kuhr fc8c891f07 Merge pull request #436 from Schmidor/contrib_tiffutilities_jpeg6
TIFFUtilities: Enhanced support for old-style to new-style JPEG transformation
2018-08-21 17:44:09 +02:00
Oliver Schmidtmer 632e5e6664 TIFFUtilities: Enhanced support for old-style to new-style JPEG transformation 2018-08-21 15:04:21 +02:00
Harald Kuhr 7af0cfaa78 Update README.md 2018-08-20 11:38:46 +02:00
Harald Kuhr f4b154f6c8 Update README.md 2018-08-20 11:32:29 +02:00
Harald Kuhr b09a8a5dbe Updated License. 2018-08-18 23:37:59 +02:00
Harald Kuhr a651939274 Updated readme. 2018-08-18 23:26:44 +02:00
Harald Kuhr 08283299ba #311 Updated license headers to be the same as in the LICENSE.txt 2018-08-18 23:17:54 +02:00
Harald Kuhr 1228043d57 [maven-release-plugin] prepare for next development iteration 2018-08-18 20:29:07 +02:00
oermolaev 5c35c6e997 CR2ImageReader: unslice result of JPEGLosslessDecoder. 2018-05-05 03:21:10 +03:00
oermolaev 7692eeef5e Merge branch 'jpeg-lossless-decoder-components' into camera-raw 2018-05-05 01:39:13 +03:00
oermolaev c924f284a0 JPEGLosslessDecoder: Support for various component count. 2018-05-05 01:38:37 +03:00
oermolaev cdadbf7051 The code base for raw modules was updated to compile in current environment. 2018-05-04 03:25:15 +03:00
oermolaev 7c2423bc94 Merge remote-tracking branch 'origin/master' into camera-raw
# Conflicts:
#	imageio/pom.xml
2018-05-04 01:25:12 +03:00
Harald Kuhr f9837d4540 TMI-DNG: Adding @Ignore on tests 2014-10-15 12:27:05 +02:00
Harald Kuhr 7728a912b1 TMI-CR2: Commented out lossless JPEG. 2014-10-15 12:26:29 +02:00
Harald Kuhr 923598636e POM including camera RAW plugins. 2014-10-15 12:23:49 +02:00
Harald Kuhr 6ab0e64107 TMI-DNG: Initial commit. 2014-10-15 12:22:55 +02:00
Harald Kuhr f69f706f60 TMI-NEF: Initial commit. 2014-10-15 12:22:38 +02:00
Harald Kuhr 53dd493cfa TMI-CR2: Initial commit. 2014-10-15 12:22:12 +02:00
Harald Kuhr 607502fbbd Merge pull request #68 from haraldk/master
TMI-META: Now parsing SubIFDs.
2014-10-15 12:17:05 +02:00
491 changed files with 29882 additions and 5524 deletions
+1
View File
@@ -0,0 +1 @@
github: haraldk
+53
View File
@@ -0,0 +1,53 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: Reported bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**Version information**
1. The version of the TwelveMonkeys ImageIO library in use.
For example: 4.0.0
2. The *exact* output of `java --version` (or `java -version` for older Java releases).
For example:
java version "1.8.0_271"
Java(TM) SE Runtime Environment (build 1.8.0_271-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.271-b09, mixed mode)
3. Extra information about OS version, server version, standalone program or web application packaging, executable wrapper, etc. Please state exact version numbers where applicable.
**To Reproduce**
Steps to reproduce the behavior:
1. Compile the below sample code
2. Download the sample image file
3. Run the code with the sample file
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Example code**
Preferably as a failing JUnit test, or a standalone program with a `main` method that showcases the problem.
Less is more. Don't add your entire project, only the code required to reproduce the problem. 😀
**Sample file(s)**
Attach any sample files needed to reproduce the problem. Use a ZIP-file if the format is not directly supported by GitHub.
**Stak trace**
Always include the stack trace you experience.
**Screenshots**
If applicable, add screenshots to help explain your problem.
Do not add screenshots of code or stack traces. 😀
**Additional context**
Add any other context about the problem here.
+20
View File
@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: New feature
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem or use case is.
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here, like links to specifications or sample files.
@@ -0,0 +1,13 @@
---
name: Trouble shooting and programming help
about: "General programming issues will reach a wider audience at StackOverflow. Tag
questions with javax-imageio and/or twelvemonkeys \U0001F600 "
title: ''
labels: Trouble-shooting
assignees: ''
---
General programming issues and problems will reach a much wider audience at StackOverflow, we suggest you ask them there. This will offload our work with maintaining the library, and make sure you get better help sooner.
Tag the question with `javax-imageio` and/or `twelvemonkeys` and we'll find them there.
@@ -0,0 +1,17 @@
**What is fixed**
Add link to the issue this PR fixes.
Fixes #42.
**Why is this change proposed**
If this change does *not* fix an open issue, briefly describe the rationale for this PR.
**What is changed**
Briefly describe the changes proposed in this pull request:
* Fixed rare exception happening in `x >= 42` case
* Small optimization of `decompress()` method
* Corrected API doc for `compress()` method to reflect current implementation
+8 -8
View File
@@ -1,14 +1,14 @@
dist: trusty
language: java
jdk:
- oraclejdk8
# Oracle JDK 7 no longer supported, we use env matrix to test various CMM providers
# - oraclejdk7
# Some JPEGImageReader tests fail on OpenJDK, need to investigate/fix before enabling
# - openjdk7
env:
- MAVEN_OPTS=-Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider
- MAVEN_OPTS=""
- oraclejdk8 # Legacy
- oraclejdk11 # LTS
- oraclejdk15 # Latest
jobs:
include:
# Extra job, testing legacy CMM option
- jdk: oraclejdk8
env: MAVEN_OPTS=-Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider
cache:
directories:
- $HOME/.m2
+1 -1
View File
@@ -1,6 +1,6 @@
BSD 3-Clause License
Copyright (c) 2017, Harald Kuhr
Copyright (c) 2008-2020, Harald Kuhr
All rights reserved.
Redistribution and use in source and binary forms, with or without
+272 -423
View File
@@ -1,15 +1,13 @@
## Latest
Master branch build status: [![Build Status](https://travis-ci.org/haraldk/TwelveMonkeys.svg?branch=master)](https://travis-ci.org/haraldk/TwelveMonkeys)
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).
[![Build Status](https://travis-ci.org/haraldk/TwelveMonkeys.svg?branch=master)](https://travis-ci.org/haraldk/TwelveMonkeys)
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.twelvemonkeys.imageio/imageio/badge.svg?color=slateblue)](https://maven-badges.herokuapp.com/maven-central/com.twelvemonkeys.imageio/imageio)
[![StackOverflow](https://img.shields.io/badge/stack_overflow-twelvemonkeys-orange.svg)](https://stackoverflow.com/questions/tagged/twelvemonkeys)
[![Donate](https://img.shields.io/badge/donate-PayPal-blue.svg)](https://paypal.me/haraldk76/100)
## About
TwelveMonkeys ImageIO is a collection of plugins and extensions for Java's ImageIO.
These plugins extends the number of image file formats supported in Java, using the javax.imageio.* package.
These plugins extend the number of image file formats supported in Java, using the `javax.imageio.*` package.
The main purpose of this project is to provide support for formats not covered by the JRE itself.
Support for formats is important, both to be able to read data found
@@ -19,246 +17,64 @@ The goal is to create a set of efficient and robust ImageIO plug-ins, that can b
----
## Features
## File formats supported
Mainstream format support
| Plugin | Format | Description | Read | Write | Metadata | Notes |
| ------ | -------- | ----------- |:----:|:-----:| -------- | ----- |
| Batik | **SVG** | Scalable Vector Graphics | ✔ | - | - | Requires [Batik](https://xmlgraphics.apache.org/batik/)
| | WMF | MS Windows Metafile | ✔ | - | - | Requires [Batik](https://xmlgraphics.apache.org/batik/)
| [BMP](https://github.com/haraldk/TwelveMonkeys/wiki/BMP-Plugin) | **BMP** | MS Windows and IBM OS/2 Device Independent Bitmap | ✔ | ✔ | [Native](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/bmp_metadata.html) & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| | CUR | MS Windows Cursor Format | ✔ | - | - |
| | ICO | MS Windows Icon Format | ✔ | ✔ | - |
| [HDR](https://github.com/haraldk/TwelveMonkeys/wiki/HDR-Plugin) | HDR | Radiance High Dynamic Range RGBE Format | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| [ICNS](https://github.com/haraldk/TwelveMonkeys/wiki/ICNS-Plugin) | ICNS | Apple Icon Image | ✔ | ✔ | - |
| [IFF](https://github.com/haraldk/TwelveMonkeys/wiki/IFF-Plugin) | IFF | Commodore Amiga/Electronic Arts Interchange File Format | ✔ | ✔ | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| [JPEG](https://github.com/haraldk/TwelveMonkeys/wiki/JPEG-Plugin) | **JPEG** | Joint Photographers Expert Group | ✔ | ✔ | [Native](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/jpeg_metadata.html#image) & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| | JPEG Lossless | | ✔ | - | [Native](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/jpeg_metadata.html#image) & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| [PCX](https://github.com/haraldk/TwelveMonkeys/wiki/PCX-Plugin) | PCX | ZSoft Paintbrush Format | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| | DCX | Multi-page PCX fax document | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| [PICT](https://github.com/haraldk/TwelveMonkeys/wiki/PICT-Plugin) | PICT | Apple QuickTime Picture Format | ✔ | ✔ | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| | PNTG | Apple MacPaint Picture Format | ✔ | | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| [PNM](https://github.com/haraldk/TwelveMonkeys/wiki/PNM-Plugin) | PAM | NetPBM Portable Any Map | ✔ | ✔ | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| | PBM | NetPBM Portable Bit Map | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| | PGM | NetPBM Portable Grey Map | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| | PPM | NetPBM Portable Pix Map | ✔ | ✔ | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| | PFM | Portable Float Map | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| [PSD](https://github.com/haraldk/TwelveMonkeys/wiki/PSD-Plugin) | **PSD** | Adobe Photoshop Document | ✔ | - | Native & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| | PSB | Adobe Photoshop Large Document | ✔ | - | Native & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| [SGI](https://github.com/haraldk/TwelveMonkeys/wiki/SGI-Plugin) | SGI | Silicon Graphics Image Format | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| [TGA](https://github.com/haraldk/TwelveMonkeys/wiki/TGA-Plugin) | TGA | Truevision TGA Image Format | ✔ | ✔ | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|ThumbsDB| Thumbs.db| MS Windows Thumbs DB | ✔ | - | - | OLE2 Compound Document based format only
| [TIFF](https://github.com/haraldk/TwelveMonkeys/wiki/TIFF-Plugin) | **TIFF** | Aldus/Adobe Tagged Image File Format | ✔ | ✔ | [Native](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/tiff_metadata.html#ImageMetadata) & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| | BigTIFF | | ✔ | - | [Native](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/tiff_metadata.html#ImageMetadata) & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| [WebP](https://github.com/haraldk/TwelveMonkeys/wiki/WebP-Plugin) | **WebP** | Google WebP Format | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
| XWD | XWD | X11 Window Dump Format | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
#### BMP - MS Windows/IBM OS/2 Device Independent Bitmap
* Read support for all known versions of the DIB/BMP format
* Indexed color, 1, 4 and 8 bit, including 4 and 8 bit RLE
* RGB, 16, 24 and 32 bit
* Embedded PNG and JPEG data
* Windows and OS/2 versions
* Native and standard metadata format
#### JPEG
* Read support for the following JPEG "flavors":
* All JFIF compliant JPEGs
* All Exif compliant JPEGs
* YCbCr JPEGs without JFIF segment (converted to RGB, using embedded ICC profile)
* CMYK JPEGs (converted to RGB by default or as CMYK, using embedded ICC profile)
* Adobe YCCK JPEGs (converted to RGB by default or as CMYK, using embedded ICC profile)
* JPEGs containing ICC profiles with interpretation other than 'Perceptual' or class other than 'Display'
* JPEGs containing ICC profiles that are incompatible with stream data, corrupted ICC profiles or corrupted `ICC_PROFILE` segments
* JPEGs using non-standard color spaces, unsupported by Java 2D
* JPEGs with APP14/Adobe segments with length other than 14 bytes
* 8 bit JPEGs with 16 bit DQT segments
* Issues warnings instead of throwing exceptions in cases of corrupted or non-conformant data where ever the image
data can still be read in a reasonable way
* Thumbnail support:
* JFIF thumbnails (even if stream contains "inconsistent metadata")
* JFXX thumbnails (JPEG, Indexed and RGB)
* EXIF thumbnails (JPEG, RGB and YCbCr)
* Metadata support:
* JPEG metadata in both standard and native formats (even if stream contains "inconsistent metadata")
* `javax_imageio_jpeg_image_1.0` format (currently as native format, may change in the future)
* Non-conforming combinations of JFIF, Exif and Adobe markers, using "unknown" segments in the
"MarkerSequence" tag for the unsupported segments (for `javax_imageio_jpeg_image_1.0` format)
* Extended write support:
* CMYK JPEGs
* YCCK JPEGs in progress
#### JPEG-2000
* Possibly coming in the future, pending some license issues.
If you are one of the authors, or know one of the authors and/or the current license holders of either the original
jj2000 package or the JAI ImageIO project, please contact me (I've tried to get in touch in various ways,
without success so far).
Alternatively, if you have or know of a JPEG-2000 implementation in Java with a suitable license, get in touch. :-)
#### PNM - NetPBM Portable Any Map
* Read support for the following file types:
* PBM in 'P1' (ASCII) and 'P4' (binary) formats, 1 bit per pixel
* PGM in 'P2' (ASCII) and 'P5' (binary) formats, up to 16/32 bits per pixel
* PPM in 'P3' (ASCII) and 'P6' (binary) formats, up to 16/32 bits per pixel component
* PAM in 'P7' (binary) format up to 32 bits per pixel component
* Limited support for PFM in 'Pf' (gray) and 'PF' (RGB) formats, 32 bits floating point
* Write support for the following formats:
* PPM in 'P6' (binary) format
* PAM in 'P7' (binary) format
* Standard metadata support
#### PSD - Adobe Photoshop Document
* Read support for the following file types:
* Monochrome, 1 channel, 1 bit
* Indexed, 1 channel, 8 bit
* Gray, 1 channel, 8, 16 and 32 bit
* Duotone, 1 channel, 8, 16 and 32 bit
* RGB, 3-4 channels, 8, 16 and 32 bit
* CMYK, 4-5 channels, 8, 16 and 32 bit
* Read support for the following compression types:
* Uncompressed
* RLE (PackBits)
* Layer support
* Image layers only, in all of the above types
* Thumbnail support
* JPEG
* RAW (RGB)
* Support for "Large Document Format" (PSB)
* Native and Standard metadata support
#### TIFF - Aldus/Adobe Tagged Image File Format
* Read support for the following "Baseline" TIFF file types:
* Class B (Bi-level), all relevant compression types, 1 bit per sample
* Class G (Gray), all relevant compression types, 2, 4, 8, 16 or 32 bits per sample, unsigned integer
* Class P (Palette/indexed color), all relevant compression types, 1, 2, 4, 8 or 16 bits per sample, unsigned integer
* Class R (RGB), all relevant compression types, 8 or 16 bits per sample, unsigned integer
* Read support for the following TIFF extensions:
* Tiling
* Class F (Facsimile), CCITT Modified Huffman RLE, T4 and T6 (type 2, 3 and 4) compressions.
* LZW Compression (type 5)
* "Old-style" JPEG Compression (type 6), as a best effort, as the spec is not well-defined
* JPEG Compression (type 7)
* ZLib (aka Adobe-style Deflate) Compression (type 8)
* Deflate Compression (type 32946)
* Horizontal differencing Predictor (type 2) for LZW, ZLib, Deflate and PackBits compression
* Alpha channel (ExtraSamples type 1/Associated Alpha and type 2/Unassociated Alpha)
* CMYK data (PhotometricInterpretation type 5/Separated)
* YCbCr data (PhotometricInterpretation type 6/YCbCr) for JPEG
* CIELab data in TIFF, ITU and ICC variants (PhotometricInterpretation type 9, 10 and 11)
* Planar data (PlanarConfiguration type 2/Planar)
* ICC profiles (ICCProfile)
* BitsPerSample values up to 16 for most PhotometricInterpretations
* Multiple images (pages) in one file
* Write support for most "Baseline" TIFF options
* Uncompressed, PackBits, ZLib and Deflate
* Additional support for CCITT T4 and and T6 compressions.
* Additional support for LZW and JPEG (type 7) compressions
* Horizontal differencing Predictor (type 2) for LZW, ZLib, Deflate
* Native and Standard metadata support
Legacy formats
#### HDR - Radiance High Dynamic Range RGBE Format
* Read support for the most common RGBE (.hdr) format
* Samples are converted to 32 bit floating point (`float`) and normalized using a global tone mapper by default.
* Support for custom global tone mappers
* Alternatively, use a "null-tone mapper", for unnormalized data (allows local tone mapping)
* Unconverted RGBE samples accessible using `readRaster`
* Standard metadata support
#### IFF - Commodore Amiga/Electronic Arts Interchange File Format
* Legacy format, allows reading popular image format from the Commodore Amiga computer.
* Read support for the following file types:
* ILBM Indexed color, 1-8 interleaved bit planes, including 6 bit EHB
* ILBM Gray, 8 bit interleaved bit planes
* ILBM RGB, 24 and 32 bit interleaved bit planes
* ILBM HAM6 and HAM8
* PBM Indexed color, 1-8 bit,
* PBM Gray, 8 bit
* PBM RGB, 24 and 32 bit
* PBM HAM6 and HAM8
* Write support
* ILBM Indexed color, 1-8 bits per sample, 8 bit gray, 24 and 32 bit true color.
* Support for the following compression types (read/write):
* Uncompressed
* RLE (PackBits)
#### PCX - ZSoft Paintbrush Format
* Read support for the following file types:
* Indexed color, 1, 2, 4 or 8 bits per pixel, bit planes or interleaved
* Grayscale, 8 bits per pixel
* Color (RGB), 8 bits per pixel component
* Read support for DCX (multi-page) fax format, containing any of the above types
* Support for the following compression types:
* Uncompressed (experimental)
* RLE compressed
* Standard metadata support
#### PICT - Apple Mac Paint Picture Format
* Legacy format, especially useful for reading OS X clipboard data.
* Read support for the following file types:
* QuickDraw (format support is not complete, but supports most OS X clipboard data as well as RGB pixel data)
* QuickDraw bitmap
* QuickDraw pixmap
* QuickTime stills
* Write support for RGB pixel data:
* QuickDraw pixmap
#### SGI - Silicon Graphics Image Format
* Read support for the following file types:
* 1, 2, 3 or 4 channel image data
* 8 or 16 bits per pixel component
* Support for the following compression types:
* Uncompressed
* RLE compressed
* Standard metadata support
#### TGA - Truevision TGA Image Format
* Read support for the following file types:
* ColorMapped
* Monochrome
* TrueColor
* Support for the following compression types:
* Uncompressed
* RLE compressed
* Standard metadata support
* Write support
Icon/other formats
#### ICNS - Apple Icon Image
* Read support for the following icon types:
* All known "native" icon types
* Large PNG encoded icons
* Large JPEG 2000 encoded icons (requires JPEG 2000 ImageIO plugin or fallback to `sips` command line tool)
* Write support for PNG encoded icons
#### ICO & CUR - MS Windows Icon and Cursor Formats
* Read support for the following file types:
* ICO Indexed color, 1, 4 and 8 bit
* ICO RGB, 16, 24 and 32 bit
* CUR Indexed color, 1, 4 and 8 bit
* CUR RGB, 16, 24 and 32 bit
* Write support
* *3.1* Note: These formats are now part of the BMP plugin
#### Thumbs.db - MS Windows Thumbs DB
* Read support
Other formats, using 3rd party libraries
#### SVG - Scalable Vector Graphics
* Read-only support using Batik
#### WMF - MS Windows MetaFile
* Limited read-only support using Batik
**Important note on using Batik:** *Please read [The Apache™ XML Graphics Project - Security](http://xmlgraphics.apache.org/security.html), and make sure you use
either version 1.6.1, 1.7.1 or 1.8+.*
**Important note on using Batik:** *Please read [The Apache™ XML Graphics Project - Security](https://xmlgraphics.apache.org/security.html),
and make sure you use version 1.14 or later.*
Note that GIF, PNG and WBMP formats are already supported through the ImageIO API, using the
[JDK standard plugins](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/package-summary.html).
For BMP, JPEG, and TIFF formats the TwelveMonkeys plugins provides extended format support and additional features.
## Basic usage
Most of the time, all you need to do is simply include the plugins in your project and write:
BufferedImage image = ImageIO.read(file);
```java
BufferedImage image = ImageIO.read(file);
```
This will load the first image of the file, entirely into memory.
The basic and simplest form of writing is:
if (!ImageIO.write(image, format, file)) {
// Handle image not written case
}
```java
if (!ImageIO.write(image, format, file)) {
// Handle image not written case
}
```
This will write the entire image into a single file, using the default settings for the given format.
@@ -269,50 +85,44 @@ The plugins are discovered automatically at run time. See the [FAQ](#faq) for mo
If you need more control of read parameters and the reading process, the common idiom for reading is something like:
```java
// Create input stream
ImageInputStream input = ImageIO.createImageInputStream(file);
// Create input stream (in try-with-resource block to avoid leaks)
try (ImageInputStream input = ImageIO.createImageInputStream(file)) {
// Get the reader
Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
if (!readers.hasNext()) {
throw new IllegalArgumentException("No reader for: " + file);
}
ImageReader reader = readers.next();
try {
// Get the reader
Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
reader.setInput(input);
if (!readers.hasNext()) {
throw new IllegalArgumentException("No reader for: " + file);
}
// Optionally, listen for read warnings, progress, etc.
reader.addIIOReadWarningListener(...);
reader.addIIOReadProgressListener(...);
ImageReader reader = readers.next();
ImageReadParam param = reader.getDefaultReadParam();
try {
reader.setInput(input);
// Optionally, control read settings like sub sampling, source region or destination etc.
param.setSourceSubsampling(...);
param.setSourceRegion(...);
param.setDestination(...);
// ...
// Optionally, listen for read warnings, progress, etc.
reader.addIIOReadWarningListener(...);
reader.addIIOReadProgressListener(...);
// Finally read the image, using settings from param
BufferedImage image = reader.read(0, param);
ImageReadParam param = reader.getDefaultReadParam();
// Optionally, control read settings like sub sampling, source region or destination etc.
param.setSourceSubsampling(...);
param.setSourceRegion(...);
param.setDestination(...);
// ...
// Finally read the image, using settings from param
BufferedImage image = reader.read(0, param);
// Optionally, read thumbnails, meta data, etc...
int numThumbs = reader.getNumThumbnails(0);
// ...
}
finally {
// Dispose reader in finally block to avoid memory leaks
reader.dispose();
}
// Optionally, read thumbnails, meta data, etc...
int numThumbs = reader.getNumThumbnails(0);
// ...
}
finally {
// Close stream in finally block to avoid resource leaks
input.close();
// Dispose reader in finally block to avoid memory leaks
reader.dispose();
}
}
```
Query the reader for source image dimensions using `reader.getWidth(n)` and `reader.getHeight(n)` without reading the
@@ -324,86 +134,56 @@ It's also possible to read multiple images from the same file in a loop, using `
If you need more control of write parameters and the writing process, the common idiom for writing is something like:
```java
// Get the writer
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(format);
// Get the writer
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(format);
if (!writers.hasNext()) {
throw new IllegalArgumentException("No writer for: " + format);
}
ImageWriter writer = writers.next();
try {
// Create output stream
ImageOutputStream output = ImageIO.createImageOutputStream(file);
try {
writer.setOutput(output);
// Optionally, listen to progress, warnings, etc.
ImageWriteParam param = writer.getDefaultWriteParam();
// Optionally, control format specific settings of param (requires casting), or
// control generic write settings like sub sampling, source region, output type etc.
// Optionally, provide thumbnails and image/stream metadata
writer.write(..., new IIOImage(..., image, ...), param);
}
finally {
// Close stream in finally block to avoid resource leaks
output.close();
}
}
finally {
// Dispose writer in finally block to avoid memory leaks
writer.dispose();
if (!writers.hasNext()) {
throw new IllegalArgumentException("No writer for: " + format);
}
ImageWriter writer = writers.next();
try {
// Create output stream (in try-with-resource block to avoid leaks)
try (ImageOutputStream output = ImageIO.createImageOutputStream(file)) {
writer.setOutput(output);
// Optionally, listen to progress, warnings, etc.
ImageWriteParam param = writer.getDefaultWriteParam();
// Optionally, control format specific settings of param (requires casting), or
// control generic write settings like sub sampling, source region, output type etc.
// Optionally, provide thumbnails and image/stream metadata
writer.write(..., new IIOImage(..., image, ...), param);
}
}
finally {
// Dispose writer in finally block to avoid memory leaks
writer.dispose();
}
```
For more advanced usage, and information on how to use the ImageIO API, I suggest you read the
[Java Image I/O API Guide](http://docs.oracle.com/javase/7/docs/technotes/guides/imageio/spec/imageio_guideTOC.fm.html)
[Java Image I/O API Guide](https://docs.oracle.com/javase/7/docs/technotes/guides/imageio/spec/imageio_guideTOC.fm.html)
from Oracle.
#### Adobe Clipping Path support
#### Deploying the plugins in a web app
```java
import com.twelvemonkeys.imageio.path.Paths;
Because the `ImageIO` plugin registry (the `IIORegistry`) is "VM global", it doesn't by default work well with
servlet contexts. This is especially evident if you load plugins from the `WEB-INF/lib` or `classes` folder.
Unless you add `ImageIO.scanForPlugins()` somewhere in your code, the plugins might never be available at all.
...
In addition, servlet contexts dynamically loads and unloads classes (using a new class loader per context).
If you restart your application, old classes will by default remain in memory forever (because the next time
`scanForPlugins` is called, it's another `ClassLoader` that scans/loads classes, and thus they will be new instances
in the registry). If a read is attempted using one of the remaining "old" readers, weird exceptions
(like `NullPointerException`s when accessing `static final` initialized fields or `NoClassDefFoundError`s
for uninitialized inner classes) may occur.
try (ImageInputStream stream = ImageIO.createImageInputStream(new File("image_with_path.jpg")) {
BufferedImage image = Paths.readClipped(stream);
To work around both the discovery problem and the resource leak,
it is *strongly recommended* to use the `IIOProviderContextListener` that implements
dynamic loading and unloading of ImageIO plugins for web applications.
```xml
<web-app ...>
...
<listener>
<display-name>ImageIO service provider loader/unloader</display-name>
<listener-class>com.twelvemonkeys.servlet.image.IIOProviderContextListener</listener-class>
</listener>
...
</web-app>
// Do something with the clipped image...
}
```
See [Adobe Clipping Path support on the Wiki](https://github.com/haraldk/TwelveMonkeys/wiki/Photoshop-Clipping-Path-support) for more details and example code.
Loading plugins from `WEB-INF/lib` without the context listener installed is unsupported and will not work correctly.
The context listener has no dependencies to the TwelveMonkeys ImageIO plugins, and may be used with JAI ImageIO
or other ImageIO plugins as well.
Another safe option, is to place the JAR files in the application server's shared or common lib folder.
#### Using the ResampleOp
@@ -411,15 +191,15 @@ The library comes with a resampling (image resizing) operation, that contains ma
to provide excellent results at reasonable speed.
```java
import com.twelvemonkeys.image.ResampleOp;
import com.twelvemonkeys.image.ResampleOp;
...
...
BufferedImage input = ...; // Image to resample
int width, height = ...; // new width/height
BufferedImage input = ...; // Image to resample
int width, height = ...; // new width/height
BufferedImageOp resampler = new ResampleOp(width, height, ResampleOp.FILTER_LANCZOS); // A good default filter, see class documentation for more info
BufferedImage output = resampler.filter(input, null);
BufferedImageOp resampler = new ResampleOp(width, height, ResampleOp.FILTER_LANCZOS); // A good default filter, see class documentation for more info
BufferedImage output = resampler.filter(input, null);
```
#### Using the DiffusionDither
@@ -428,30 +208,30 @@ The library comes with a dithering operation, that can be used to convert `Buffe
Floyd-Steinberg error-diffusion dither.
```java
import com.twelvemonkeys.image.DiffusionDither;
import com.twelvemonkeys.image.DiffusionDither;
...
...
BufferedImage input = ...; // Image to dither
BufferedImage input = ...; // Image to dither
BufferedImageOp ditherer = new DiffusionDither();
BufferedImage output = ditherer.filter(input, null);
BufferedImageOp ditherer = new DiffusionDither();
BufferedImage output = ditherer.filter(input, null);
```
## Building
Download the project (using [Git](http://git-scm.com/downloads)):
Download the project (using [Git](https://git-scm.com/downloads)):
$ git clone git@github.com:haraldk/TwelveMonkeys.git
This should create a folder named `TwelveMonkeys` in your current directory. Change directory to the `TwelveMonkeys`
folder, and issue the command below to build.
Build the project (using [Maven](http://maven.apache.org/download.cgi)):
Build the project (using [Maven](https://maven.apache.org/download.cgi)):
$ mvn package
Currently, the recommended JDK for making a build is Oracle JDK 7.x or 8.x.
Currently, the recommended JDK for making a build is Oracle JDK 8.x.
It's possible to build using OpenJDK, but some tests might fail due to some minor differences between the color management systems used. You will need to either disable the tests in question, or build without tests altogether.
@@ -473,10 +253,10 @@ The ImageIO registry and service lookup mechanism will make sure the plugins are
To verify that the JPEG plugin is installed and used at run-time, you could use the following code:
```java
Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName("JPEG");
while (readers.hasNext()) {
System.out.println("reader: " + readers.next());
}
Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName("JPEG");
while (readers.hasNext()) {
System.out.println("reader: " + readers.next());
}
```
The first line should print:
@@ -488,119 +268,183 @@ The first line should print:
To depend on the JPEG and TIFF plugin using Maven, add the following to your POM:
```xml
...
<dependencies>
...
<dependencies>
...
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-jpeg</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-tiff</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-jpeg</artifactId>
<version>3.7.0</version>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-tiff</artifactId>
<version>3.7.0</version>
</dependency>
<!--
Optional dependency. Needed only if you deploy `ImageIO` plugins as part of a web app.
Make sure you add the `IIOProviderContextListener` to your `web.xml`, see above.
-->
<dependency>
<groupId>com.twelvemonkeys.servlet</groupId>
<artifactId>servlet</artifactId>
<version>3.4.1</version>
</dependency>
</dependencies>
<!--
Optional dependency. Needed only if you deploy ImageIO plugins as part of a web app.
Make sure you add the IIOProviderContextListener to your web.xml, see above.
-->
<dependency>
<groupId>com.twelvemonkeys.servlet</groupId>
<artifactId>servlet</artifactId>
<version>3.7.0</version>
</dependency>
</dependencies>
```
#### Manual dependency example
To depend on the JPEG and TIFF plugin in your IDE or program, add all of the following JARs to your class path:
twelvemonkeys-common-lang-3.4.1.jar
twelvemonkeys-common-io-3.4.1.jar
twelvemonkeys-common-image-3.4.1.jar
twelvemonkeys-imageio-core-3.4.1.jar
twelvemonkeys-imageio-metadata-3.4.1.jar
twelvemonkeys-imageio-jpeg-3.4.1.jar
twelvemonkeys-imageio-tiff-3.4.1.jar
twelvemonkeys-common-lang-3.7.0.jar
twelvemonkeys-common-io-3.7.0.jar
twelvemonkeys-common-image-3.7.0.jar
twelvemonkeys-imageio-core-3.7.0.jar
twelvemonkeys-imageio-metadata-3.7.0.jar
twelvemonkeys-imageio-jpeg-3.7.0.jar
twelvemonkeys-imageio-tiff-3.7.0.jar
#### Deploying the plugins in a web app
Because the `ImageIO` plugin registry (the `IIORegistry`) is "VM global", it doesn't by default work well with
servlet contexts. This is especially evident if you load plugins from the `WEB-INF/lib` or `classes` folder.
Unless you add `ImageIO.scanForPlugins()` somewhere in your code, the plugins might never be available at all.
In addition, servlet contexts dynamically loads and unloads classes (using a new class loader per context).
If you restart your application, old classes will by default remain in memory forever (because the next time
`scanForPlugins` is called, it's another `ClassLoader` that scans/loads classes, and thus they will be new instances
in the registry). If a read is attempted using one of the remaining "old" readers, weird exceptions
(like `NullPointerException`s when accessing `static final` initialized fields or `NoClassDefFoundError`s
for uninitialized inner classes) may occur.
To work around both the discovery problem and the resource leak,
it is *strongly recommended* to use the `IIOProviderContextListener` that implements
dynamic loading and unloading of ImageIO plugins for web applications.
```xml
<web-app ...>
...
<listener>
<display-name>ImageIO service provider loader/unloader</display-name>
<listener-class>com.twelvemonkeys.servlet.image.IIOProviderContextListener</listener-class>
</listener>
...
</web-app>
```
Loading plugins from `WEB-INF/lib` without the context listener installed is unsupported and will not work correctly.
The context listener has no dependencies to the TwelveMonkeys ImageIO plugins, and may be used with JAI ImageIO
or other ImageIO plugins as well.
Another safe option, is to place the JAR files in the application server's shared or common lib folder.
#### Including the plugins in a "fat" JAR
The recommended way to use the plugins, is just to include the JARs as-is in your project, through a Maven dependency or similar.
Re-packaging is not necessary to use the library, and not recommended.
However, if you like to create a "fat"
JAR, or otherwise like to re-package the JARs for some reason, it's important to remember that automatic discovery of
the plugins by ImageIO depends on the
[Service Provider Interface (SPI)](https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html) mechanism.
In short, each JAR contains a special folder, named `META-INF/services` containing one or more files,
typically `javax.imageio.spi.ImageReaderSpi` and `javax.imageio.spi.ImageWriterSpi`.
These files exist *with the same name in every JAR*,
so if you simply unpack everything to a single folder or create a JAR, files will be overwritten and behavior be
unspecified (most likely you will end up with a single plugin being installed).
The solution is to make sure all files with the same name, are merged to a single file,
containing all the SPI information of each type. If using the Maven Shade plugin, you should use the
[ServicesResourceTransformer](https://maven.apache.org/plugins/maven-shade-plugin/examples/resource-transformers.html#ServicesResourceTransformer)
to properly merge these files. You may also want to use the
[ManifestResourceTransforme](https://maven.apache.org/plugins/maven-shade-plugin/examples/resource-transformers.html#ManifestResourceTransformer)
to get the correct vendor name, version info etc.
Other "fat" JAR bundlers will probably have similar mechanisms to merge entries with the same name.
### Links to prebuilt binaries
##### Latest version (3.4.1)
##### Latest version (3.7.0)
Requires Java 7 or later.
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-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-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-lang-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-lang/3.7.0/common-lang-3.7.0.jar)
* [common-io-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-io/3.7.0/common-io-3.7.0.jar)
* [common-image-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-image/3.7.0/common-image-3.7.0.jar)
ImageIO dependencies
* [imageio-core-3.4.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-core/3.4.1/imageio-core-3.4.1.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-core-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-core/3.7.0/imageio-core-3.7.0.jar)
* [imageio-metadata-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-metadata/3.7.0/imageio-metadata-3.7.0.jar)
ImageIO plugins
* [imageio-bmp-3.4.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-bmp/3.4.1/imageio-bmp-3.4.1.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-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-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-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-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-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-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-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-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-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-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.4.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-thumbsdb/3.4.1/imageio-thumbsdb-3.4.1.jar)
* [imageio-bmp-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-bmp/3.7.0/imageio-bmp-3.7.0.jar)
* [imageio-hdr-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-hdr/3.7.0/imageio-hdr-3.7.0.jar)
* [imageio-icns-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-icns/3.7.0/imageio-icns-3.7.0.jar)
* [imageio-iff-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-iff/3.7.0/imageio-iff-3.7.0.jar)
* [imageio-jpeg-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jpeg/3.7.0/imageio-jpeg-3.7.0.jar)
* [imageio-pcx-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pcx/3.7.0/imageio-pcx-3.7.0.jar)
* [imageio-pict-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pict/3.7.0/imageio-pict-3.7.0.jar)
* [imageio-pnm-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pnm/3.7.0/imageio-pnm-3.7.0.jar)
* [imageio-psd-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-psd/3.7.0/imageio-psd-3.7.0.jar)
* [imageio-sgi-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-sgi/3.7.0/imageio-sgi-3.7.0.jar)
* [imageio-tga-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tga/3.7.0/imageio-tga-3.7.0.jar)
* [imageio-thumbsdb-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-thumbsdb/3.7.0/imageio-thumbsdb-3.7.0.jar)
* [imageio-tiff-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tiff/3.7.0/imageio-tiff-3.7.0.jar)
* [imageio-webp-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-webp/3.7.0/imageio-webp-3.7.0.jar)
* [imageio-xwd-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-xwd/3.7.0/imageio-xwd-3.7.0.jar)
ImageIO plugins requiring 3rd party libs
* [imageio-batik-3.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.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-batik/3.7.0/imageio-batik-3.7.0.jar)
Photoshop Path support for ImageIO
* [imageio-clippath-3.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.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-clippath/3.7.0/imageio-clippath-3.7.0.jar)
Servlet support
* [servlet-3.4.1.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/servlet/servlet/3.4.1/servlet-3.4.1.jar)
* [servlet-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/servlet/servlet/3.7.0/servlet-3.7.0.jar)
##### Old version (3.0.x)
Use this version for projects that requires Java 6 or need the JMagick support. *Does not support Java 8 or later*.
Common dependencies
* [common-lang-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-lang/3.0.2/common-lang-3.0.2.jar)
* [common-io-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-io/3.0.2/common-io-3.0.2.jar)
* [common-image-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-image/3.0.2/common-image-3.0.2.jar)
* [common-lang-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-lang/3.0.2/common-lang-3.0.2.jar)
* [common-io-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-io/3.0.2/common-io-3.0.2.jar)
* [common-image-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-image/3.0.2/common-image-3.0.2.jar)
ImageIO dependencies
* [imageio-core-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-core/3.0.2/imageio-core-3.0.2.jar)
* [imageio-metadata-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-metadata/3.0.2/imageio-metadata-3.0.2.jar)
* [imageio-core-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-core/3.0.2/imageio-core-3.0.2.jar)
* [imageio-metadata-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-metadata/3.0.2/imageio-metadata-3.0.2.jar)
ImageIO plugins
* [imageio-jpeg-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jpeg/3.0.2/imageio-jpeg-3.0.2.jar)
* [imageio-tiff-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tiff/3.0.2/imageio-tiff-3.0.2.jar)
* [imageio-psd-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-psd/3.0.2/imageio-psd-3.0.2.jar)
* [imageio-pict-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pict/3.0.2/imageio-pict-3.0.2.jar)
* [imageio-iff-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-iff/3.0.2/imageio-iff-3.0.2.jar)
* [imageio-icns-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-icns/3.0.2/imageio-icns-3.0.2.jar)
* [imageio-ico-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-ico/3.0.2/imageio-ico-3.0.2.jar)
* [imageio-thumbsdb-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-thumbsdb/3.0.2/imageio-thumbsdb-3.0.2.jar)
* [imageio-jpeg-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jpeg/3.0.2/imageio-jpeg-3.0.2.jar)
* [imageio-tiff-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tiff/3.0.2/imageio-tiff-3.0.2.jar)
* [imageio-psd-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-psd/3.0.2/imageio-psd-3.0.2.jar)
* [imageio-pict-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pict/3.0.2/imageio-pict-3.0.2.jar)
* [imageio-iff-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-iff/3.0.2/imageio-iff-3.0.2.jar)
* [imageio-icns-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-icns/3.0.2/imageio-icns-3.0.2.jar)
* [imageio-ico-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-ico/3.0.2/imageio-ico-3.0.2.jar)
* [imageio-thumbsdb-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-thumbsdb/3.0.2/imageio-thumbsdb-3.0.2.jar)
ImageIO plugins requiring 3rd party libs
* [imageio-batik-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-batik/3.0.2/imageio-batik-3.0.2.jar)
* [imageio-jmagick-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jmagick/3.0.2/imageio-jmagick-3.0.2.jar)
* [imageio-batik-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-batik/3.0.2/imageio-batik-3.0.2.jar)
* [imageio-jmagick-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jmagick/3.0.2/imageio-jmagick-3.0.2.jar)
Servlet support
* [servlet-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/servlet/servlet/3.0.2/servlet-3.0.2.jar)
* [servlet-3.0.2.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/servlet/servlet/3.0.2/servlet-3.0.2.jar)
## License
The project is distributed under the OSI approved [BSD license](http://opensource.org/licenses/BSD-3-Clause):
This project is provided under the OSI approved [BSD license](https://opensource.org/licenses/BSD-3-Clause):
Copyright (c) 2008-2018, Harald Kuhr
Copyright (c) 2008-2020, Harald Kuhr
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -653,7 +497,7 @@ a: The TwelveMonkeys ImageIO project contains plug-ins for ImageIO. ImageIO uses
All you have have to do, is to make sure you have the TwelveMonkeys JARs in your classpath.
You can read more about the registry and the lookup mechanism in the [IIORegistry API doc](http://docs.oracle.com/javase/7/docs/api/javax/imageio/spi/IIORegistry.html).
You can read more about the registry and the lookup mechanism in the [IIORegistry API doc](https://docs.oracle.com/javase/7/docs/api/javax/imageio/spi/IIORegistry.html).
The fine print: The TwelveMonkeys service providers for JPEG, BMP and TIFF, overrides the onRegistration method, and
utilizes the pairwise partial ordering mechanism of the `IIOServiceRegistry` to make sure it is installed before
@@ -661,10 +505,15 @@ the Sun/Oracle provided `JPEGImageReader` and `BMPImageReader`, and the Apple pr
respectively. Using the pairwise ordering will not remove any functionality form these implementations, but in most
cases you'll end up using the TwelveMonkeys plug-ins instead.
q: Why is there no support for common formats like GIF or PNG?
a: The short answer is simply that the built-in support in ImageIO for these formats are good enough as-is.
If you are looking for better PNG write performance on Java 7 and 8, see [JDK9 PNG Writer Backport](https://github.com/gredler/jdk9-png-writer-backport).
q: What about JAI? Several of the formats are already supported by JAI.
a: While JAI (and jai-imageio in particular) have support for some of the formats, JAI has some major issues.
a: While JAI (and jai-imageio in particular) have support for some of the same formats, JAI has some major issues.
The most obvious being:
- It's not actively developed. No issues has been fixed for years.
- To get full format support, you need native libs.
+21 -1
View File
@@ -5,7 +5,7 @@
<parent>
<groupId>com.twelvemonkeys</groupId>
<artifactId>twelvemonkeys</artifactId>
<version>3.4.4-SNAPSHOT</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<groupId>com.twelvemonkeys.bom</groupId>
@@ -58,6 +58,16 @@
<artifactId>imageio-bmp</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-cr2</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-dng</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-hdr</artifactId>
@@ -78,6 +88,11 @@
<artifactId>imageio-jpeg</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-nef</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-pcx</artifactId>
@@ -123,6 +138,11 @@
<artifactId>imageio-tiff</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-xwd</artifactId>
<version>${project.version}</version>
</dependency>
<!-- ImageIO 3rd party dependent plugins -->
<dependency>
+5 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.common</groupId>
<artifactId>common</artifactId>
<version>3.4.4-SNAPSHOT</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<artifactId>common-image</artifactId>
<packaging>jar</packaging>
@@ -13,6 +13,10 @@
The TwelveMonkeys Common Image support
</description>
<properties>
<project.jpms.module.name>com.twelvemonkeys.common.image</project.jpms.module.name>
</properties>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
+5 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.common</groupId>
<artifactId>common</artifactId>
<version>3.4.4-SNAPSHOT</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<artifactId>common-io</artifactId>
<packaging>jar</packaging>
@@ -13,6 +13,10 @@
The TwelveMonkeys Common IO support
</description>
<properties>
<project.jpms.module.name>com.twelvemonkeys.common.io</project.jpms.module.name>
</properties>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
@@ -72,7 +72,7 @@ public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
}
@Override
public void write(byte pBytes[], int pOffset, int pLength) {
public void write(byte[] pBytes, int pOffset, int pLength) {
if ((pOffset < 0) || (pOffset > pBytes.length) || (pLength < 0) ||
((pOffset + pLength) > pBytes.length) || ((pOffset + pLength) < 0)) {
throw new IndexOutOfBoundsException();
@@ -98,7 +98,7 @@ public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
private void growIfNeeded(int pNewCount) {
if (pNewCount > buf.length) {
int newSize = Math.max(Math.min(buf.length << 1, buf.length + maxGrowSize), pNewCount);
byte newBuf[] = new byte[newSize];
byte[] newBuf = new byte[newSize];
System.arraycopy(buf, 0, newBuf, 0, count);
buf = newBuf;
}
@@ -113,7 +113,7 @@ public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
// Non-synchronized version of toByteArray
@Override
public byte[] toByteArray() {
byte newBuf[] = new byte[count];
byte[] newBuf = new byte[count];
System.arraycopy(buf, 0, newBuf, 0, count);
return newBuf;
@@ -346,7 +346,7 @@ public final class FileUtil {
/**
* Gets the file (type) extension of the given file.
* A file extension is the part of the filename, after the last occurence
* A file extension is the part of the filename, after the last occurrence
* of a period {@code '.'}.
* If the filename contains no period, {@code null} is returned.
*
@@ -922,7 +922,7 @@ public final class FileUtil {
// A URL should never be able to represent an opaque URI, test anyway
throw new IllegalArgumentException("URI is not hierarchical");
}
if (path.equals("")) {
if (path.isEmpty()) {
throw new IllegalArgumentException("URI path component is empty");
}
@@ -32,13 +32,13 @@ package com.twelvemonkeys.io.enc;
import com.twelvemonkeys.io.FileUtil;
import com.twelvemonkeys.lang.ObjectAbstractTest;
import org.junit.Test;
import java.io.*;
import java.util.Arrays;
import java.util.Random;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.fail;
/**
@@ -73,7 +73,7 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
}
}
private byte[] createData(final int pLength) throws Exception {
private byte[] createData(final int pLength) {
byte[] bytes = new byte[pLength];
RANDOM.nextBytes(bytes);
return bytes;
@@ -82,9 +82,8 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
private void runStreamTest(final int pLength) throws Exception {
byte[] data = createData(pLength);
ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
OutputStream out = new EncoderStream(outBytes, createEncoder(), true);
try {
try (OutputStream out = new EncoderStream(outBytes, createEncoder(), true)) {
// Provoke failure for encoders that doesn't take array offset properly into account
int off = (data.length + 1) / 2;
out.write(data, 0, off);
@@ -92,9 +91,6 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
out.write(data, off, data.length - off);
}
}
finally {
out.close();
}
byte[] encoded = outBytes.toByteArray();
@@ -102,7 +98,7 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
// System.err.println("encoded: " + Arrays.toString(encoded));
byte[] decoded = FileUtil.read(new DecoderStream(new ByteArrayInputStream(encoded), createCompatibleDecoder()));
assertTrue(Arrays.equals(data, decoded));
assertArrayEquals(data, decoded);
InputStream in = new DecoderStream(new ByteArrayInputStream(encoded), createCompatibleDecoder());
outBytes = new ByteArrayOutputStream();
@@ -116,7 +112,7 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
}
decoded = outBytes.toByteArray();
assertTrue(Arrays.equals(data, decoded));
assertArrayEquals(data, decoded);
}
@Test
@@ -129,10 +125,6 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
e.printStackTrace();
fail(e.getMessage() + ": " + i);
}
catch (Exception e) {
e.printStackTrace();
fail(e.getMessage() + ": " + i);
}
}
for (int i = 100; i < 2000; i += 250) {
@@ -143,10 +135,6 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
e.printStackTrace();
fail(e.getMessage() + ": " + i);
}
catch (Exception e) {
e.printStackTrace();
fail(e.getMessage() + ": " + i);
}
}
for (int i = 2000; i < 80000; i += 1000) {
@@ -157,14 +145,8 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
e.printStackTrace();
fail(e.getMessage() + ": " + i);
}
catch (Exception e) {
e.printStackTrace();
fail(e.getMessage() + ": " + i);
}
}
}
// TODO: Test that the transition from byte[] to ByteBuffer didn't introduce bugs when writing to a wrapped array with offset.
// TODO: Test that the transition from byte[] to ByteBuffer didn't introduce bugs when writing to a wrapped array with offset.
}
@@ -33,12 +33,12 @@ package com.twelvemonkeys.io.ole2;
import com.twelvemonkeys.io.InputStreamAbstractTest;
import com.twelvemonkeys.io.LittleEndianDataOutputStream;
import com.twelvemonkeys.io.MemoryCacheSeekableStream;
import com.twelvemonkeys.io.SeekableInputStream;
import org.junit.Test;
import java.io.*;
import java.net.URISyntaxException;
import java.net.URL;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.Arrays;
+5 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.common</groupId>
<artifactId>common</artifactId>
<version>3.4.4-SNAPSHOT</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<artifactId>common-lang</artifactId>
<packaging>jar</packaging>
@@ -13,4 +13,8 @@
The TwelveMonkeys Common Language support
</description>
<properties>
<project.jpms.module.name>com.twelvemonkeys.common.lang</project.jpms.module.name>
</properties>
</project>
@@ -88,7 +88,7 @@ public final class BeanUtil {
while (begIdx < pProperty.length() && begIdx >= 0) {
endIdx = pProperty.indexOf(".", endIdx + 1);
endIdx = pProperty.indexOf('.', endIdx + 1);
if (endIdx > 0) {
subProp = pProperty.substring(begIdx, endIdx);
begIdx = endIdx + 1;
@@ -106,7 +106,7 @@ public final class BeanUtil {
Class[] paramClass = new Class[0];
int begBracket;
if ((begBracket = subProp.indexOf("[")) > 0) {
if ((begBracket = subProp.indexOf('[')) > 0) {
// An error if there is no matching bracket
if (!subProp.endsWith("]")) {
return null;
@@ -330,7 +330,7 @@ abstract class AbstractDecoratedMap<K, V> extends AbstractMap<K, V> implements M
}
/**
* A simple Map.Entry implementaton.
* A simple Map.Entry implementation.
*/
static class BasicEntry<K, V> implements Entry<K, V>, Serializable {
K mKey;
@@ -129,8 +129,7 @@ public class Time {
* @see #toString(String)
*/
public String toString() {
return "" + getMinutes() + ":"
+ (getSeconds() < 10 ? "0" : "") + getSeconds();
return getMinutes() + ":" + (getSeconds() < 10 ? "0" : "") + getSeconds();
}
/**
@@ -44,12 +44,12 @@ import java.util.Vector;
* The format is expressed in a string as follows:
* <DL>
* <DD>m (or any multiple of m's)
* <DT>the minutes part (padded with 0's, if number has less digits than
* <DT>the minutes part (padded with 0's, if number has less digits than
* the number of m's)
* m -&gt; 0,1,...,59,60,61,...
* mm -&gt; 00,01,...,59,60,61,...
* <DD>s or ss
* <DT>the seconds part (padded with 0's, if number has less digits than
* <DT>the seconds part (padded with 0's, if number has less digits than
* the number of s's)
* s -&gt; 0,1,...,59
* ss -&gt; 00,01,...,59
@@ -62,7 +62,7 @@ import java.util.Vector;
* <P>
* Known bugs:
* <P>
* The last character in the formatString is not escaped, while it should be.
* The last character in the formatString is not escaped, while it should be.
* The first character after an escaped character is escaped while is shouldn't
* be.
* <P>
@@ -81,15 +81,15 @@ public class TimeFormat extends Format {
final static String SECOND = "s";
final static String TIME = "S";
final static String ESCAPE = "\\";
/**
* The default time format
* The default time format
*/
private final static TimeFormat DEFAULT_FORMAT = new TimeFormat("m:ss");
protected String formatString = null;
/**
/**
* Main method for testing ONLY
*/
@@ -122,7 +122,7 @@ public class TimeFormat extends Format {
}
else
time = new Time();
System.out.println("Time is \"" + out.format(time) +
"\" according to format \"" + out.formatString + "\"");
}
@@ -147,18 +147,18 @@ public class TimeFormat extends Format {
String previous = null;
String current = null;
int previousCount = 0;
while (tok.hasMoreElements()) {
current = tok.nextToken();
if (previous != null && previous.equals(ESCAPE)) {
// Handle escaping of s, S or m
current = ((current != null) ? current : "")
current = ((current != null) ? current : "")
+ (tok.hasMoreElements() ? tok.nextToken() : "");
previous = null;
previousCount = 0;
}
// Skip over first,
// or if current is the same, increase count, and try again
if (previous == null || previous.equals(current)) {
@@ -173,12 +173,12 @@ public class TimeFormat extends Format {
formatter.add(new SecondsFormatter(previousCount));
else if (previous.equals(TIME))
formatter.add(new SecondsFormatter(-1));
else
else
formatter.add(new TextFormatter(previous));
previousCount = 1;
previous = current;
}
}
@@ -197,7 +197,7 @@ public class TimeFormat extends Format {
// Debug
/*
for (int i = 0; i < formatter.size(); i++) {
System.out.println("Formatter " + formatter.get(i).getClass()
System.out.println("Formatter " + formatter.get(i).getClass()
+ ": length=" + ((TimeFormatter) formatter.get(i)).digits);
}
*/
@@ -206,7 +206,7 @@ public class TimeFormat extends Format {
}
/**
/**
* DUMMY IMPLEMENTATION!!
* Not locale specific.
*/
@@ -259,9 +259,9 @@ public class TimeFormat extends Format {
/** DUMMY IMPLEMENTATION!! */
public Object parseObject(String pStr, ParsePosition pStatus) {
Time t = parse(pStr);
pStatus.setIndex(pStr.length()); // Not 100%
return t;
}
@@ -270,7 +270,7 @@ public class TimeFormat extends Format {
* <p>
* Will bug on some formats. It's safest to always use delimiters between
* the minutes (m) and seconds (s) part.
*
*
*/
public Time parse(String pStr) {
Time time = new Time();
@@ -286,7 +286,7 @@ public class TimeFormat extends Format {
&& (pos + skip < pStr.length()) ; i++) {
// Go to next offset
pos += skip;
if (formatter[i] instanceof MinutesFormatter) {
// Parse MINUTES
if ((i + 1) < formatter.length
@@ -327,9 +327,9 @@ public class TimeFormat extends Format {
else {
// Cannot possibly know how long?
skip = 0;
continue;
continue;
}
// Get seconds
sec = Integer.parseInt(pStr.substring(pos, skip));
// System.out.println("Only seconds: " + sec);
@@ -343,7 +343,7 @@ public class TimeFormat extends Format {
&& formatter[i + 1] instanceof TextFormatter) {
// Skip until next format element
skip = pStr.indexOf(((TextFormatter) formatter[i + 1]).text, pos);
}
else if ((i + 1) >= formatter.length) {
// Skip until end of string
@@ -359,7 +359,7 @@ public class TimeFormat extends Format {
else if (formatter[i] instanceof TextFormatter) {
skip = formatter[i].digits;
}
}
// Set the minutes part if we should
@@ -390,7 +390,7 @@ class SecondsFormatter extends TimeFormatter {
SecondsFormatter(int pDigits) {
digits = pDigits;
}
String format(Time t) {
// Negative number of digits, means all seconds, no padding
if (digits < 0) {
@@ -404,7 +404,7 @@ class SecondsFormatter extends TimeFormatter {
// Else return it with leading 0's
//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
//return StringUtil.formatNumber(t.getMinutes(), digits);
return StringUtil.pad("" + t.getMinutes(), digits, "0", true);
return StringUtil.pad(String.valueOf(t.getMinutes()), digits, "0", true);
}
}
@@ -34,7 +34,7 @@ import java.io.Serializable;
import java.util.*;
/**
* A {@code Map} implementation that removes (exipres) its elements after
* A {@code Map} implementation that removes (expires) its elements after
* a given period. The map is by default backed by a {@link java.util.HashMap},
* or can be instantiated with any given {@code Map} as backing.
* <p>
@@ -67,7 +67,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
protected long expiryTime = 60000L; // 1 minute
//////////////////////
private volatile long nextExpiryTime;
private volatile long nextExpiryTime = Long.MAX_VALUE;
//////////////////////
/**
@@ -178,7 +178,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
* @return {@code true} if this map contains no key-value mappings.
*/
public boolean isEmpty() {
return (size() <= 0);
return size() <= 0;
}
/**
@@ -208,7 +208,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
* @see #containsKey(java.lang.Object)
*/
public V get(Object pKey) {
TimedEntry<K, V> entry = (TimedEntry<K, V>) entries.get(pKey);
TimedEntry entry = (TimedEntry) entries.get(pKey);
if (entry == null) {
return null;
@@ -236,7 +236,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
* {@code null} values.
*/
public V put(K pKey, V pValue) {
TimedEntry<K, V> entry = (TimedEntry<K, V>) entries.get(pKey);
TimedEntry entry = (TimedEntry) entries.get(pKey);
V oldValue;
if (entry == null) {
@@ -272,7 +272,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
* {@code null} values.
*/
public V remove(Object pKey) {
TimedEntry<K, V> entry = (TimedEntry<K, V>) entries.remove(pKey);
TimedEntry entry = (TimedEntry) entries.remove(pKey);
return (entry != null) ? entry.getValue() : null;
}
@@ -284,13 +284,12 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
init();
}
/*protected*/ TimedEntry<K, V> createEntry(K pKey, V pValue) {
return new TimedEntry<K, V>(pKey, pValue);
/*protected*/ TimedEntry createEntry(K pKey, V pValue) {
return new TimedEntry(pKey, pValue);
}
/**
* Removes any expired mappings.
*
*/
protected void removeExpiredEntries() {
// Remove any expired elements
@@ -312,7 +311,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
long next = Long.MAX_VALUE;
nextExpiryTime = next; // Avoid multiple runs...
for (Iterator<Entry<K, V>> iterator = new EntryIterator(); iterator.hasNext();) {
TimedEntry<K, V> entry = (TimedEntry<K, V>) iterator.next();
TimedEntry entry = (TimedEntry) iterator.next();
////
long expires = entry.expires();
if (expires < next) {
@@ -376,7 +375,7 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
while (mNext == null && mIterator.hasNext()) {
Entry<K, Entry<K, V>> entry = mIterator.next();
TimedEntry<K, V> timed = (TimedEntry<K, V>) entry.getValue();
TimedEntry timed = (TimedEntry) entry.getValue();
if (timed.isExpiredBy(mNow)) {
// Remove from map, and continue
@@ -425,19 +424,28 @@ public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements Expi
/**
* Keeps track of timed objects
*/
private class TimedEntry<K, V> extends BasicEntry<K, V> {
private class TimedEntry extends BasicEntry<K, V> {
private long mTimestamp;
TimedEntry(K pKey, V pValue) {
super(pKey, pValue);
mTimestamp = System.currentTimeMillis();
updateTimestamp();
}
public V setValue(V pValue) {
mTimestamp = System.currentTimeMillis();
updateTimestamp();
return super.setValue(pValue);
}
private void updateTimestamp() {
mTimestamp = System.currentTimeMillis();
long expires = expires();
if (expires < nextExpiryTime) {
nextExpiryTime = expires;
}
}
final boolean isExpired() {
return isExpiredBy(System.currentTimeMillis());
}
@@ -622,7 +622,7 @@ public class WildcardStringParser {
buffer.append("\n");
buffer.append(" Format: <state index>: <character> (<last free state>)");
buffer.append("\n");
buffer.append(" Number of strings parsed: " + totalNumberOfStringsParsed);
buffer.append(" Number of strings parsed: ").append(totalNumberOfStringsParsed);
buffer.append("\n");
}
return buffer.toString();
@@ -30,7 +30,7 @@
package com.twelvemonkeys.lang;
import org.junit.Test;
import static org.junit.Assert.*;
import java.awt.*;
import java.sql.Timestamp;
@@ -41,7 +41,7 @@ import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import static org.junit.Assert.*;
import org.junit.Test;
/**
* StringUtilTestCase
@@ -76,24 +76,24 @@ public class StringUtilTest {
assertNull(StringUtil.valueOf(null));
}
@SuppressWarnings("ConstantConditions")
@Test
public void testToUpperCase() {
String str = StringUtil.toUpperCase(TEST_STRING);
assertNotNull(str);
assertEquals(TEST_STRING.toUpperCase(), str);
str = StringUtil.toUpperCase(null);
assertNull(str);
assertNull(StringUtil.toUpperCase(null));
}
@SuppressWarnings("ConstantConditions")
@Test
public void testToLowerCase() {
String str = StringUtil.toLowerCase(TEST_STRING);
assertNotNull(str);
assertEquals(TEST_STRING.toLowerCase(), str);
str = StringUtil.toLowerCase(null);
assertNull(str);
assertNull(StringUtil.toLowerCase(null));
}
@Test
@@ -113,6 +113,7 @@ public class StringUtilTest {
assertFalse(StringUtil.isEmpty(new String[]{WHITESPACE_STRING, TEST_STRING}));
}
@SuppressWarnings("ConstantConditions")
@Test
public void testContains() {
assertTrue(StringUtil.contains(TEST_STRING, TEST_STRING));
@@ -145,6 +146,7 @@ public class StringUtilTest {
assertFalse(StringUtil.containsIgnoreCase(null, null));
}
@SuppressWarnings("ConstantConditions")
@Test
public void testContainsChar() {
for (int i = 0; i < TEST_STRING.length(); i++) {
@@ -466,7 +468,7 @@ public class StringUtilTest {
assertEquals(TEST_STRING, StringUtil.ltrim(TEST_STRING));
assertEquals(TEST_STRING, StringUtil.ltrim(" " + TEST_STRING));
assertEquals(TEST_STRING, StringUtil.ltrim(WHITESPACE_STRING + TEST_STRING));
assertFalse(TEST_STRING.equals(StringUtil.ltrim(TEST_STRING + WHITESPACE_STRING)));
assertNotEquals(TEST_STRING, StringUtil.ltrim(TEST_STRING + WHITESPACE_STRING));
// TODO: Test is not complete
}
@@ -475,7 +477,7 @@ public class StringUtilTest {
assertEquals(TEST_STRING, StringUtil.rtrim(TEST_STRING));
assertEquals(TEST_STRING, StringUtil.rtrim(TEST_STRING + " "));
assertEquals(TEST_STRING, StringUtil.rtrim(TEST_STRING + WHITESPACE_STRING));
assertFalse(TEST_STRING.equals(StringUtil.rtrim(WHITESPACE_STRING + TEST_STRING)));
assertNotEquals(TEST_STRING, StringUtil.rtrim(WHITESPACE_STRING + TEST_STRING));
// TODO: Test is not complete
}
@@ -516,7 +518,7 @@ public class StringUtilTest {
public void testCaptialize() {
assertNull(StringUtil.capitalize(null));
assertEquals(TEST_STRING.toUpperCase(), StringUtil.capitalize(TEST_STRING.toUpperCase()));
assertTrue(StringUtil.capitalize("abc").charAt(0) == 'A');
assertEquals('A', StringUtil.capitalize("abc").charAt(0));
}
@Test
@@ -552,13 +554,13 @@ public class StringUtilTest {
public void testToDateWithFormatString() {
Calendar cal = new GregorianCalendar();
cal.clear();
cal.set(1976, 2, 16); // Month is 0-based
cal.set(1976, Calendar.MARCH, 16); // Month is 0-based
Date date = StringUtil.toDate("16.03.1976", "dd.MM.yyyy");
assertNotNull(date);
assertEquals(cal.getTime(), date);
cal.clear();
cal.set(2004, 4, 13, 23, 51, 3);
cal.set(2004, Calendar.MAY, 13, 23, 51, 3);
date = StringUtil.toDate("2004-5-13 23:51 (03)", "yyyy-MM-dd hh:mm (ss)");
assertNotNull(date);
assertEquals(cal.getTime(), date);
@@ -576,15 +578,15 @@ public class StringUtilTest {
public void testToDateWithFormat() {
Calendar cal = new GregorianCalendar();
cal.clear();
cal.set(1976, 2, 16); // Month is 0-based
cal.set(1976, Calendar.MARCH, 16); // Month is 0-based
Date date = StringUtil.toDate("16.03.1976", new SimpleDateFormat("dd.MM.yyyy"));
assertNotNull(date);
assertEquals(cal.getTime(), date);
cal.clear();
cal.set(2004, 4, 13, 23, 51);
date = StringUtil.toDate("13.5.04 23:51",
DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, new Locale("no", "NO")));
cal.set(2004, Calendar.MAY, 13, 23, 51);
DateFormat format = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, new Locale("no", "NO"));
date = StringUtil.toDate(format.format(cal.getTime()), format);
assertNotNull(date);
assertEquals(cal.getTime(), date);
@@ -601,10 +603,9 @@ public class StringUtilTest {
public void testToTimestamp() {
Calendar cal = new GregorianCalendar();
cal.clear();
cal.set(1976, 2, 16, 21, 28, 4); // Month is 0-based
Date date = StringUtil.toTimestamp("1976-03-16 21:28:04");
cal.set(1976, Calendar.MARCH, 16, 21, 28, 4); // Month is 0-based
Timestamp date = StringUtil.toTimestamp("1976-03-16 21:28:04");
assertNotNull(date);
assertTrue(date instanceof Timestamp);
assertEquals(cal.getTime(), date);
}
@@ -821,7 +822,7 @@ public class StringUtilTest {
assertTrue(StringUtil.isNumber("12345"));
assertTrue(StringUtil.isNumber(TEST_INTEGER.toString()));
assertTrue(StringUtil.isNumber("1234567890123456789012345678901234567890"));
assertTrue(StringUtil.isNumber(String.valueOf(Long.MAX_VALUE) + String.valueOf(Long.MAX_VALUE)));
assertTrue(StringUtil.isNumber(String.valueOf(Long.MAX_VALUE) + Long.MAX_VALUE));
assertFalse(StringUtil.isNumber("abc"));
assertFalse(StringUtil.isNumber(TEST_STRING));
}
@@ -831,7 +832,7 @@ public class StringUtilTest {
assertTrue(StringUtil.isNumber("-12345"));
assertTrue(StringUtil.isNumber('-' + TEST_INTEGER.toString()));
assertTrue(StringUtil.isNumber("-1234567890123456789012345678901234567890"));
assertTrue(StringUtil.isNumber('-' + String.valueOf(Long.MAX_VALUE) + String.valueOf(Long.MAX_VALUE)));
assertTrue(StringUtil.isNumber('-' + String.valueOf(Long.MAX_VALUE) + Long.MAX_VALUE));
assertFalse(StringUtil.isNumber("-abc"));
assertFalse(StringUtil.isNumber('-' + TEST_STRING));
}
@@ -1117,18 +1117,19 @@ public abstract class CollectionAbstractTest extends ObjectAbstractTest {
/**
* Tests {@link Collection#toArray(Object[])}.
*/
@SuppressWarnings({"SuspiciousToArrayCall", "RedundantCast"})
@Test
public void testCollectionToArray2() {
resetEmpty();
Object[] a = new Object[] { new Object(), null, null };
Object[] array = collection.toArray(a);
assertEquals("Given array shouldn't shrink", array, a);
assertEquals("Last element should be set to null", a[0], null);
assertArrayEquals("Given array shouldn't shrink", array, a);
assertNull("Last element should be set to null", a[0]);
verifyAll();
resetFull();
try {
array = collection.toArray(new Void[0]);
collection.toArray(new Void[0]);
fail("toArray(new Void[0]) should raise ArrayStore");
} catch (ArrayStoreException e) {
// expected
@@ -1136,7 +1137,7 @@ public abstract class CollectionAbstractTest extends ObjectAbstractTest {
verifyAll();
try {
array = collection.toArray(null);
collection.toArray((Object[]) null);
fail("toArray(null) should raise NPE");
} catch (NullPointerException e) {
// expected
@@ -1150,13 +1151,13 @@ public abstract class CollectionAbstractTest extends ObjectAbstractTest {
// Figure out if they're all the same class
// 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++) {
classes.add((array[i] == null) ? null : array[i].getClass());
}
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
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);
}
private void assertCorrectListIterator(ListIterator<String> iterator, final Object[] elements) {
private static void assertCorrectListIterator(ListIterator<String> iterator, final Object[] elements) {
assertCorrectListIterator(iterator, elements, false, false);
}
// 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"
assertEquals(-1, iterator.previousIndex());
assertEquals(0, iterator.nextIndex());
@@ -557,7 +557,7 @@ public class TimeoutMapTest extends MapAbstractTest {
// NOTE: Only wait fist time, to avoid slooow tests
synchronized (this) {
try {
wait(60l);
wait(60L);
}
catch (InterruptedException e) {
}
@@ -591,7 +591,7 @@ public class TimeoutMapTest extends MapAbstractTest {
try {
wait(60l);
}
catch (InterruptedException e) {
catch (InterruptedException ignore) {
}
}
}
@@ -651,5 +651,24 @@ public class TimeoutMapTest extends MapAbstractTest {
assertTrue("Wrong entry removed, keySet().iterator() is broken.", !map.containsKey(removedKey));
assertTrue("Wrong entry removed, keySet().iterator() is broken.", map.containsKey(otherKey));
}
@Test
public void testContainsKeyOnEmptyMap() {
// See #600
Map<String, String> timeoutMap = new TimeoutMap<>(30);
assertFalse(timeoutMap.containsKey("xyz"));
timeoutMap.put("xyz", "xyz");
assertTrue(timeoutMap.containsKey("xyz"));
try {
Thread.sleep(50); // Let the item expire
}
catch (InterruptedException ignore) {
}
assertFalse(timeoutMap.containsKey("xyz"));
assertNull(timeoutMap.get("xyz"));
}
}
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys</groupId>
<artifactId>twelvemonkeys</artifactId>
<version>3.4.4-SNAPSHOT</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<groupId>com.twelvemonkeys.common</groupId>
<artifactId>common</artifactId>
@@ -47,7 +47,7 @@
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
</dependencies>
+2 -2
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys</groupId>
<artifactId>twelvemonkeys</artifactId>
<version>3.4.4-SNAPSHOT</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<groupId>com.twelvemonkeys.contrib</groupId>
<artifactId>contrib</artifactId>
@@ -65,7 +65,7 @@
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
</dependencies>
@@ -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 java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
@@ -202,11 +200,10 @@ public final class TIFFUtilities {
}
public static List<TIFFPage> getPages(ImageInputStream imageInput) throws IOException {
ArrayList<TIFFPage> pages = new ArrayList<TIFFPage>();
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++) {
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;
Entry oldJpegData = IFD.getEntryById(TIFF.TAG_JPEG_INTERCHANGE_FORMAT);
Entry oldJpegDataLength = IFD.getEntryById(TIFF.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
long[] jpegByteCounts = null;
long[] jpegOffsets = null;
if (oldJpegData != null && oldJpegData.valueCount() > 0) {
// convert JPEGInterchangeFormat to new-style-JPEG
long[] jpegByteCounts = new long[0];
long[] jpegOffsets = getValueAsLongArray(oldJpegData);
jpegByteCounts = new long[0];
jpegOffsets = getValueAsLongArray(oldJpegData);
if (oldJpegDataLength != null && oldJpegDataLength.valueCount() > 0) {
jpegByteCounts = getValueAsLongArray(oldJpegDataLength);
}
@@ -388,6 +393,20 @@ public final class TIFFUtilities {
newIFD.remove(oldJpegDataLength);
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) {
// multiple bytestrips
// 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]];
newByteCounts[i] = (int) (jpegInterchangeData.length + byteCounts[i]);
stream.readFully(buffer);
if (buffer[0] != 0xff && buffer[1] != 0xda) {
if (buffer[0] != ((byte) 0xff) || buffer[1] != ((byte) 0xda)) {
outputStream.write(sosMarker);
newByteCounts[i] += sosMarker.length;
}
@@ -440,7 +459,58 @@ public final class TIFFUtilities {
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) {
newOffsets = writeData(offsets, byteCounts, outputStream);
@@ -457,12 +527,21 @@ public final class TIFFUtilities {
oldJpegTableQ = IFD.getEntryById(TIFF.TAG_OLD_JPEG_Q_TABLES);
oldJpegTableDC = IFD.getEntryById(TIFF.TAG_OLD_JPEG_DC_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) {
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) {
newIFD.remove(oldJpegTableQ);
}
@@ -474,17 +553,66 @@ public final class TIFFUtilities {
}
}
Entry compressionEntry = IFD.getEntryById(TIFF.TAG_COMPRESSION);
if(compressionEntry != null) {
Number compression = (Number) compressionEntry.getValue();
if (compression.shortValue() == TIFFExtension.COMPRESSION_OLD_JPEG) {
newIFD.remove(compressionEntry);
newIFD.add(new TIFFEntry(TIFF.TAG_COMPRESSION, TIFF.TYPE_SHORT, TIFFExtension.COMPRESSION_JPEG));
}
if (compressionEntry != null && compression == TIFFExtension.COMPRESSION_OLD_JPEG) {
newIFD.remove(compressionEntry);
newIFD.add(new TIFFEntry(TIFF.TAG_COMPRESSION, TIFF.TYPE_SHORT, TIFFExtension.COMPRESSION_JPEG));
}
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 {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
@@ -494,12 +622,16 @@ public final class TIFFUtilities {
long[] off = getValueAsLongArray(qEntry);
byte[] table = new byte[64];
for (int tableId = 0; tableId < off.length; tableId++) {
stream.seek(off[tableId]);
stream.readFully(table);
dos.writeShort(JPEG.DQT);
dos.writeShort(3 + 64);
dos.writeByte(tableId);
dos.write(table);
try {
stream.seek(off[tableId]);
stream.readFully(table);
dos.writeShort(JPEG.DQT);
dos.writeShort(3 + 64);
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) {
long[] off = getValueAsLongArray(dcEntry);
for (int tableId = 0; tableId < off.length; tableId++) {
stream.seek(off[tableId]);
byte[] table = readHUFFTable();
dos.writeShort(JPEG.DHT);
dos.writeShort(3 + table.length);
dos.writeByte(tableId);
dos.write(table);
try {
stream.seek(off[tableId]);
byte[] table = readHUFFTable();
if (table.length > (16 + 17)) {
// to long, table is invalid, just ignoe
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) {
long[] off = getValueAsLongArray(acEntry);
for (int tableId = 0; tableId < off.length; tableId++) {
stream.seek(off[tableId]);
byte[] table = readHUFFTable();
dos.writeShort(JPEG.DHT);
dos.writeShort(3 + table.length);
dos.writeByte(16 | tableId);
dos.write(table);
try {
stream.seek(off[tableId]);
byte[] table = readHUFFTable();
if (table.length > (16 + 256)) {
// to long, table is invalid, just ignoe
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);
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());
}
@@ -540,15 +692,11 @@ public final class TIFFUtilities {
stream.readFully(lengths);
int numCodes = 0;
for (int i = 0; i < lengths.length; i++) {
numCodes += lengths[i];
numCodes += ((int) lengths[i]) & 0xff;
}
byte table[] = new byte[16 + numCodes];
System.arraycopy(lengths, 0, table, 0, 16);
int off = 16;
for (int i = 0; i < lengths.length; i++) {
stream.read(table, off, lengths[i]);
off += lengths[i];
}
stream.readFully(table, 16, numCodes);
return table;
}
@@ -559,7 +707,11 @@ public final class TIFFUtilities {
stream.seek(offsets[i]);
byte[] buffer = new byte[(int) byteCounts[i]];
stream.readFully(buffer);
try {
stream.readFully(buffer);
} catch (EOFException e) {
// invalid strip length
}
outputStream.write(buffer);
}
return newOffsets;
@@ -571,7 +723,7 @@ public final class TIFFUtilities {
if (entry.valueCount() == 1) {
// 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[]) {
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
public void testMergeBogusInterchangeFormatLength() throws IOException {
String[] testFiles = new String[] {
public void testOldStyleJPEGTransform() throws IOException {
String[] testFiles = new String[]{
"/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-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) {
File output = File.createTempFile("imageiotest", ".tif");
ImageOutputStream outputStream = ImageIO.createImageOutputStream(output);
InputStream inputStream1 = getClassLoaderResource(testFile).openStream();
ImageInputStream imageInput1 = ImageIO.createImageInputStream(inputStream1);
InputStream inputStream2 = getClassLoaderResource(testFile).openStream();
ImageInputStream imageInput2 = ImageIO.createImageInputStream(inputStream2);
ArrayList<TIFFUtilities.TIFFPage> pages = new ArrayList<>();
pages.addAll(TIFFUtilities.getPages(imageInput1));
pages.addAll(TIFFUtilities.getPages(imageInput2));
TIFFUtilities.writePages(outputStream, pages);
try {
File output = File.createTempFile("imageiotest", ".tif");
ImageOutputStream outputStream = ImageIO.createImageOutputStream(output);
InputStream inputStream = getClassLoaderResource(testFile).openStream();
ImageInputStream imageInput = ImageIO.createImageInputStream(inputStream);
List<TIFFUtilities.TIFFPage> pages = TIFFUtilities.getPages(imageInput);
TIFFUtilities.writePages(outputStream, pages);
ImageInputStream testOutput = ImageIO.createImageInputStream(output);
ImageReader reader = ImageIO.getImageReaders(testOutput).next();
reader.setInput(testOutput);
int numImages = reader.getNumImages(true);
for (int i = 0; i < numImages; i++) {
reader.read(i);
ImageInputStream testOutput = ImageIO.createImageInputStream(output);
ImageReader reader = ImageIO.getImageReaders(testOutput).next();
reader.setInput(testOutput);
int numImages = reader.getNumImages(true);
for (int i = 0; i < numImages; 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.
+23 -5
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.4.4-SNAPSHOT</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<artifactId>imageio-batik</artifactId>
<name>TwelveMonkeys :: ImageIO :: Batik Plugin</name>
@@ -15,6 +15,27 @@
for more information.]]>
</description>
<properties>
<project.jpms.module.name>com.twelvemonkeys.imageio.batik</project.jpms.module.name>
<batik.version>1.14</batik.version>
</properties>
<build>
<plugins>
<plugin>
<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>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
@@ -24,6 +45,7 @@
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-core</artifactId>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
@@ -86,8 +108,4 @@
</exclusions>
</dependency>
</dependencies>
<properties>
<batik.version>1.9</batik.version>
</properties>
</project>
@@ -33,9 +33,11 @@ package com.twelvemonkeys.imageio.plugins.svg;
import com.twelvemonkeys.image.ImageUtil;
import com.twelvemonkeys.imageio.ImageReaderBase;
import com.twelvemonkeys.imageio.util.IIOUtil;
import com.twelvemonkeys.lang.StringUtil;
import org.apache.batik.anim.dom.SVGDOMImplementation;
import org.apache.batik.anim.dom.SVGOMDocument;
import org.apache.batik.bridge.*;
import org.apache.batik.css.parser.CSSLexicalUnit;
import org.apache.batik.dom.util.DOMUtilities;
import org.apache.batik.ext.awt.image.GraphicsUtil;
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.util.ParsedURL;
import org.apache.batik.util.SVGConstants;
import org.apache.batik.xml.LexicalUnits;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
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>
*/
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 boolean allowExternalResources = DEFAULT_ALLOW_EXTERNAL_RESOURCES;
/**
* Creates an {@code SVGImageReader}.
@@ -113,6 +121,9 @@ public class SVGImageReader extends ImageReaderBase {
if (pParam instanceof SVGReadParam) {
SVGReadParam svgParam = (SVGReadParam) pParam;
// set the external-resource-resolution preference
allowExternalResources = svgParam.isAllowExternalResources();
// Get the base URI
// This must be done before converting the params to hints
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
Dimension2D docSize = ctx.getDocumentSize();
if (docSize != null) {
defaultWidth = (float) docSize.getWidth();
defaultHeight = (float) docSize.getHeight();
UnitProcessor.Context uctx
= UnitProcessor.createContext(ctx, rootElement);
String widthStr = rootElement.getAttributeNS(null, SVGConstants.SVG_WIDTH_ATTRIBUTE);
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 {
defaultWidth = 200;
defaultHeight = 200;
if(!StringUtil.isEmpty(heightStr)){
defaultHeight = UnitProcessor.svgToUserSpace(heightStr, SVGConstants.SVG_HEIGHT_ATTRIBUTE, UnitProcessor.VERTICAL_LENGTH, uctx);
}
SVGSVGElement rootElement = svgDoc.getRootElement();
String viewBoxStr = rootElement.getAttributeNS
(null, SVGConstants.SVG_VIEW_BOX_ATTRIBUTE);
if (viewBoxStr.length() != 0) {
float[] rect = ViewBox.parseViewBoxAttribute(rootElement, viewBoxStr, null);
defaultWidth = rect[2];
defaultHeight = rect[3];
boolean hasWidth = defaultWidth > 0.0;
boolean hasHeight = defaultHeight > 0.0;
if (!hasWidth || !hasHeight) {
String viewBoxStr = rootElement.getAttributeNS
(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
@@ -608,6 +649,14 @@ public class SVGImageReader extends ImageReaderBase {
public void displayMessage(String message) {
processWarningOccurred(message.replaceAll("[\\r\\n]+", " "));
}
@Override
public ExternalResourceSecurity getExternalResourceSecurity(ParsedURL resourceURL, ParsedURL docURL) {
if (allowExternalResources) {
return super.getExternalResourceSecurity(resourceURL, docURL);
}
return new EmbededExternalResourceSecurity(resourceURL);
}
}
}
}
@@ -41,6 +41,11 @@ import java.awt.*;
public class SVGReadParam extends ImageReadParam {
private Paint background;
private String baseURI;
private boolean allowExternalResources = SVGImageReader.DEFAULT_ALLOW_EXTERNAL_RESOURCES;
public SVGReadParam() {
super();
}
public Paint getBackgroundColor() {
return background;
@@ -58,6 +63,14 @@ public class SVGReadParam extends ImageReadParam {
baseURI = pBaseURI;
}
public void setAllowExternalResources(boolean allow) {
allowExternalResources = allow;
}
public boolean isAllowExternalResources() {
return allowExternalResources;
}
@Override
public boolean canSetSourceRenderSize() {
return true;
@@ -31,6 +31,7 @@
package com.twelvemonkeys.imageio.plugins.svg;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
import org.junit.Ignore;
import org.junit.Test;
@@ -44,12 +45,10 @@ import javax.imageio.stream.ImageInputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.ImagingOpException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.Buffer;
import java.util.Arrays;
import java.util.Collections;
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$
*/
public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader> {
private SVGImageReaderSpi provider = new SVGImageReaderSpi();
@Override
protected ImageReaderSpi createProvider() {
return new SVGImageReaderSpi();
}
protected List<TestData> getTestData() {
return Arrays.asList(
new TestData(getClassLoaderResource("/svg/batikLogo.svg"), new Dimension(450, 500)),
new TestData(getClassLoaderResource("/svg/red-square.svg"), new Dimension(100, 100)),
new TestData(getClassLoaderResource("/svg/blue-square.svg"), new Dimension(100, 100)),
new TestData(getClassLoaderResource("/svg/Android_robot.svg"), new Dimension(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() {
return Collections.singletonList("svg");
}
@@ -107,8 +101,6 @@ public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader>
public void testScaleViewBox() throws IOException {
URL svgUrl = getClassLoaderResource("/svg/quadrants.svg");
File tmpDir = new File(System.getProperty("java.io.tmpdir"));
SVGImageReader reader = createReader();
SVGReadParam param = new SVGReadParam();
@@ -156,11 +148,11 @@ public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader>
@Test
@Override
public void testReadWithSizeParam() {
public void testReadWithSizeParam() throws IOException {
try {
super.testReadWithSizeParam();
}
catch (AssertionError failure) {
catch (AssertionError | IOException failure) {
Throwable cause = failure;
while (cause.getCause() != null) {
@@ -224,6 +216,7 @@ public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader>
reader.addIIOReadWarningListener(listener);
SVGReadParam param = reader.getDefaultReadParam();
param.setAllowExternalResources(true);
param.setBaseURI(resource.toURI().toASCIIString());
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,
// will cause the document to be parsed without a base URI.
// 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");
SVGImageReader reader = createReader();
@@ -282,18 +277,17 @@ public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader>
public void testEmbeddedNoBaseURI() throws IOException {
// With no base URI, we will throw an exception, about the missing embedded resource
URL resource = getClassLoaderResource("/svg/barChart.svg");
SVGImageReader reader = createReader();
TestData data = new TestData(resource, (Dimension) null);
try (ImageInputStream stream = data.getInputStream()) {
reader.setInput(stream);
BufferedImage image = reader.read(0);
SVGReadParam params = reader.getDefaultReadParam();
params.setAllowExternalResources(true);
reader.read(0, params);
assertNotNull(image);
assertEquals(450, image.getWidth());
assertEquals(500, image.getHeight());
fail("reader.read should've thrown an exception, but didn't");
}
catch (IIOException allowed) {
assertTrue(allowed.getMessage().contains("batikLogo.svg")); // The embedded resource we don't find
@@ -302,4 +296,47 @@ public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader>
reader.dispose();
}
}
}
@Test
public void testReadEmbeddedWithDisallowExternalResources() throws IOException{
// File using "data:" URLs for embedded resources
URL resource = getClassLoaderResource("/svg/embedded-data-resource.svg");
SVGImageReader reader = createReader();
TestData data = new TestData(resource, (Dimension) null);
try (ImageInputStream stream = data.getInputStream()) {
reader.setInput(stream);
SVGReadParam param = reader.getDefaultReadParam();
param.setAllowExternalResources(false);
reader.read(0, param);
}
finally {
reader.dispose();
}
}
@Test(expected = SecurityException.class)
public void testDisallowedExternalResources() throws URISyntaxException, IOException {
// system-property set to true in surefire-plugin-settings in the pom
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();
}
}
}
@@ -31,6 +31,7 @@
package com.twelvemonkeys.imageio.plugins.wmf;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
import org.junit.Ignore;
import org.junit.Test;
@@ -49,7 +50,10 @@ import java.util.List;
* @version $Id: WMFImageReaderTest.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$
*/
public class WMFImageReaderTest extends ImageReaderAbstractTest<WMFImageReader> {
private WMFImageReaderSpi provider = new WMFImageReaderSpi();
@Override
protected ImageReaderSpi createProvider() {
return new WMFImageReaderSpi();
}
protected List<TestData> getTestData() {
return Collections.singletonList(
@@ -57,27 +61,17 @@ public class WMFImageReaderTest extends ImageReaderAbstractTest<WMFImageReader>
);
}
protected ImageReaderSpi createProvider() {
return provider;
}
@Override
protected WMFImageReader createReader() {
return new WMFImageReader(createProvider());
}
protected Class<WMFImageReader> getReaderClass() {
return WMFImageReader.class;
}
protected List<String> getFormatNames() {
return Collections.singletonList("wmf");
}
@Override
protected List<String> getSuffixes() {
return Arrays.asList("wmf", "emf");
}
@Override
protected List<String> getMIMETypes() {
return Arrays.asList("image/x-wmf", "application/x-msmetafile");
}
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 92 KiB

@@ -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

+6 -1
View File
@@ -4,12 +4,16 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.4.4-SNAPSHOT</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<artifactId>imageio-bmp</artifactId>
<name>TwelveMonkeys :: ImageIO :: BMP plugin</name>
<description>ImageIO plugin for Microsoft Device Independent Bitmap (BMP/DIB) format.</description>
<properties>
<project.jpms.module.name>com.twelvemonkeys.imageio.bmp</project.jpms.module.name>
</properties>
<dependencies>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
@@ -19,6 +23,7 @@
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-core</artifactId>
<type>test-jar</type>
<scope>test</scope>
</dependency>
</dependencies>
</project>
@@ -4,26 +4,28 @@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* * 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;
@@ -4,30 +4,54 @@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* * 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 java.awt.*;
import java.awt.color.ColorSpace;
import java.awt.image.*;
import java.io.DataInput;
import java.io.File;
import java.io.IOException;
import java.nio.ByteOrder;
import java.util.Collections;
import java.util.Iterator;
import javax.imageio.IIOException;
import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.event.IIOReadUpdateListener;
import javax.imageio.event.IIOReadWarningListener;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataFormatImpl;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import com.twelvemonkeys.imageio.ImageReaderBase;
import com.twelvemonkeys.imageio.stream.SubImageInputStream;
import com.twelvemonkeys.imageio.util.IIOUtil;
@@ -37,30 +61,13 @@ import com.twelvemonkeys.io.LittleEndianDataInputStream;
import com.twelvemonkeys.io.enc.DecoderStream;
import com.twelvemonkeys.xml.XMLSerializer;
import javax.imageio.*;
import javax.imageio.event.IIOReadUpdateListener;
import javax.imageio.event.IIOReadWarningListener;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataFormatImpl;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
import java.awt.color.ColorSpace;
import java.awt.image.*;
import java.io.DataInput;
import java.io.File;
import java.io.IOException;
import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.Iterator;
/**
* ImageReader for Microsoft Windows Bitmap (BMP) format.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: CURImageReader.java,v 1.0 Apr 20, 2009 11:54:28 AM haraldk Exp$
* @see com.twelvemonkeys.imageio.plugins.bmp.ICOImageReader
* @version $Id: BMPImageReader.java,v 1.0 Apr 20, 2009 11:54:28 AM haraldk Exp$
* @see ICOImageReader
*/
public final class BMPImageReader extends ImageReaderBase {
private long pixelOffset;
@@ -123,6 +130,10 @@ public final class BMPImageReader extends ImageReaderBase {
// Read DIB header
header = DIBHeader.read(imageInput);
if (pixelOffset < header.size + DIB.BMP_FILE_HEADER_SIZE) {
throw new IIOException("Invalid pixel offset: " + pixelOffset);
}
}
}
@@ -194,7 +205,7 @@ public final class BMPImageReader extends ImageReaderBase {
checkBounds(pImageIndex);
// TODO: Better implementation, include INT_RGB types for 3BYTE_BGR and 4BYTE_ABGR for INT_ARGB
return Arrays.asList(getRawImageType(pImageIndex)).iterator();
return Collections.singletonList(getRawImageType(pImageIndex)).iterator();
}
@Override
@@ -205,50 +216,55 @@ public final class BMPImageReader extends ImageReaderBase {
throw new IIOException("Multiple planes not supported");
}
switch (header.getBitCount()) {
case 1:
case 2:
case 4:
case 8:
return ImageTypeSpecifiers.createFromIndexColorModel(readColorMap());
try {
switch (header.getBitCount()) {
case 1:
case 2:
case 4:
case 8:
return ImageTypeSpecifiers.createFromIndexColorModel(readColorMap());
case 16:
if (header.hasMasks()) {
return ImageTypeSpecifiers.createPacked(
ColorSpace.getInstance(ColorSpace.CS_sRGB),
header.masks[0], header.masks[1], header.masks[2], header.masks[3],
DataBuffer.TYPE_USHORT, false
);
}
case 16:
if (header.hasMasks()) {
return ImageTypeSpecifiers.createPacked(
ColorSpace.getInstance(ColorSpace.CS_sRGB),
header.masks[0], header.masks[1], header.masks[2], header.masks[3],
DataBuffer.TYPE_USHORT, false
);
}
// Default if no mask is 555
return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_USHORT_555_RGB);
// Default if no mask is 555
return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_USHORT_555_RGB);
case 24:
if (header.getCompression() != DIB.COMPRESSION_RGB) {
throw new IIOException("Unsupported compression for RGB: " + header.getCompression());
}
case 24:
if (header.getCompression() != DIB.COMPRESSION_RGB) {
throw new IIOException("Unsupported compression for RGB: " + header.getCompression());
}
return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR);
return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR);
case 32:
if (header.hasMasks()) {
return ImageTypeSpecifiers.createPacked(
ColorSpace.getInstance(ColorSpace.CS_sRGB),
header.masks[0], header.masks[1], header.masks[2], header.masks[3],
DataBuffer.TYPE_INT, false
);
}
case 32:
if (header.hasMasks()) {
return ImageTypeSpecifiers.createPacked(
ColorSpace.getInstance(ColorSpace.CS_sRGB),
header.masks[0], header.masks[1], header.masks[2], header.masks[3],
DataBuffer.TYPE_INT, false
);
}
// Default if no mask
return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB);
// Default if no mask
return ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB);
case 0:
if (header.getCompression() == DIB.COMPRESSION_JPEG || header.getCompression() == DIB.COMPRESSION_PNG) {
return initReaderDelegate(header.getCompression()).getRawImageType(0);
}
default:
throw new IIOException("Unsupported bit count: " + header.getBitCount());
case 0:
if (header.getCompression() == DIB.COMPRESSION_JPEG || header.getCompression() == DIB.COMPRESSION_PNG) {
return initReaderDelegate(header.getCompression()).getRawImageType(0);
}
default:
throw new IIOException("Unsupported bit count: " + header.getBitCount());
}
}
catch (IllegalArgumentException e) {
throw new IIOException(e.getMessage(), e);
}
}
@@ -394,6 +410,13 @@ public final class BMPImageReader extends ImageReaderBase {
private ImageReader initReaderDelegate(int compression) throws IOException {
ImageReader reader = getImageReaderDelegate(compression);
reader.reset();
// Install listener
ListenerDelegator listenerDelegator = new ListenerDelegator();
reader.addIIOReadWarningListener(listenerDelegator);
reader.addIIOReadProgressListener(listenerDelegator);
reader.addIIOReadUpdateListener(listenerDelegator);
imageInput.seek(pixelOffset);
reader.setInput(new SubImageInputStream(imageInput, header.getImageSize()));
@@ -434,12 +457,6 @@ public final class BMPImageReader extends ImageReaderBase {
ImageReader reader = readers.next();
// Install listener
ListenerDelegator listenerDelegator = new ListenerDelegator();
reader.addIIOReadWarningListener(listenerDelegator);
reader.addIIOReadProgressListener(listenerDelegator);
reader.addIIOReadUpdateListener(listenerDelegator);
// Cache for later use
switch (compression) {
case DIB.COMPRESSION_JPEG:
@@ -612,12 +629,13 @@ public final class BMPImageReader extends ImageReaderBase {
}
// 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
return new BMPMetadata(header, colors);
}
public static void main(String[] args) throws IOException {
@SuppressWarnings("ConstantConditions")
public static void main(String[] args) {
BMPImageReaderSpi provider = new BMPImageReaderSpi();
BMPImageReader reader = new BMPImageReader(provider);
@@ -670,7 +688,7 @@ public final class BMPImageReader extends ImageReaderBase {
}
}
@SuppressWarnings({"unchecked", "UnusedDeclaration"})
@SuppressWarnings({ "unchecked", "UnusedDeclaration", "SameParameterValue" })
static <T extends Throwable> void throwAs(final Class<T> pType, final Throwable pThrowable) throws T {
throw (T) pThrowable;
}
@@ -4,26 +4,28 @@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* * 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;
@@ -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));
}
}
}
@@ -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";
}
}
@@ -4,26 +4,28 @@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* * 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;
@@ -168,87 +170,90 @@ final class BMPMetadata extends AbstractMetadata {
protected IIOMetadataNode getStandardCompressionNode() {
IIOMetadataNode compression = new IIOMetadataNode("Compression");
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;
// 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
protected IIOMetadataNode getStandardDataNode() {
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");
switch (header.getBitCount()) {
// TODO: case 0: determined by embedded format (PNG/JPEG)
case 1:
case 2:
case 4:
case 8:
bitsPerSample.setAttribute("value", createListValue(1, Integer.toString(header.getBitCount())));
break;
case 16:
// TODO: Consult masks here!
bitsPerSample.setAttribute("value", createListValue(4, Integer.toString(4)));
// Default is 555
bitsPerSample.setAttribute("value", header.hasMasks()
? createBitsPerSampleForBitMasks()
: createListValue(3, Integer.toString(5)));
break;
case 24:
bitsPerSample.setAttribute("value", createListValue(3, Integer.toString(8)));
break;
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;
}
node.appendChild(bitsPerSample);
// TODO: Do we need MSB?
// IIOMetadataNode sampleMSB = new IIOMetadataNode("SampleMSB");
// sampleMSB.setAttribute("value", createListValue(header.getChannels(), "0"));
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) {
StringBuilder buffer = new StringBuilder();
@@ -265,28 +270,31 @@ final class BMPMetadata extends AbstractMetadata {
@Override
protected IIOMetadataNode getStandardDimensionNode() {
if (header.xPixelsPerMeter > 0 || header.yPixelsPerMeter > 0) {
IIOMetadataNode dimension = new IIOMetadataNode("Dimension");
IIOMetadataNode dimension = new IIOMetadataNode("Dimension");
addChildNode(dimension, "PixelAspectRatio", null);
addChildNode(dimension, "HorizontalPhysicalPixelSpacing", null);
addChildNode(dimension, "VerticalPhysicalPixelSpacing", null);
if (header.xPixelsPerMeter > 0 && header.yPixelsPerMeter > 0) {
float ratio = header.xPixelsPerMeter / (float) header.yPixelsPerMeter;
addChildNode(dimension, "PixelAspectRatio", null)
.setAttribute("value", String.valueOf(ratio));
// IIOMetadataNode imageOrientation = new IIOMetadataNode("ImageOrientation");
//
// if (header.topDown) {
// imageOrientation.setAttribute("value", "FlipH");
// }
// else {
// imageOrientation.setAttribute("value", "Normal");
// }
//
// dimension.appendChild(imageOrientation);
addChildNode(dimension, "HorizontalPixelSize", null)
.setAttribute("value", String.valueOf(1f / header.xPixelsPerMeter * 1000));
addChildNode(dimension, "VerticalPixelSize", null)
.setAttribute("value", String.valueOf(1f / header.yPixelsPerMeter * 1000));
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
@@ -297,16 +305,15 @@ final class BMPMetadata extends AbstractMetadata {
@Override
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");
//
// IIOMetadataNode alpha = new IIOMetadataNode("Alpha");
//
// // TODO: Consult masks
// alpha.setAttribute("value", header.getBitCount() == 32 ? "nonpremultiplied" : "none");
// transparency.appendChild(alpha);
//
// return transparency;
return transparency;
}
return null;
}
}
@@ -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;
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$
*/
final class BMPProviderInfo extends ReaderWriterProviderInfo {
protected BMPProviderInfo() {
BMPProviderInfo() {
super(
BMPProviderInfo.class,
new String[] {"bmp", "BMP"},
new String[] {"bmp", "rle"},
new String[] {
"image/bmp",
"image/x-bmp"
// "image/vnd.microsoft.bitmap", // TODO: Official IANA MIME
"image/x-bmp",
"image/vnd.microsoft.bitmap"
},
"com.twelvemonkeys.imageio.plugins.bmp.BMPImageReader",
new String[] {"com.twelvemonkeys.imageio.plugins.bmp.BMPImageReaderSpi"},
"com.sun.imageio.plugins.bmp.BMPImageWriter",
new String[]{"com.sun.imageio.plugins.bmp.BMPImageWriterSpi"}, // We support the same native metadata format
false, null, null, null, null,
true, BMPMetadata.nativeMetadataFormatName, "com.sun.imageio.plugins.bmp.BMPMetadataFormat", null, null
"com.twelvemonkeys.imageio.plugins.bmp.BMPImageWriter",
new String[] {"com.twelvemonkeys.imageio.plugins.bmp.BMPImageWriterSpi"},
false, null, null,
null, null,
true, BMPMetadata.nativeMetadataFormatName, "com.sun.imageio.plugins.bmp.BMPMetadataFormat",
null, null
);
}
}
@@ -4,33 +4,35 @@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* * 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.lang.Validate;
import static com.twelvemonkeys.lang.Validate.notNull;
import java.awt.image.BufferedImage;
import java.io.IOException;
/**
* Describes a bitmap structure.
@@ -46,14 +48,11 @@ abstract class BitmapDescriptor {
protected BitmapMask mask;
public BitmapDescriptor(final DirectoryEntry pEntry, final DIBHeader pHeader) {
Validate.notNull(pEntry, "entry");
Validate.notNull(pHeader, "header");
entry = pEntry;
header = pHeader;
entry = notNull(pEntry, "entry");;
header = notNull(pHeader, "header");
}
abstract public BufferedImage getImage();
abstract public BufferedImage getImage() throws IOException;
public final int getWidth() {
return entry.getWidth();
@@ -4,28 +4,29 @@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* * 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 java.awt.image.BufferedImage;
@@ -162,6 +163,7 @@ class BitmapIndexed extends BitmapDescriptor {
return transparent;
}
@Override
public BufferedImage getImage() {
if (image == null) {
image = createImageIndexed();
@@ -4,26 +4,28 @@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* * 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;
@@ -4,26 +4,28 @@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* * 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;
@@ -44,6 +46,7 @@ class BitmapRGB extends BitmapDescriptor {
super(pEntry, pHeader);
}
@Override
public BufferedImage getImage() {
// Test is mask != null rather than hasMask(), as 32 bit (w/alpha)
// might still have bitmask, but we don't read or use it.
@@ -4,31 +4,36 @@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* * 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 java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.IIOException;
/**
* Represents bitmap structures we can't read.
@@ -40,13 +45,14 @@ import java.awt.image.BufferedImage;
class BitmapUnsupported extends BitmapDescriptor {
private String message;
public BitmapUnsupported(final DirectoryEntry pEntry, final String pMessage) {
super(pEntry, null);
public BitmapUnsupported(final DirectoryEntry pEntry, DIBHeader header, final String pMessage) {
super(pEntry, header);
message = pMessage;
}
public BufferedImage getImage() {
throw new IllegalStateException(message);
@Override
public BufferedImage getImage() throws IOException {
throw new IIOException(message);
}
}
@@ -4,26 +4,28 @@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* * 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;
@@ -4,26 +4,28 @@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* * 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;
@@ -4,26 +4,28 @@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* * 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;
@@ -38,7 +40,7 @@ import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
* @version $Id: CURProviderInfo.java,v 1.0 20/03/15 harald.kuhr Exp$
*/
final class CURProviderInfo extends ReaderWriterProviderInfo {
protected CURProviderInfo() {
CURProviderInfo() {
super(
CURProviderInfo.class,
new String[]{"cur", "CUR"},
@@ -4,26 +4,28 @@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* * 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;
@@ -30,11 +30,12 @@
package com.twelvemonkeys.imageio.plugins.bmp;
import javax.imageio.IIOException;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import javax.imageio.IIOException;
/**
* Represents the DIB (Device Independent Bitmap) Information header structure.
*
@@ -213,7 +214,7 @@ abstract class DIBHeader {
// NOTE: Unlike all other headers, width and height are unsigned SHORT values (16 bit)!
width = pStream.readUnsignedShort();
height = pStream.readUnsignedShort();
height = pStream.readShort();
if (height < 0) {
height = -height;
@@ -240,6 +241,7 @@ abstract class DIBHeader {
* @see <a href="http://www.fileformat.info/format/os2bmp/egff.htm">OS/2 Bitmap File Format Summary</a>
*/
static final class BitmapCoreHeaderV2 extends DIBHeader {
@SuppressWarnings("unused")
protected void read(final int pSize, final DataInput pStream) throws IOException {
if (pSize != DIB.OS2_V2_HEADER_SIZE && pSize != DIB.OS2_V2_HEADER_16_SIZE) {
throw new IIOException(String.format("Size: %s !=: %s", pSize, DIB.OS2_V2_HEADER_SIZE));
@@ -339,9 +341,34 @@ 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() {
// This is to be compatible with the native metadata of the original com.sun....BMPMetadata
return compression == DIB.COMPRESSION_BITFIELDS ? "BMP v. 3.x NT" : "BMP v. 3.x";
return size > DIB.BITMAP_INFO_HEADER_SIZE
? "BMP V2/V3 INFO"
: compression == DIB.COMPRESSION_BITFIELDS || compression == DIB.COMPRESSION_ALPHA_BITFIELDS
? "BMP v. 3.x NT"
: "BMP v. 3.x";
}
}
@@ -4,40 +4,32 @@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* * 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.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.color.ColorSpace;
import java.awt.event.WindowAdapter;
@@ -45,10 +37,27 @@ import java.awt.event.WindowEvent;
import java.awt.image.*;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.*;
import java.util.Map;
import java.util.WeakHashMap;
import javax.imageio.IIOException;
import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import javax.swing.*;
import com.twelvemonkeys.image.ImageUtil;
import com.twelvemonkeys.imageio.ImageReaderBase;
import com.twelvemonkeys.imageio.stream.SubImageInputStream;
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
import com.twelvemonkeys.util.WeakWeakMap;
/**
* ImageReader for Microsoft Windows ICO (icon) format.
@@ -83,7 +92,7 @@ abstract class DIBImageReader extends ImageReaderBase {
protected void resetMembers() {
directory = null;
headers.clear();
descriptors.clear();
@@ -233,8 +242,9 @@ abstract class DIBImageReader extends ImageReaderBase {
ImageReader pngReader = getPNGReader();
imageInput.seek(pEntry.getOffset());
InputStream inputStream = IIOUtil.createStreamAdapter(imageInput, pEntry.getSize());
ImageInputStream stream = ImageIO.createImageInputStream(inputStream);
// InputStream inputStream = IIOUtil.createStreamAdapter(imageInput, pEntry.getSize());
// ImageInputStream stream = ImageIO.createImageInputStream(inputStream);
ImageInputStream stream = new SubImageInputStream(imageInput, pEntry.getSize());
// NOTE: Will throw IOException on later reads if input is not PNG
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
if (header.getCompression() != 0) {
descriptor = new BitmapUnsupported(pEntry, String.format("Unsupported compression: %d", header.getCompression()));
if (header.getCompression() != DIB.COMPRESSION_RGB) {
descriptor = new BitmapUnsupported(pEntry, header, String.format("Unsupported compression: %d", header.getCompression()));
}
else {
int bitCount = header.getBitCount();
@@ -313,7 +323,7 @@ abstract class DIBImageReader extends ImageReaderBase {
break;
default:
descriptor = new BitmapUnsupported(pEntry, String.format("Unsupported bit count %d", bitCount));
descriptor = new BitmapUnsupported(pEntry, header, String.format("Unsupported bit count %d", bitCount));
}
}
@@ -353,7 +363,7 @@ abstract class DIBImageReader extends ImageReaderBase {
}
private void readBitmapIndexed1(final BitmapIndexed pBitmap, final boolean pAsMask) throws IOException {
int width = adjustToPadding(pBitmap.getWidth() >> 3);
int width = adjustToPadding((pBitmap.getWidth() + 7) >> 3);
byte[] row = new byte[width];
for (int y = 0; y < pBitmap.getHeight(); y++) {
@@ -387,7 +397,7 @@ abstract class DIBImageReader extends ImageReaderBase {
}
private void readBitmapIndexed4(final BitmapIndexed pBitmap) throws IOException {
int width = adjustToPadding(pBitmap.getWidth() >> 1);
int width = adjustToPadding((pBitmap.getWidth() + 1) >> 1);
byte[] row = new byte[width];
for (int y = 0; y < pBitmap.getHeight(); y++) {
@@ -455,13 +465,12 @@ abstract class DIBImageReader extends ImageReaderBase {
}
private void readBitmap16(final BitmapDescriptor pBitmap) throws IOException {
// TODO: No idea if this actually works..
short[] pixels = new short[pBitmap.getWidth() * pBitmap.getHeight()];
// TODO: Support TYPE_USHORT_565 and the RGB 444/ARGB 4444 layouts
// Will create TYPE_USHORT_555
DirectColorModel cm = new DirectColorModel(16, 0x7C00, 0x03E0, 0x001F);
DataBuffer buffer = new DataBufferShort(pixels, pixels.length);
DataBuffer buffer = new DataBufferUShort(pixels, pixels.length);
WritableRaster raster = Raster.createPackedRaster(
buffer, pBitmap.getWidth(), pBitmap.getHeight(), pBitmap.getWidth(), cm.getMasks(), null
);
@@ -549,7 +558,7 @@ abstract class DIBImageReader extends ImageReaderBase {
if (abortRequested()) {
processReadAborted();
break;
}
}
processImageProgress(100 * y / (float) pBitmap.getHeight());
}
@@ -589,7 +598,7 @@ abstract class DIBImageReader extends ImageReaderBase {
return directory.getEntry(pImageIndex);
}
/// Test code below, ignore.. :-)
public static void main(final String[] pArgs) throws IOException {
if (pArgs.length == 0) {
@@ -699,10 +708,10 @@ abstract class DIBImageReader extends ImageReaderBase {
}
});
button.setText("" + image.getWidth() + "x" +
button.setText(image.getWidth() + "x" +
image.getHeight() + ": "
+ ((image.getColorModel() instanceof IndexColorModel) ?
"" + ((IndexColorModel) image.getColorModel()).getMapSize() :
String.valueOf(((IndexColorModel) image.getColorModel()).getMapSize()) :
"TrueColor"));
pParent.add(button);
@@ -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);
}
}
}
@@ -4,26 +4,28 @@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* * 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;
@@ -4,33 +4,38 @@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* * 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.IIOException;
import java.awt.*;
import java.awt.image.ColorModel;
import java.awt.image.IndexColorModel;
import java.io.DataInput;
import java.io.DataOutput;
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>
*/
abstract class DirectoryEntry {
private int width;
private int height;
private int colorCount;
int width;
int height;
int colorCount;
int planes;
int bitCount;
private int size;
private int offset;
int size;
int offset;
DirectoryEntry() {
}
@@ -96,6 +101,18 @@ abstract class DirectoryEntry {
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() {
return String.format(
"%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.
*/
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;
}
}
}
@@ -4,26 +4,28 @@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* * 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;
@@ -47,5 +49,4 @@ public final class ICOImageReader extends DIBImageReader {
protected ICOImageReader(final ImageReaderSpi pProvider) {
super(pProvider);
}
}
@@ -4,26 +4,28 @@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* * 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;
@@ -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;
}
}
@@ -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();
}
}
}
@@ -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";
}
}
@@ -1,29 +1,31 @@
/*
* Copyright (c) 2015, Harald Kuhr
* 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 "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* * 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;
@@ -31,14 +33,14 @@ package com.twelvemonkeys.imageio.plugins.bmp;
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
/**
* CURProviderInfo.
* ICOProviderInfo.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @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 {
protected ICOProviderInfo() {
ICOProviderInfo() {
super(
ICOProviderInfo.class,
new String[]{"ico", "ICO"},
@@ -50,9 +52,12 @@ final class ICOProviderInfo extends ReaderWriterProviderInfo {
},
"com.twelvemonkeys.imageio.plugins.bmp.ICOImageReader",
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,
false, null, null, null, null,
true, null, null, null, null
true, null, null,
null, null
);
}
}
@@ -4,33 +4,34 @@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* * 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 java.io.InputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.io.InputStream;
import java.util.Arrays;
/**
@@ -40,8 +41,8 @@ import java.util.Arrays;
* @version $Id: RLE4Decoder.java#1 $
*/
final class RLE4Decoder extends AbstractRLEDecoder {
final static int BIT_MASKS[] = {0xf0, 0x0f};
final static int BIT_SHIFTS[] = {4, 0};
final static int[] BIT_MASKS = {0xf0, 0x0f};
final static int[] BIT_SHIFTS = {4, 0};
public RLE4Decoder(final int width) {
super(width, 4);
@@ -93,7 +94,7 @@ final class RLE4Decoder extends AbstractRLEDecoder {
boolean paddingByte = (((byte2 + 1) / 2) % 2) != 0;
int packed = 0;
for (int i = 0; i < byte2; i++) {
for (int i = 0; i < byte2 && srcX / 2 < row.length; i++) {
if (i % 2 == 0) {
packed = checkEOF(stream.read());
}
@@ -110,7 +111,7 @@ final class RLE4Decoder extends AbstractRLEDecoder {
else {
// Encoded mode
// Replicate the two samples in byte2 as many times as byte1 says
for (int i = 0; i < byte1; i++) {
for (int i = 0; i < byte1 && srcX / 2 < row.length; i++) {
row[srcX / 2] |= (byte) (((byte2 & BIT_MASKS[i % 2]) >> BIT_SHIFTS[i % 2]) << BIT_SHIFTS[srcX % 2]);
srcX++;
}
@@ -4,32 +4,34 @@
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* * 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 java.io.InputStream;
import java.io.IOException;
import java.io.InputStream;
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
boolean paddingByte = (byte2 % 2) != 0;
while (byte2-- > 0) {
while (byte2-- > 0 && srcX < row.length) {
row[srcX++] = (byte) checkEOF(stream.read());
}
@@ -105,7 +107,7 @@ final class RLE8Decoder extends AbstractRLEDecoder {
// Encoded mode
// Replicate byte2 as many times as byte1 says
byte value = (byte) byte2;
while (byte1-- > 0) {
while (byte1-- > 0 && srcX < row.length) {
row[srcX++] = value;
}
}
@@ -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.CURImageReaderSpi
com.twelvemonkeys.imageio.plugins.bmp.ICOImageReaderSpi
@@ -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
@@ -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;
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.Test;
import org.mockito.InOrder;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.imageio.*;
import javax.imageio.event.IIOReadProgressListener;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.spi.ImageReaderSpi;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import static org.junit.Assert.*;
import static org.junit.Assume.assumeNoException;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.*;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
import com.twelvemonkeys.xml.XMLSerializer;
/**
* BMPImageReaderTest
@@ -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$
*/
public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader> {
@Override
protected ImageReaderSpi createProvider() {
return new BMPImageReaderSpi();
}
@Override
protected List<TestData> getTestData() {
return Arrays.asList(
// BMP Suite "Good"
@@ -112,27 +158,17 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
);
}
protected ImageReaderSpi createProvider() {
return new BMPImageReaderSpi();
}
@Override
protected BMPImageReader createReader() {
return new BMPImageReader(createProvider());
}
protected Class<BMPImageReader> getReaderClass() {
return BMPImageReader.class;
}
protected List<String> getFormatNames() {
return Collections.singletonList("bmp");
}
@Override
protected List<String> getSuffixes() {
return Arrays.asList("bmp", "rle");
}
@Override
protected List<String> getMIMETypes() {
return Collections.singletonList("image/bmp");
}
@@ -146,7 +182,7 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
ImageTypeSpecifier rawType = reader.getRawImageType(0);
// As the JPEGImageReader we delegate to returns null for YCbCr, we'll have to do the same
// As the JPEGImageReader we delegate to may return null for YCbCr, we'll have to do the same
if (rawType == null && data.getInput().toString().contains("jpeg")) {
continue;
}
@@ -241,7 +277,7 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
}
@Test
public void testAddIIOReadProgressListenerCallbacksJPEG() {
public void testAddIIOReadProgressListenerCallbacksJPEG() throws IOException {
ImageReader reader = createReader();
TestData data = new TestData(getClassLoaderResource("/bmpsuite/q/rgb24jpeg.bmp"), new Dimension(127, 64));
reader.setInput(data.getInputStream());
@@ -264,7 +300,7 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
}
@Test
public void testAddIIOReadProgressListenerCallbacksPNG() {
public void testAddIIOReadProgressListenerCallbacksPNG() throws IOException {
ImageReader reader = createReader();
TestData data = new TestData(getClassLoaderResource("/bmpsuite/q/rgb24png.bmp"), new Dimension(127, 64));
reader.setInput(data.getInputStream());
@@ -287,7 +323,7 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
}
@Test
public void testMetadataEqualsJRE() throws IOException, URISyntaxException {
public void testMetadataEqualsJRE() throws IOException {
ImageReader jreReader;
try {
@SuppressWarnings("unchecked")
@@ -306,6 +342,7 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
for (TestData data : getTestData()) {
if (data.getInput().toString().contains("pal8offs")) {
// Skip: Contains extra bogus PaletteEntry nodes
continue;
}
@@ -322,9 +359,10 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
System.err.println("WARNING: Reading " + data + " caused exception: " + e.getMessage());
continue;
}
IIOMetadata jreMetadata = jreReader.getImageMetadata(0);
assertEquals(true, metadata.isStandardMetadataFormatSupported());
assertTrue(metadata.isStandardMetadataFormatSupported());
assertEquals(jreMetadata.getNativeMetadataFormatName(), metadata.getNativeMetadataFormatName());
assertArrayEquals(jreMetadata.getExtraMetadataFormatNames(), metadata.getExtraMetadataFormatNames());
@@ -334,23 +372,24 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
String absolutePath = data.toString();
String localPath = absolutePath.substring(absolutePath.lastIndexOf("test-classes") + 12);
// TODO: blauesglas_16_bitmask444 fails BMP Version for 11+
Node expectedTree = jreMetadata.getAsTree(format);
Node actualTree = metadata.getAsTree(format);
// try {
try {
assertNodeEquals(localPath + " - " + format, expectedTree, actualTree);
// }
// catch (AssertionError e) {
// ByteArrayOutputStream expected = new ByteArrayOutputStream();
// ByteArrayOutputStream actual = new ByteArrayOutputStream();
//
// new XMLSerializer(expected, "UTF-8").serialize(expectedTree, false);
// new XMLSerializer(actual, "UTF-8").serialize(actualTree, false);
//
// assertEquals(e.getMessage(), new String(expected.toByteArray(), "UTF-8"), new String(actual.toByteArray(), "UTF-8"));
//
// throw e;
// }
}
catch (AssertionError e) {
ByteArrayOutputStream expected = new ByteArrayOutputStream();
ByteArrayOutputStream actual = new ByteArrayOutputStream();
new XMLSerializer(expected, "UTF-8").serialize(expectedTree, false);
new XMLSerializer(actual, "UTF-8").serialize(actualTree, false);
assertEquals(e.getMessage(), new String(expected.toByteArray(), StandardCharsets.UTF_8), new String(actual.toByteArray(), StandardCharsets.UTF_8));
throw e;
}
}
}
}
@@ -372,18 +411,27 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
NodeList expectedChildNodes = expected.getChildNodes();
NodeList actualChildNodes = actual.getChildNodes();
assertEquals(message + " child length differs: " + toString(expectedChildNodes) + " != " + toString(actualChildNodes),
expectedChildNodes.getLength(), actualChildNodes.getLength());
assertTrue(message + " child length differs: " + toString(expectedChildNodes) + " != " + toString(actualChildNodes),
expectedChildNodes.getLength() <= actualChildNodes.getLength());
for (int i = 0; i < expectedChildNodes.getLength(); i++) {
Node expectedChild = expectedChildNodes.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());
assertNodeEquals(message + "/" + expectedChild.getLocalName(), expectedChild, actualChild);
}
}
@SuppressWarnings("RedundantIfStatement")
private boolean excludeEqualValueTest(final Node expected) {
if (expected.getLocalName().equals("ImageSize")) {
// JRE metadata returns 0, even if known in reader...
@@ -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)
);
}
}
@@ -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;
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
@@ -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;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
import org.junit.Ignore;
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$
*/
public class CURImageReaderTest extends ImageReaderAbstractTest<CURImageReader> {
@Override
protected ImageReaderSpi createProvider() {
return new CURImageReaderSpi();
}
@Override
protected List<TestData> getTestData() {
return Arrays.asList(
new TestData(getClassLoaderResource("/cur/hand.cur"), new Dimension(32, 32)),
@@ -30,27 +67,17 @@ public class CURImageReaderTest extends ImageReaderAbstractTest<CURImageReader>
);
}
protected ImageReaderSpi createProvider() {
return new CURImageReaderSpi();
}
@Override
protected CURImageReader createReader() {
return new CURImageReader();
}
protected Class<CURImageReader> getReaderClass() {
return CURImageReader.class;
}
protected List<String> getFormatNames() {
return Collections.singletonList("cur");
}
@Override
protected List<String> getSuffixes() {
return Collections.singletonList("cur");
}
@Override
protected List<String> getMIMETypes() {
return Arrays.asList("image/vnd.microsoft.cursor", "image/cursor", "image/x-cursor");
}
@@ -69,7 +96,7 @@ public class CURImageReaderTest extends ImageReaderAbstractTest<CURImageReader>
assertNotNull("Hotspot for cursor not present", hotspot);
// Image weirdness
assertTrue("Hotspot for cursor undefined (java.awt.Image.UndefinedProperty)", Image.UndefinedProperty != hotspot);
assertNotSame("Hotspot for cursor undefined (java.awt.Image.UndefinedProperty)", Image.UndefinedProperty, hotspot);
assertTrue(String.format("Hotspot not a java.awt.Point: %s", hotspot.getClass()), hotspot instanceof Point);
assertEquals(pExpected, hotspot);
@@ -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;
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
@@ -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;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
import org.junit.Ignore;
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$
*/
public class ICOImageReaderTest extends ImageReaderAbstractTest<ICOImageReader> {
@Override
protected ImageReaderSpi createProvider() {
return new ICOImageReaderSpi();
}
@Override
protected List<TestData> getTestData() {
return Arrays.asList(
new TestData(
@@ -45,27 +82,17 @@ public class ICOImageReaderTest extends ImageReaderAbstractTest<ICOImageReader>
);
}
protected ImageReaderSpi createProvider() {
return new ICOImageReaderSpi();
}
@Override
protected ICOImageReader createReader() {
return new ICOImageReader();
}
protected Class<ICOImageReader> getReaderClass() {
return ICOImageReader.class;
}
protected List<String> getFormatNames() {
return Collections.singletonList("ico");
}
@Override
protected List<String> getSuffixes() {
return Collections.singletonList("ico");
}
@Override
protected List<String> getMIMETypes() {
return Arrays.asList("image/vnd.microsoft.icon", "image/ico", "image/x-icon");
}
@@ -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)
);
}
}
@@ -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;
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
@@ -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;
import com.twelvemonkeys.io.enc.Decoder;
@@ -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;
import com.twelvemonkeys.io.enc.Decoder;
+6 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.4.4-SNAPSHOT</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<artifactId>imageio-clippath</artifactId>
<name>TwelveMonkeys :: ImageIO :: Photoshop Path Support</name>
@@ -12,6 +12,10 @@
Photoshop Clipping Path Support.
</description>
<properties>
<project.jpms.module.name>com.twelvemonkeys.imageio.clippath</project.jpms.module.name>
</properties>
<dependencies>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
@@ -21,6 +25,7 @@
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-core</artifactId>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, Harald Kuhr
* Copyright (c) 2020, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -30,215 +30,37 @@
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.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;
/**
* 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>
* @author <a href="mailto:jpalmer@itemmaster.com">Jason Palmer, itemMaster LLC</a>
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @deprecated Use {@link AdobePathReader} instead. This class will be removed in a future release.
*/
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
* {@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}
* @see AdobePathReader#AdobePathReader(DataInput)
*/
public AdobePathBuilder(final DataInput data) {
notNull(data, "data");
this.data = data;
this.delegate = new AdobePathReader(data);
}
/**
* Creates a path builder 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.
* @see AdobePathReader#AdobePathReader(byte[])
*/
public AdobePathBuilder(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")
));
this.delegate = new AdobePathReader(data);
}
/**
* Builds the path.
*
* @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.
* @see AdobePathReader#readPath()
*/
public Path2D path() throws IOException {
List<List<AdobePathSegment>> subPaths = new ArrayList<List<AdobePathSegment>>();
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);
return delegate.readPath();
}
}
@@ -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())
);
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, Harald Kuhr
* Copyright (c) 2014-2020, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -30,7 +30,7 @@
package com.twelvemonkeys.imageio.path;
import com.twelvemonkeys.lang.Validate;
import static com.twelvemonkeys.lang.Validate.isTrue;
/**
* Adobe path segment.
@@ -40,17 +40,17 @@ import com.twelvemonkeys.lang.Validate;
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
*/
final class AdobePathSegment {
public final static int CLOSED_SUBPATH_LENGTH_RECORD = 0;
public final static int CLOSED_SUBPATH_BEZIER_LINKED = 1;
public final static int CLOSED_SUBPATH_BEZIER_UNLINKED = 2;
public final static int OPEN_SUBPATH_LENGTH_RECORD = 3;
public final static int OPEN_SUBPATH_BEZIER_LINKED = 4;
public final static int OPEN_SUBPATH_BEZIER_UNLINKED = 5;
public final static int PATH_FILL_RULE_RECORD = 6;
public final static int CLIPBOARD_RECORD = 7;
public final static int INITIAL_FILL_RULE_RECORD = 8;
static final int CLOSED_SUBPATH_LENGTH_RECORD = 0;
static final int CLOSED_SUBPATH_BEZIER_LINKED = 1;
static final int CLOSED_SUBPATH_BEZIER_UNLINKED = 2;
static final int OPEN_SUBPATH_LENGTH_RECORD = 3;
static final int OPEN_SUBPATH_BEZIER_LINKED = 4;
static final int OPEN_SUBPATH_BEZIER_UNLINKED = 5;
static final int PATH_FILL_RULE_RECORD = 6;
static final int CLIPBOARD_RECORD = 7;
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 Bezier knot, linked",
"Closed subpath Bezier knot, unlinked",
@@ -63,12 +63,18 @@ final class AdobePathSegment {
};
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 cppx;
// Anchor point
final double apy;
final double apx;
// Control point leaving knot
final double cply;
final double cplx;
@@ -79,11 +85,14 @@ final class AdobePathSegment {
this(selector, -1, cppy, cppx, apy, apx, cply, cplx);
}
AdobePathSegment(final int selector, final int length) {
this(selector, length, -1, -1, -1, -1, -1, -1);
AdobePathSegment(final int selector, final int lengthOrRule) {
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 apy, final double apx,
final double cply, final double cplx) {
@@ -91,27 +100,29 @@ final class AdobePathSegment {
switch (selector) {
case CLOSED_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;
case CLOSED_SUBPATH_BEZIER_LINKED:
case CLOSED_SUBPATH_BEZIER_UNLINKED:
case OPEN_SUBPATH_BEZIER_LINKED:
case OPEN_SUBPATH_BEZIER_UNLINKED:
Validate.isTrue(
cppx >= 0 && cppx <= 1 && cppy >= 0 && cppy <= 1,
String.format("Unexpected point: [%f, %f]", cppx ,cppy)
isTrue(
cppx >= -16 && cppx <= 16 && cppy >= -16 && cppy <= 16,
String.format("Expected point in range [-16...16]: (%f, %f)", cppx ,cppy)
);
break;
case PATH_FILL_RULE_RECORD:
case CLIPBOARD_RECORD:
case INITIAL_FILL_RULE_RECORD:
isTrue(lengthOrRule == 0 || lengthOrRule == 1, lengthOrRule, "Expected rule (1 or 0): %d");
break;
case CLIPBOARD_RECORD:
break;
default:
throw new IllegalArgumentException("Bad selector: " + selector);
throw new IllegalArgumentException("Unknown selector: " + selector);
}
this.selector = selector;
this.length = length;
this.lengthOrRule = lengthOrRule;
this.cppy = cppy;
this.cppx = cppx;
this.apy = apy;
@@ -120,6 +131,14 @@ final class AdobePathSegment {
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
public boolean equals(final Object other) {
if (this == other) {
@@ -139,7 +158,7 @@ final class AdobePathSegment {
&& Double.compare(that.cppx, cppx) == 0
&& Double.compare(that.cppy, cppy) == 0
&& selector == that.selector
&& length == that.length;
&& lengthOrRule == that.lengthOrRule;
}
@@ -148,7 +167,7 @@ final class AdobePathSegment {
long tempBits;
int result = selector;
result = 31 * result + length;
result = 31 * result + lengthOrRule;
tempBits = Double.doubleToLongBits(cppy);
result = 31 * result + (int) (tempBits ^ (tempBits >>> 32));
tempBits = Double.doubleToLongBits(cppx);
@@ -170,13 +189,13 @@ final class AdobePathSegment {
switch (selector) {
case INITIAL_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 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:
// 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]);
}
}
@@ -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();
}
}
+172 -19
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, Harald Kuhr
* Copyright (c) 2014-2020, Harald Kuhr
* All rights reserved.
*
* 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.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.ImageOutputStream;
import javax.imageio.stream.MemoryCacheImageInputStream;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.File;
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.Map;
import static com.twelvemonkeys.lang.Validate.isTrue;
import static com.twelvemonkeys.lang.Validate.notNull;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
/**
* 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>Apply a given path to a given {@code BufferedImage} {@link #applyClippingPath}</li>
* <li>Read an image with path applied {@link #readClipped}</li>
* <li>Write an image with embedded path {@link #writeClipped}</li>
* </ul>
*
* @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:harald.kuhr@gmail.com">Harald Kuhr</a>
* @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 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 {
notNull(stream, "stream");
@@ -99,7 +109,7 @@ public final class Paths {
if (magic == PSD.RESOURCE_TYPE) {
// This is a PSD Image Resource Block, we can parse directly
return buildPathFromPhotoshopResources(stream);
return readPathFromPhotoshopResources(stream);
}
else if (magic == PSD.SIGNATURE_8BPS) {
// PSD version
@@ -115,17 +125,21 @@ public final class Paths {
long imageResourcesLen = stream.readUnsignedInt();
// Image resources
return buildPathFromPhotoshopResources(new SubImageInputStream(stream, imageResourcesLen));
return readPathFromPhotoshopResources(new SubImageInputStream(stream, imageResourcesLen));
}
else if (magic >>> 16 == JPEG.SOI && (magic & 0xff00) == 0xff00) {
// JPEG version
Map<Integer, java.util.List<String>> segmentIdentifiers = new LinkedHashMap<>();
segmentIdentifiers.put(JPEG.APP13, singletonList("Photoshop 3.0"));
Map<Integer, List<String>> segmentIdentifiers = singletonMap(JPEG.APP13, singletonList("Photoshop 3.0"));
List<JPEGSegment> photoshop = JPEGSegmentUtil.readSegments(stream, segmentIdentifiers);
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
@@ -137,7 +151,7 @@ public final class Paths {
Entry photoshop = directory.getEntryById(TIFF.TAG_PHOTOSHOP);
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);
if (AdobePathBuilder.DEBUG) {
if (AdobePathReader.DEBUG) {
System.out.println("resourceBlocks: " + resourceBlocks);
}
Entry resourceBlock = resourceBlocks.getEntryById(PSD.RES_CLIPPING_PATH);
Entry pathResource = resourceBlocks.getEntryById(PSD.RES_CLIPPING_PATH);
if (resourceBlock != null) {
return new AdobePathBuilder((byte[]) resourceBlock.getValue()).path();
if (pathResource != null) {
return new AdobePathReader((byte[]) pathResource.getValue()).readPath();
}
return null;
@@ -254,9 +268,149 @@ public final class Paths {
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
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");
tempFile.deleteOnExit();
@@ -270,5 +424,4 @@ public final class Paths {
System.err.printf("%s not deleted\n", tempFile);
}
}
}
@@ -43,6 +43,7 @@ import static com.twelvemonkeys.imageio.path.PathsTest.assertPathEquals;
import static com.twelvemonkeys.imageio.path.PathsTest.readExpectedPath;
import static org.junit.Assert.assertNotNull;
@SuppressWarnings("deprecation")
public class AdobePathBuilderTest {
@Test(expected = IllegalArgumentException.class)
@@ -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"));
}
}
@@ -63,7 +63,7 @@ public class AdobePathSegmentTest {
AdobePathSegment segment = new AdobePathSegment(AdobePathSegment.OPEN_SUBPATH_LENGTH_RECORD, 42);
assertEquals(AdobePathSegment.OPEN_SUBPATH_LENGTH_RECORD, segment.selector);
assertEquals(42, segment.length);
assertEquals(42, segment.lengthOrRule);
assertEquals(-1, segment.cppx, 0);
assertEquals(-1, segment.cppy, 0);
assertEquals(-1, segment.apx, 0);
@@ -82,7 +82,7 @@ public class AdobePathSegmentTest {
AdobePathSegment segment = new AdobePathSegment(AdobePathSegment.CLOSED_SUBPATH_LENGTH_RECORD, 27);
assertEquals(AdobePathSegment.CLOSED_SUBPATH_LENGTH_RECORD, segment.selector);
assertEquals(27, segment.length);
assertEquals(27, segment.lengthOrRule);
assertEquals(-1, segment.cppx, 0);
assertEquals(-1, segment.cppy, 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);
assertEquals(AdobePathSegment.OPEN_SUBPATH_BEZIER_LINKED, segment.selector);
assertEquals(-1, segment.length);
assertEquals(-1, segment.lengthOrRule);
assertEquals(.5, segment.cppx, 0);
assertEquals(.5, segment.cppy, 0);
assertEquals(0, segment.apx, 0);
@@ -113,8 +113,13 @@ public class AdobePathSegmentTest {
}
@Test(expected = IllegalArgumentException.class)
public void testCreateOpenLinkedRecordNegative() {
new AdobePathSegment(AdobePathSegment.OPEN_SUBPATH_BEZIER_LINKED, -.5, -.5, 0, 0, 1, 1);
public void testCreateOpenLinkedRecordOutOfRangeNegative() {
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
@@ -122,7 +127,7 @@ public class AdobePathSegmentTest {
AdobePathSegment segment = new AdobePathSegment(AdobePathSegment.OPEN_SUBPATH_BEZIER_UNLINKED, .5, .5, 0, 0, 1, 1);
assertEquals(AdobePathSegment.OPEN_SUBPATH_BEZIER_UNLINKED, segment.selector);
assertEquals(-1, segment.length);
assertEquals(-1, segment.lengthOrRule);
assertEquals(.5, segment.cppx, 0);
assertEquals(.5, segment.cppy, 0);
assertEquals(0, segment.apx, 0);
@@ -138,8 +143,13 @@ public class AdobePathSegmentTest {
@Test(expected = IllegalArgumentException.class)
public void testCreateOpenUnlinkedRecordNegative() {
new AdobePathSegment(AdobePathSegment.OPEN_SUBPATH_BEZIER_UNLINKED, -.5, -.5, 0, 0, 1, 1);
public void testCreateOpenUnlinkedRecordOutOfRangeNegative() {
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
@@ -149,7 +159,7 @@ public class AdobePathSegmentTest {
AdobePathSegment segment = new AdobePathSegment(AdobePathSegment.CLOSED_SUBPATH_BEZIER_LINKED, .5, .5, 0, 0, 1, 1);
assertEquals(AdobePathSegment.CLOSED_SUBPATH_BEZIER_LINKED, segment.selector);
assertEquals(-1, segment.length);
assertEquals(-1, segment.lengthOrRule);
assertEquals(.5, segment.cppx, 0);
assertEquals(.5, segment.cppy, 0);
assertEquals(0, segment.apx, 0);
@@ -164,8 +174,13 @@ public class AdobePathSegmentTest {
}
@Test(expected = IllegalArgumentException.class)
public void testCreateClosedLinkedRecordNegative() {
new AdobePathSegment(AdobePathSegment.CLOSED_SUBPATH_BEZIER_LINKED, -.5, -.5, 0, 0, 1, 1);
public void testCreateClosedLinkedRecordOutOfRangeNegative() {
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
@@ -173,7 +188,7 @@ public class AdobePathSegmentTest {
AdobePathSegment segment = new AdobePathSegment(AdobePathSegment.CLOSED_SUBPATH_BEZIER_UNLINKED, .5, .5, 0, 0, 1, 1);
assertEquals(AdobePathSegment.CLOSED_SUBPATH_BEZIER_UNLINKED, segment.selector);
assertEquals(-1, segment.length);
assertEquals(-1, segment.lengthOrRule);
assertEquals(.5, segment.cppx, 0);
assertEquals(.5, segment.cppy, 0);
assertEquals(0, segment.apx, 0);
@@ -189,17 +204,22 @@ public class AdobePathSegmentTest {
@Test(expected = IllegalArgumentException.class)
public void testCreateClosedUnlinkedRecordNegative() {
new AdobePathSegment(AdobePathSegment.CLOSED_SUBPATH_BEZIER_UNLINKED, -.5, -.5, 0, 0, 1, 1);
public void testCreateClosedUnlinkedRecordOutOfRangeNegative() {
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
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.contains("Initial"));
assertTrue(string, string.contains("fill"));
assertTrue(string, string.contains("rule=2"));
assertTrue(string, string.contains("rule=0"));
}
@Test
@@ -208,13 +228,13 @@ public class AdobePathSegmentTest {
assertTrue(string, string.startsWith("Len"));
assertTrue(string, string.contains("Closed"));
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();
assertTrue(string, string.startsWith("Len"));
assertTrue(string, string.contains("Open"));
assertTrue(string, string.contains("subpath"));
assertTrue(string, string.contains("totalPoints=42"));
assertTrue(string, string.contains("length=42"));
}
@Test
@@ -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);
}
}
@@ -38,15 +38,18 @@ import org.junit.Test;
import javax.imageio.ImageIO;
import javax.imageio.spi.IIORegistry;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import java.awt.*;
import java.awt.geom.GeneralPath;
import java.awt.geom.Path2D;
import java.awt.geom.PathIterator;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import static org.junit.Assert.*;
import static org.junit.Assume.assumeTrue;
/**
* PathsTest.
@@ -125,12 +128,12 @@ public class PathsTest {
}
@Test(expected = IllegalArgumentException.class)
public void testApplyClippingPathNullPath() throws IOException {
public void testApplyClippingPathNullPath() {
Paths.applyClippingPath(null, new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY));
}
@Test(expected = IllegalArgumentException.class)
public void testApplyClippingPathNullSource() throws IOException {
public void testApplyClippingPathNullSource() {
Paths.applyClippingPath(new GeneralPath(), null);
}
@@ -147,7 +150,7 @@ public class PathsTest {
assertEquals(source.getWidth(), image.getWidth());
assertEquals(source.getHeight(), image.getHeight());
// Transparent
assertTrue(image.getColorModel().getTransparency() == Transparency.TRANSLUCENT);
assertEquals(Transparency.TRANSLUCENT, image.getColorModel().getTransparency());
// Corners (at least) should be transparent
assertEquals(0, image.getRGB(0, 0));
@@ -161,8 +164,9 @@ public class PathsTest {
// TODO: Mor sophisticated test that tests all pixels outside path...
}
@SuppressWarnings("ConstantConditions")
@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);
}
@@ -209,7 +213,7 @@ public class PathsTest {
assertEquals(857, image.getWidth());
assertEquals(1800, image.getHeight());
// Transparent
assertTrue(image.getColorModel().getTransparency() == Transparency.TRANSLUCENT);
assertEquals(Transparency.TRANSLUCENT, image.getColorModel().getTransparency());
// Corners (at least) should be transparent
assertEquals(0, image.getRGB(0, 0));
@@ -230,34 +234,70 @@ public class PathsTest {
}
static Path2D readExpectedPath(final String resource) throws IOException {
ObjectInputStream ois = new ObjectInputStream(PathsTest.class.getResourceAsStream(resource));
try {
try (ObjectInputStream ois = new ObjectInputStream(PathsTest.class.getResourceAsStream(resource))) {
return (Path2D) ois.readObject();
}
catch (ClassNotFoundException e) {
throw new IOException(e);
}
finally {
ois.close();
}
}
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 actualIterator = actualPath.getPathIterator(null);
float[] expectedCoords = 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 actualType = actualIterator.currentSegment(actualCoords);
assertEquals(expectedType, actualType);
assertArrayEquals(expectedCoords, actualCoords, 0);
assertEquals("Unexpected segment type", expectedType, actualType);
assertArrayEquals("Unexpected coordinates", expectedCoords, actualCoords, 0);
actualIterator.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);
}
}

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