mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2026-05-18 00:00:03 -04:00
Compare commits
172 Commits
3.7-bugfix
...
3.8-bugfix
| Author | SHA1 | Date | |
|---|---|---|---|
| 488d6da71a | |||
| b19e45a911 | |||
| a5b6cf898d | |||
| ce597c437d | |||
| fa4b3787d0 | |||
| 3c18e8a510 | |||
| 206481038e | |||
| cff4d88991 | |||
| 8ea8e061a9 | |||
| 101ad18f71 | |||
| 08b441a17e | |||
| b6c76d8566 | |||
| 3f74b2ddf3 | |||
| 46b48f32c3 | |||
| a07d0285fe | |||
| 98de4ad4ec | |||
| aa82612765 | |||
| 9213da3184 | |||
| a5e2226a5a | |||
| 773bedccca | |||
| 6bcc17a020 | |||
| 37d1da9b9d | |||
| 8cf1405dfc | |||
| 8c37d19928 | |||
| 87cd506fdd | |||
| e0c7edebbd | |||
| 5d13bd653f | |||
| 2d974874a9 | |||
| f625622b10 | |||
| dbdd7ae3f1 | |||
| 73883ebf99 | |||
| 970b238066 | |||
| 6cb8ac4b68 | |||
| 1a2a4edfe8 | |||
| a12a1f73b5 | |||
| 46bfdd93d8 | |||
| 447ef6b8eb | |||
| c19b6ede0a | |||
| 401c6355a1 | |||
| be0cf16124 | |||
| 47b0cd6e9a | |||
| b52ab149b3 | |||
| 900c26a5ac | |||
| 7233c593ac | |||
| 8159ba1245 | |||
| 92581a077b | |||
| f87b4d6748 | |||
| 0495f6e266 | |||
| bc7c8ba20c | |||
| 0c270f8343 | |||
| e1db332dca | |||
| 016977e382 | |||
| 134eecc59f | |||
| 16c78052ee | |||
| b51e8ccf6e | |||
| 76a9ff1122 | |||
| e9996f096f | |||
| 93d42e1c24 | |||
| 5824167600 | |||
| 126956ebd0 | |||
| d54ceba3ff | |||
| 11ee7e5e23 | |||
| 954dffd213 | |||
| b7b2a61c93 | |||
| 3cf6a4b836 | |||
| a93be99933 | |||
| b19bd1441f | |||
| 482af60534 | |||
| f55a6d30dd | |||
| e5f6227479 | |||
| 01d6fc8b49 | |||
| f133ea7d61 | |||
| 3d5cf0eecd | |||
| 975e23c28f | |||
| be60f307f7 | |||
| 3e783fba92 | |||
| 35ffe29e03 | |||
| 55155aa61c | |||
| 9e9decd5dd | |||
| 3c0b78549e | |||
| 46db5a2fbf | |||
| f6a9477279 | |||
| b7192ae857 | |||
| c0b2769e3b | |||
| 6c27ec6b30 | |||
| 0c90196357 | |||
| 48f82a159f | |||
| 7105738811 | |||
| 10aa4ba41e | |||
| 6fb06da4d7 | |||
| a963e1c355 | |||
| 966a9da45d | |||
| 319b2c4e18 | |||
| e9bf7d080c | |||
| fb3691e2ee | |||
| 25f9cc5c55 | |||
| 94777ddc96 | |||
| a4c12d0d64 | |||
| 08a69886b1 | |||
| ab85ff0ec8 | |||
| 7de8231471 | |||
| 0de9f79029 | |||
| eeb56acdde | |||
| a6862cfec8 | |||
| f8284700b4 | |||
| 38caeb22e0 | |||
| b2c5915db8 | |||
| 3911191b04 | |||
| bc328419ac | |||
| da4efe98bf | |||
| 6653f4a85d | |||
| 511a29beb9 | |||
| 5617b4323c | |||
| 16d0af357d | |||
| 74927d5396 | |||
| 7e809dd834 | |||
| 7aa95a08bc | |||
| c28963ae49 | |||
| 0327f5fc1a | |||
| 1c59057c30 | |||
| 3e1f85c4dc | |||
| 11227a68a0 | |||
| 62ba73a30e | |||
| 1f33afb5a1 | |||
| 9d3f271867 | |||
| 812e12acb0 | |||
| 060b6cf852 | |||
| e68ce7ffd1 | |||
| 778cdef69c | |||
| d46a76fca8 | |||
| 105a1ee466 | |||
| aa030f526c | |||
| 976e5d6210 | |||
| 6daca00fcd | |||
| ef05872934 | |||
| 1ddab866fd | |||
| ce997a6951 | |||
| 23bf5cb7b2 | |||
| 564778f415 | |||
| e28bf8fb44 | |||
| cf8d630d01 | |||
| 0ff7224912 | |||
| 196081a317 | |||
| ff50180d86 | |||
| 8f2c482167 | |||
| eab24890ca | |||
| cd42d81817 | |||
| ba5c667b6c | |||
| 4e9fa9442c | |||
| 4d2326c18d | |||
| 94eac2d6e5 | |||
| f63a33d541 | |||
| 00f8d87f36 | |||
| 4c2ab6da7b | |||
| b5088312e2 | |||
| f04f968f12 | |||
| 8896092e31 | |||
| 2f9768a1d4 | |||
| 06bcf22242 | |||
| 20c7f8e60e | |||
| 15a9ad0a9b | |||
| 7ae2d636dc | |||
| 12e756b23c | |||
| 4e2bf131d2 | |||
| d0c4a07556 | |||
| 21059c8d5a | |||
| fa7b530809 | |||
| 790cf3b32e | |||
| b1baaad23b | |||
| 7fa704ace5 | |||
| 8d07f4fe90 | |||
| 32bba6857b |
@@ -0,0 +1 @@
|
||||
github: haraldk
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: Reported bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**Version information**
|
||||
1. The version of the TwelveMonkeys ImageIO library in use.
|
||||
For example: 4.0.0
|
||||
|
||||
2. The *exact* output of `java --version` (or `java -version` for older Java releases).
|
||||
For example:
|
||||
|
||||
java version "1.8.0_271"
|
||||
Java(TM) SE Runtime Environment (build 1.8.0_271-b09)
|
||||
Java HotSpot(TM) 64-Bit Server VM (build 25.271-b09, mixed mode)
|
||||
|
||||
3. Extra information about OS version, server version, standalone program or web application packaging, executable wrapper, etc. Please state exact version numbers where applicable.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
|
||||
1. Compile the below sample code
|
||||
2. Download the sample image file
|
||||
3. Run the code with the sample file
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Example code**
|
||||
Preferably as a failing JUnit test, or a standalone program with a `main` method that showcases the problem.
|
||||
|
||||
Less is more. Don't add your entire project, only the code required to reproduce the problem. 😀
|
||||
|
||||
**Sample file(s)**
|
||||
Attach any sample files needed to reproduce the problem. Use a ZIP-file if the format is not directly supported by GitHub.
|
||||
|
||||
**Stak trace**
|
||||
Always include the stack trace you experience.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
Do not add screenshots of code or stack traces. 😀
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: New feature
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a use case or a problem you are working on? Please describe.**
|
||||
A clear and concise description of what the problem or use case is. Understanding the rationale is key, to be able to implemeent the right solution.
|
||||
|
||||
**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 already considered, and why they won't work.
|
||||
|
||||
**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,11 @@
|
||||
**What is fixed** Add link to the issue this PR fixes.
|
||||
|
||||
Example: 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
|
||||
@@ -0,0 +1,90 @@
|
||||
name: CI
|
||||
|
||||
on: [ push, pull_request ]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Test OpenJDK ${{ matrix.java }} on ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ ubuntu-latest, windows-latest, macos-latest ]
|
||||
java: [ 8, 11, 17 ]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-java@v2
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: ${{ matrix.java }}
|
||||
java-package: jdk
|
||||
cache: 'maven'
|
||||
- name: Run Tests
|
||||
run: mvn test
|
||||
- name: Publish Test Report
|
||||
uses: mikepenz/action-junit-report@v2
|
||||
if: ${{ !cancelled() }}
|
||||
with:
|
||||
report_paths: "**/target/surefire-reports/TEST*.xml"
|
||||
check_name: Unit Test Results for OpenJDK ${{ matrix.java }} on ${{ matrix.os }}
|
||||
|
||||
test_oracle:
|
||||
name: Test Oracle JDK 8 with KCMS=${{ matrix.kcms }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
kcms: [ true, false ]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: |
|
||||
download_url="https://javadl.oracle.com/webapps/download/AutoDL?BundleId=245038_d3c52aa6bfa54d3ca74e617f18309292"
|
||||
wget -O $RUNNER_TEMP/java_package.tar.gz $download_url
|
||||
- uses: actions/setup-java@v2
|
||||
with:
|
||||
distribution: 'jdkfile'
|
||||
jdkFile: ${{ runner.temp }}/java_package.tar.gz
|
||||
java-version: '8'
|
||||
cache: 'maven'
|
||||
- name: Set MAVEN_OPTS
|
||||
if: ${{ matrix.kcms }}
|
||||
run: |
|
||||
echo "MAVEN_OPTS=-Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider" >> $GITHUB_ENV
|
||||
- name: Display Java version
|
||||
run: java -version
|
||||
- name: Run Tests
|
||||
run: mvn test
|
||||
- name: Publish Test Report
|
||||
uses: mikepenz/action-junit-report@v2
|
||||
if: ${{ !cancelled() }}
|
||||
with:
|
||||
report_paths: "**/target/surefire-reports/TEST*.xml"
|
||||
check_name: Unit Test Results for Oracle JDK 8 with KCMS=${{ matrix.kcms }}
|
||||
|
||||
release:
|
||||
name: Deploy
|
||||
needs: [ test, test_oracle ]
|
||||
if: github.ref == 'refs/heads/master' # only perform on latest master
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Maven Central
|
||||
uses: actions/setup-java@v2
|
||||
with: # running setup-java again overwrites the settings.xml
|
||||
distribution: 'temurin'
|
||||
java-version: '8'
|
||||
java-package: jdk
|
||||
server-id: ossrh # Value of the distributionManagement/repository/id field of the pom.xml
|
||||
server-username: MAVEN_CENTRAL_USERNAME # env variable for username in deploy (1)
|
||||
server-password: MAVEN_CENTRAL_PASSWORD # env variable for token in deploy (2)
|
||||
gpg-private-key: ${{ secrets.GPG_KEY }} # Value of the GPG private key to import
|
||||
gpg-passphrase: MAVEN_CENTRAL_GPG_PASSPHRASE # env variable for GPG private key passphrase (3)
|
||||
- name: Get Project Version
|
||||
run: |
|
||||
echo "PROJECT_VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)" >> $GITHUB_ENV
|
||||
- name: Publish to Maven Central
|
||||
if: ${{ endsWith(env.PROJECT_VERSION, '-SNAPSHOT') }}
|
||||
run: mvn deploy -P release -DskipTests
|
||||
env:
|
||||
MAVEN_CENTRAL_USERNAME: ${{ secrets.SONATYPE_USERNAME }} # must be the same env variable name as (1)
|
||||
MAVEN_CENTRAL_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} # must be the same env variable name as (2)
|
||||
MAVEN_CENTRAL_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} # must be the same env variable name as (3)
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
dist: trusty
|
||||
language: java
|
||||
jdk:
|
||||
- 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,57 +1,55 @@
|
||||
[](https://travis-ci.org/haraldk/TwelveMonkeys)
|
||||
[](https://maven-badges.herokuapp.com/maven-central/com.twelvemonkeys.imageio/imageio)
|
||||
[](https://github.com/haraldk/TwelveMonkeys/actions/workflows/ci.yml)
|
||||
[](https://search.maven.org/search?q=g:com.twelvemonkeys.imageio)
|
||||
[](https://oss.sonatype.org/content/repositories/snapshots/com/twelvemonkeys/)
|
||||
[](https://stackoverflow.com/questions/tagged/twelvemonkeys)
|
||||
[](https://paypal.me/haraldk76/100)
|
||||
|
||||
## About
|
||||
|
||||
TwelveMonkeys ImageIO is a collection of plugins and extensions for Java's ImageIO.
|
||||
TwelveMonkeys ImageIO provides extended image file format support for the Java platform, through plugins for 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
|
||||
The main goal of this project is to provide support for formats not covered by the JRE itself.
|
||||
Support for these formats is important, to be able to read data found
|
||||
"in the wild", as well as to maintain access to data in legacy formats.
|
||||
Because there is lots of legacy data out there, we see the need for open implementations of readers for popular formats.
|
||||
The goal is to create a set of efficient and robust ImageIO plug-ins, that can be distributed independently.
|
||||
As there is lots of legacy data out there, we see the need for open implementations of readers for popular formats.
|
||||
|
||||
----
|
||||
|
||||
## File formats supported
|
||||
|
||||
| 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) | In progress
|
||||
| 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) |
|
||||
| Plugin | Format | Description | R | W | 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) |
|
||||
|
||||
|
||||
**Important note on using Batik:** *Please read [The Apache™ XML Graphics Project - Security](http://xmlgraphics.apache.org/security.html),
|
||||
**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
|
||||
@@ -166,7 +164,7 @@ finally {
|
||||
```
|
||||
|
||||
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
|
||||
@@ -220,14 +218,14 @@ 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
|
||||
|
||||
@@ -274,12 +272,12 @@ To depend on the JPEG and TIFF plugin using Maven, add the following to your POM
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-jpeg</artifactId>
|
||||
<version>3.6.4</version>
|
||||
<version>3.8.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-tiff</artifactId>
|
||||
<version>3.6.4</version>
|
||||
<version>3.8.1</version>
|
||||
</dependency>
|
||||
|
||||
<!--
|
||||
@@ -289,7 +287,17 @@ To depend on the JPEG and TIFF plugin using Maven, add the following to your POM
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.servlet</groupId>
|
||||
<artifactId>servlet</artifactId>
|
||||
<version>3.6.4</version>
|
||||
<version>3.8.1</version>
|
||||
</dependency>
|
||||
|
||||
<!--
|
||||
Or Jakarta version, for Servlet API 5.0
|
||||
-->
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.servlet</groupId>
|
||||
<artifactId>servlet</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<classifier>jakarta</classifier>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
```
|
||||
@@ -298,13 +306,13 @@ To depend on the JPEG and TIFF plugin using Maven, add the following to your POM
|
||||
|
||||
To depend on the JPEG and TIFF plugin in your IDE or program, add all of the following JARs to your class path:
|
||||
|
||||
twelvemonkeys-common-lang-3.6.4.jar
|
||||
twelvemonkeys-common-io-3.6.4.jar
|
||||
twelvemonkeys-common-image-3.6.4.jar
|
||||
twelvemonkeys-imageio-core-3.6.4.jar
|
||||
twelvemonkeys-imageio-metadata-3.6.4.jar
|
||||
twelvemonkeys-imageio-jpeg-3.6.4.jar
|
||||
twelvemonkeys-imageio-tiff-3.6.4.jar
|
||||
twelvemonkeys-common-lang-3.8.1.jar
|
||||
twelvemonkeys-common-io-3.8.1.jar
|
||||
twelvemonkeys-common-image-3.8.1.jar
|
||||
twelvemonkeys-imageio-core-3.8.1.jar
|
||||
twelvemonkeys-imageio-metadata-3.8.1.jar
|
||||
twelvemonkeys-imageio-jpeg-3.8.1.jar
|
||||
twelvemonkeys-imageio-tiff-3.8.1.jar
|
||||
|
||||
#### Deploying the plugins in a web app
|
||||
|
||||
@@ -370,77 +378,79 @@ Other "fat" JAR bundlers will probably have similar mechanisms to merge entries
|
||||
|
||||
### Links to prebuilt binaries
|
||||
|
||||
##### Latest version (3.6.4)
|
||||
##### Latest version (3.8.1)
|
||||
|
||||
Requires Java 7 or later.
|
||||
|
||||
Common dependencies
|
||||
* [common-lang-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-lang/3.6.4/common-lang-3.6.4.jar)
|
||||
* [common-io-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-io/3.6.4/common-io-3.6.4.jar)
|
||||
* [common-image-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-image/3.6.4/common-image-3.6.4.jar)
|
||||
* [common-lang-3.8.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-lang/3.8.1/common-lang-3.8.1.jar)
|
||||
* [common-io-3.8.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-io/3.8.1/common-io-3.8.1.jar)
|
||||
* [common-image-3.8.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-image/3.8.1/common-image-3.8.1.jar)
|
||||
|
||||
ImageIO dependencies
|
||||
* [imageio-core-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-core/3.6.4/imageio-core-3.6.4.jar)
|
||||
* [imageio-metadata-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-metadata/3.6.4/imageio-metadata-3.6.4.jar)
|
||||
* [imageio-core-3.8.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-core/3.8.1/imageio-core-3.8.1.jar)
|
||||
* [imageio-metadata-3.8.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-metadata/3.8.1/imageio-metadata-3.8.1.jar)
|
||||
|
||||
ImageIO plugins
|
||||
* [imageio-bmp-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-bmp/3.6.4/imageio-bmp-3.6.4.jar)
|
||||
* [imageio-hdr-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-hdr/3.6.4/imageio-hdr-3.6.4.jar)
|
||||
* [imageio-icns-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-icns/3.6.4/imageio-icns-3.6.4.jar)
|
||||
* [imageio-iff-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-iff/3.6.4/imageio-iff-3.6.4.jar)
|
||||
* [imageio-jpeg-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jpeg/3.6.4/imageio-jpeg-3.6.4.jar)
|
||||
* [imageio-pcx-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pcx/3.6.4/imageio-pcx-3.6.4.jar)
|
||||
* [imageio-pict-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pict/3.6.4/imageio-pict-3.6.4.jar)
|
||||
* [imageio-pnm-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pnm/3.6.4/imageio-pnm-3.6.4.jar)
|
||||
* [imageio-psd-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-psd/3.6.4/imageio-psd-3.6.4.jar)
|
||||
* [imageio-sgi-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-sgi/3.6.4/imageio-sgi-3.6.4.jar)
|
||||
* [imageio-tga-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tga/3.6.4/imageio-tga-3.6.4.jar)
|
||||
* [imageio-thumbsdb-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-thumbsdb/3.6.4/imageio-thumbsdb-3.6.4.jar)
|
||||
* [imageio-tiff-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tiff/3.6.4/imageio-tiff-3.6.4.jar)
|
||||
* [imageio-bmp-3.8.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-bmp/3.8.1/imageio-bmp-3.8.1.jar)
|
||||
* [imageio-hdr-3.8.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-hdr/3.8.1/imageio-hdr-3.8.1.jar)
|
||||
* [imageio-icns-3.8.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-icns/3.8.1/imageio-icns-3.8.1.jar)
|
||||
* [imageio-iff-3.8.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-iff/3.8.1/imageio-iff-3.8.1.jar)
|
||||
* [imageio-jpeg-3.8.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jpeg/3.8.1/imageio-jpeg-3.8.1.jar)
|
||||
* [imageio-pcx-3.8.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pcx/3.8.1/imageio-pcx-3.8.1.jar)
|
||||
* [imageio-pict-3.8.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pict/3.8.1/imageio-pict-3.8.1.jar)
|
||||
* [imageio-pnm-3.8.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pnm/3.8.1/imageio-pnm-3.8.1.jar)
|
||||
* [imageio-psd-3.8.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-psd/3.8.1/imageio-psd-3.8.1.jar)
|
||||
* [imageio-sgi-3.8.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-sgi/3.8.1/imageio-sgi-3.8.1.jar)
|
||||
* [imageio-tga-3.8.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tga/3.8.1/imageio-tga-3.8.1.jar)
|
||||
* [imageio-thumbsdb-3.8.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-thumbsdb/3.8.1/imageio-thumbsdb-3.8.1.jar)
|
||||
* [imageio-tiff-3.8.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tiff/3.8.1/imageio-tiff-3.8.1.jar)
|
||||
* [imageio-webp-3.8.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-webp/3.8.1/imageio-webp-3.8.1.jar)
|
||||
* [imageio-xwd-3.8.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-xwd/3.8.1/imageio-xwd-3.8.1.jar)
|
||||
|
||||
ImageIO plugins requiring 3rd party libs
|
||||
* [imageio-batik-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-batik/3.6.4/imageio-batik-3.6.4.jar)
|
||||
* [imageio-batik-3.8.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-batik/3.8.1/imageio-batik-3.8.1.jar)
|
||||
|
||||
Photoshop Path support for ImageIO
|
||||
* [imageio-clippath-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-clippath/3.6.4/imageio-clippath-3.6.4.jar)
|
||||
* [imageio-clippath-3.8.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-clippath/3.8.1/imageio-clippath-3.8.1.jar)
|
||||
|
||||
Servlet support
|
||||
* [servlet-3.6.4.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/servlet/servlet/3.6.4/servlet-3.6.4.jar)
|
||||
* [servlet-3.8.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/servlet/servlet/3.8.1/servlet-3.8.1.jar)
|
||||
|
||||
##### Old version (3.0.x)
|
||||
|
||||
Use this version for projects that requires Java 6 or need the JMagick support. *Does not support Java 8 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
|
||||
|
||||
This project is provided 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-2020, Harald Kuhr
|
||||
All rights reserved.
|
||||
@@ -495,7 +505,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
|
||||
|
||||
+6
-1
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys</groupId>
|
||||
<artifactId>twelvemonkeys</artifactId>
|
||||
<version>3.7.0</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>com.twelvemonkeys.bom</groupId>
|
||||
@@ -123,6 +123,11 @@
|
||||
<artifactId>imageio-tiff</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-webp</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-xwd</artifactId>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>3.7.0</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>common-image</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
@@ -31,7 +31,7 @@
|
||||
<dependency>
|
||||
<groupId>jmagick</groupId>
|
||||
<artifactId>jmagick</artifactId>
|
||||
<version>6.2.4</version>
|
||||
<version>6.6.9</version>
|
||||
<optional>true</optional>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -34,7 +34,13 @@ import java.awt.*;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.image.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.BufferedImageOp;
|
||||
import java.awt.image.ColorModel;
|
||||
import java.awt.image.ImagingOpException;
|
||||
import java.awt.image.Raster;
|
||||
import java.awt.image.RasterOp;
|
||||
import java.awt.image.WritableRaster;
|
||||
|
||||
/**
|
||||
* This is a drop-in replacement for {@link java.awt.image.AffineTransformOp}.
|
||||
@@ -70,6 +76,7 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp {
|
||||
delegate = new java.awt.image.AffineTransformOp(xform, interpolationType);
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public BufferedImage filter(final BufferedImage src, BufferedImage dst) {
|
||||
try {
|
||||
@@ -80,10 +87,9 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp {
|
||||
dst = createCompatibleDestImage(src, src.getColorModel());
|
||||
}
|
||||
|
||||
Graphics2D g2d = null;
|
||||
Graphics2D g2d = dst.createGraphics();
|
||||
|
||||
try {
|
||||
g2d = dst.createGraphics();
|
||||
int interpolationType = delegate.getInterpolationType();
|
||||
|
||||
if (interpolationType > 0) {
|
||||
@@ -109,9 +115,7 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp {
|
||||
return dst;
|
||||
}
|
||||
finally {
|
||||
if (g2d != null) {
|
||||
g2d.dispose();
|
||||
}
|
||||
g2d.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,8 +45,8 @@ import java.awt.image.BufferedImage;
|
||||
*/
|
||||
public class BufferedImageIcon implements Icon {
|
||||
private final BufferedImage image;
|
||||
private int width;
|
||||
private int height;
|
||||
private final int width;
|
||||
private final int height;
|
||||
private final boolean fast;
|
||||
|
||||
public BufferedImageIcon(BufferedImage pImage) {
|
||||
@@ -81,11 +81,10 @@ public class BufferedImageIcon implements Icon {
|
||||
else {
|
||||
//System.out.println("Scaling using interpolation");
|
||||
Graphics2D g2 = (Graphics2D) g;
|
||||
AffineTransform xform = AffineTransform.getTranslateInstance(x, y);
|
||||
xform.scale(width / (double) image.getWidth(), height / (double) image.getHeight());
|
||||
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
|
||||
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
||||
g2.drawImage(image, xform, null);
|
||||
AffineTransform transform = AffineTransform.getTranslateInstance(x, y);
|
||||
transform.scale(width / (double) image.getWidth(), height / (double) image.getHeight());
|
||||
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
||||
g2.drawImage(image, transform, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -587,6 +587,7 @@ class IndexImage {
|
||||
* @deprecated Use {@link #getIndexColorModel(Image,int,int)} instead!
|
||||
* This version will be removed in a later version of the API.
|
||||
*/
|
||||
@Deprecated
|
||||
public static IndexColorModel getIndexColorModel(Image pImage, int pNumberOfColors, boolean pFast) {
|
||||
return getIndexColorModel(pImage, pNumberOfColors, pFast ? COLOR_SELECTION_FAST : COLOR_SELECTION_QUALITY);
|
||||
}
|
||||
|
||||
+22
-16
@@ -30,17 +30,26 @@
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
import org.junit.Test;
|
||||
import static java.lang.Math.min;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.image.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.BufferedImageOp;
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.awt.image.ImagingOpException;
|
||||
import java.awt.image.Raster;
|
||||
import java.awt.image.RasterOp;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* AffineTransformOpTest.
|
||||
@@ -101,6 +110,7 @@ public class AffineTransformOpTest {
|
||||
|
||||
private final int width = 30;
|
||||
private final int height = 20;
|
||||
private final double anchor = min(width, height) / 2.0;
|
||||
|
||||
@Test
|
||||
public void testGetPoint2D() {
|
||||
@@ -128,8 +138,8 @@ public class AffineTransformOpTest {
|
||||
|
||||
@Test
|
||||
public void testFilterRotateBIStandard() {
|
||||
BufferedImageOp jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, Math.min(width, height) / 2, Math.min(width, height) / 2), null);
|
||||
BufferedImageOp tmOp = new com.twelvemonkeys.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, Math.min(width, height) / 2, Math.min(width, height) / 2), null);
|
||||
BufferedImageOp jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), null);
|
||||
BufferedImageOp tmOp = new com.twelvemonkeys.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), null);
|
||||
|
||||
for (Integer type : TYPES) {
|
||||
BufferedImage image = new BufferedImage(width, height, type);
|
||||
@@ -147,8 +157,8 @@ public class AffineTransformOpTest {
|
||||
|
||||
@Test
|
||||
public void testFilterRotateBICustom() {
|
||||
BufferedImageOp jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, Math.min(width, height) / 2, Math.min(width, height) / 2), null);
|
||||
BufferedImageOp tmOp = new com.twelvemonkeys.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, Math.min(width, height) / 2, Math.min(width, height) / 2), null);
|
||||
BufferedImageOp jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), null);
|
||||
BufferedImageOp tmOp = new com.twelvemonkeys.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), null);
|
||||
|
||||
for (ImageTypeSpecifier spec : SPECS) {
|
||||
BufferedImage image = spec.createBufferedImage(width, height);
|
||||
@@ -197,8 +207,8 @@ public class AffineTransformOpTest {
|
||||
|
||||
@Test
|
||||
public void testFilterRotateRasterStandard() {
|
||||
RasterOp jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, Math.min(width, height) / 2, Math.min(width, height) / 2), null);
|
||||
RasterOp tmOp = new com.twelvemonkeys.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, Math.min(width, height) / 2, Math.min(width, height) / 2), null);
|
||||
RasterOp jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), null);
|
||||
RasterOp tmOp = new com.twelvemonkeys.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), null);
|
||||
|
||||
for (Integer type : TYPES) {
|
||||
Raster raster = new BufferedImage(width, height, type).getRaster();
|
||||
@@ -221,8 +231,6 @@ public class AffineTransformOpTest {
|
||||
fail("No result!");
|
||||
}
|
||||
else {
|
||||
System.err.println("AffineTransformOpTest.testFilterRotateRasterStandard");
|
||||
System.err.println("type: " + type);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -240,8 +248,8 @@ public class AffineTransformOpTest {
|
||||
|
||||
@Test
|
||||
public void testFilterRotateRasterCustom() {
|
||||
RasterOp jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, Math.min(width, height) / 2, Math.min(width, height) / 2), null);
|
||||
RasterOp tmOp = new com.twelvemonkeys.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, Math.min(width, height) / 2, Math.min(width, height) / 2), null);
|
||||
RasterOp jreOp = new java.awt.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), null);
|
||||
RasterOp tmOp = new com.twelvemonkeys.image.AffineTransformOp(AffineTransform.getQuadrantRotateInstance(1, anchor, anchor), null);
|
||||
|
||||
for (ImageTypeSpecifier spec : SPECS) {
|
||||
Raster raster = spec.createBufferedImage(width, height).getRaster();
|
||||
@@ -264,8 +272,6 @@ public class AffineTransformOpTest {
|
||||
fail("No result!");
|
||||
}
|
||||
else {
|
||||
System.err.println("AffineTransformOpTest.testFilterRotateRasterCustom");
|
||||
System.err.println("spec: " + spec);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>3.7.0</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>common-io</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
@@ -34,6 +34,7 @@ import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* An unsynchronized {@code ByteArrayOutputStream} implementation. This version
|
||||
@@ -42,11 +43,8 @@ import java.io.OutputStream;
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @version $Id: FastByteArrayOutputStream.java#2 $
|
||||
*/
|
||||
// TODO: Performance test of a stream impl that uses list of fixed size blocks, rather than contiguous block
|
||||
// TODO: Performance test of a stream impl that uses list of fixed size blocks, rather than contiguous block
|
||||
public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
|
||||
/** Max grow size (unless if writing more than this amount of bytes) */
|
||||
protected int maxGrowSize = 1024 * 1024; // 1 MB
|
||||
|
||||
/**
|
||||
* Creates a {@code ByteArrayOutputStream} with the given initial buffer
|
||||
* size.
|
||||
@@ -97,10 +95,8 @@ 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];
|
||||
System.arraycopy(buf, 0, newBuf, 0, count);
|
||||
buf = newBuf;
|
||||
int newSize = Math.max(buf.length << 1, pNewCount);
|
||||
buf = Arrays.copyOf(buf, newSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,10 +109,7 @@ public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
|
||||
// Non-synchronized version of toByteArray
|
||||
@Override
|
||||
public byte[] toByteArray() {
|
||||
byte[] newBuf = new byte[count];
|
||||
System.arraycopy(buf, 0, newBuf, 0, count);
|
||||
|
||||
return newBuf;
|
||||
return Arrays.copyOf(buf, count);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -65,6 +65,7 @@ import java.io.FilenameFilter;
|
||||
* @see WildcardStringParser
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated
|
||||
public class FilenameMaskFilter implements FilenameFilter {
|
||||
|
||||
// TODO: Rewrite to use regexp, or create new class
|
||||
|
||||
@@ -442,6 +442,7 @@ public class LittleEndianDataInputStream extends FilterInputStream implements Da
|
||||
* @see java.io.BufferedReader#readLine()
|
||||
* @see java.io.DataInputStream#readLine()
|
||||
*/
|
||||
@Deprecated
|
||||
public String readLine() throws IOException {
|
||||
DataInputStream ds = new DataInputStream(in);
|
||||
return ds.readLine();
|
||||
|
||||
@@ -45,39 +45,39 @@ import java.nio.ByteBuffer;
|
||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/DecoderStream.java#2 $
|
||||
*/
|
||||
public final class DecoderStream extends FilterInputStream {
|
||||
protected final ByteBuffer buffer;
|
||||
protected final Decoder decoder;
|
||||
private final ByteBuffer buffer;
|
||||
private final Decoder decoder;
|
||||
|
||||
/**
|
||||
* Creates a new decoder stream and chains it to the
|
||||
* input stream specified by the {@code pStream} argument.
|
||||
* input stream specified by the {@code stream} argument.
|
||||
* The stream will use a default decode buffer size.
|
||||
*
|
||||
* @param pStream the underlying input stream.
|
||||
* @param pDecoder the decoder that will be used to decode the underlying stream
|
||||
* @param stream the underlying input stream.
|
||||
* @param decoder the decoder that will be used to decode the underlying stream
|
||||
*
|
||||
* @see java.io.FilterInputStream#in
|
||||
*/
|
||||
public DecoderStream(final InputStream pStream, final Decoder pDecoder) {
|
||||
public DecoderStream(final InputStream stream, final Decoder decoder) {
|
||||
// TODO: Let the decoder decide preferred buffer size
|
||||
this(pStream, pDecoder, 1024);
|
||||
this(stream, decoder, 1024);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new decoder stream and chains it to the
|
||||
* input stream specified by the {@code pStream} argument.
|
||||
* input stream specified by the {@code stream} argument.
|
||||
*
|
||||
* @param pStream the underlying input stream.
|
||||
* @param pDecoder the decoder that will be used to decode the underlying stream
|
||||
* @param pBufferSize the size of the decode buffer
|
||||
* @param stream the underlying input stream.
|
||||
* @param decoder the decoder that will be used to decode the underlying stream
|
||||
* @param bufferSize the size of the decode buffer
|
||||
*
|
||||
* @see java.io.FilterInputStream#in
|
||||
*/
|
||||
public DecoderStream(final InputStream pStream, final Decoder pDecoder, final int pBufferSize) {
|
||||
super(pStream);
|
||||
public DecoderStream(final InputStream stream, final Decoder decoder, final int bufferSize) {
|
||||
super(stream);
|
||||
|
||||
decoder = pDecoder;
|
||||
buffer = ByteBuffer.allocate(pBufferSize);
|
||||
this.decoder = decoder;
|
||||
buffer = ByteBuffer.allocate(bufferSize);
|
||||
buffer.flip();
|
||||
}
|
||||
|
||||
@@ -95,15 +95,15 @@ public final class DecoderStream extends FilterInputStream {
|
||||
return buffer.get() & 0xff;
|
||||
}
|
||||
|
||||
public int read(final byte pBytes[], final int pOffset, final int pLength) throws IOException {
|
||||
if (pBytes == null) {
|
||||
public int read(final byte[] bytes, final int offset, final int length) throws IOException {
|
||||
if (bytes == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
else if ((pOffset < 0) || (pOffset > pBytes.length) || (pLength < 0) ||
|
||||
((pOffset + pLength) > pBytes.length) || ((pOffset + pLength) < 0)) {
|
||||
throw new IndexOutOfBoundsException("bytes.length=" + pBytes.length + " offset=" + pOffset + " length=" + pLength);
|
||||
else if ((offset < 0) || (offset > bytes.length) || (length < 0) ||
|
||||
((offset + length) > bytes.length) || ((offset + length) < 0)) {
|
||||
throw new IndexOutOfBoundsException("bytes.length=" + bytes.length + " offset=" + offset + " length=" + length);
|
||||
}
|
||||
else if (pLength == 0) {
|
||||
else if (length == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -114,11 +114,11 @@ public final class DecoderStream extends FilterInputStream {
|
||||
}
|
||||
}
|
||||
|
||||
// Read until we have read pLength bytes, or have reached EOF
|
||||
// Read until we have read length bytes, or have reached EOF
|
||||
int count = 0;
|
||||
int off = pOffset;
|
||||
int off = offset;
|
||||
|
||||
while (pLength > count) {
|
||||
while (length > count) {
|
||||
if (!buffer.hasRemaining()) {
|
||||
if (fill() < 0) {
|
||||
break;
|
||||
@@ -126,8 +126,8 @@ public final class DecoderStream extends FilterInputStream {
|
||||
}
|
||||
|
||||
// Copy as many bytes as possible
|
||||
int dstLen = Math.min(pLength - count, buffer.remaining());
|
||||
buffer.get(pBytes, off, dstLen);
|
||||
int dstLen = Math.min(length - count, buffer.remaining());
|
||||
buffer.get(bytes, off, dstLen);
|
||||
|
||||
// Update offset (rest)
|
||||
off += dstLen;
|
||||
@@ -139,7 +139,7 @@ public final class DecoderStream extends FilterInputStream {
|
||||
return count;
|
||||
}
|
||||
|
||||
public long skip(final long pLength) throws IOException {
|
||||
public long skip(final long length) throws IOException {
|
||||
// End of file?
|
||||
if (!buffer.hasRemaining()) {
|
||||
if (fill() < 0) {
|
||||
@@ -147,10 +147,10 @@ public final class DecoderStream extends FilterInputStream {
|
||||
}
|
||||
}
|
||||
|
||||
// Skip until we have skipped pLength bytes, or have reached EOF
|
||||
// Skip until we have skipped length bytes, or have reached EOF
|
||||
long total = 0;
|
||||
|
||||
while (total < pLength) {
|
||||
while (total < length) {
|
||||
if (!buffer.hasRemaining()) {
|
||||
if (fill() < 0) {
|
||||
break;
|
||||
@@ -158,7 +158,7 @@ public final class DecoderStream extends FilterInputStream {
|
||||
}
|
||||
|
||||
// NOTE: Skipped can never be more than avail, which is an int, so the cast is safe
|
||||
int skipped = (int) Math.min(pLength - total, buffer.remaining());
|
||||
int skipped = (int) Math.min(length - total, buffer.remaining());
|
||||
buffer.position(buffer.position() + skipped);
|
||||
total += skipped;
|
||||
}
|
||||
@@ -174,7 +174,7 @@ public final class DecoderStream extends FilterInputStream {
|
||||
*
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
protected int fill() throws IOException {
|
||||
private int fill() throws IOException {
|
||||
buffer.clear();
|
||||
int read = decoder.decode(in, buffer);
|
||||
|
||||
|
||||
@@ -45,41 +45,39 @@ import java.nio.ByteBuffer;
|
||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/EncoderStream.java#2 $
|
||||
*/
|
||||
public final class EncoderStream extends FilterOutputStream {
|
||||
// TODO: This class need a test case ASAP!!!
|
||||
|
||||
protected final Encoder encoder;
|
||||
private final Encoder encoder;
|
||||
private final boolean flushOnWrite;
|
||||
|
||||
protected final ByteBuffer buffer;
|
||||
private final ByteBuffer buffer;
|
||||
|
||||
/**
|
||||
* Creates an output stream filter built on top of the specified
|
||||
* underlying output stream.
|
||||
*
|
||||
* @param pStream the underlying output stream
|
||||
* @param pEncoder the encoder to use
|
||||
* @param stream the underlying output stream
|
||||
* @param encoder the encoder to use
|
||||
*/
|
||||
public EncoderStream(final OutputStream pStream, final Encoder pEncoder) {
|
||||
this(pStream, pEncoder, false);
|
||||
public EncoderStream(final OutputStream stream, final Encoder encoder) {
|
||||
this(stream, encoder, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an output stream filter built on top of the specified
|
||||
* underlying output stream.
|
||||
*
|
||||
* @param pStream the underlying output stream
|
||||
* @param pEncoder the encoder to use
|
||||
* @param pFlushOnWrite if {@code true}, calls to the byte-array
|
||||
* @param stream the underlying output stream
|
||||
* @param encoder the encoder to use
|
||||
* @param flushOnWrite if {@code true}, calls to the byte-array
|
||||
* {@code write} methods will automatically flush the buffer.
|
||||
*/
|
||||
public EncoderStream(final OutputStream pStream, final Encoder pEncoder, final boolean pFlushOnWrite) {
|
||||
super(pStream);
|
||||
public EncoderStream(final OutputStream stream, final Encoder encoder, final boolean flushOnWrite) {
|
||||
super(stream);
|
||||
|
||||
encoder = pEncoder;
|
||||
flushOnWrite = pFlushOnWrite;
|
||||
this.encoder = encoder;
|
||||
this.flushOnWrite = flushOnWrite;
|
||||
|
||||
buffer = ByteBuffer.allocate(1024);
|
||||
buffer.flip();
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
@@ -104,33 +102,33 @@ public final class EncoderStream extends FilterOutputStream {
|
||||
}
|
||||
}
|
||||
|
||||
public final void write(final byte[] pBytes) throws IOException {
|
||||
write(pBytes, 0, pBytes.length);
|
||||
public void write(final byte[] bytes) throws IOException {
|
||||
write(bytes, 0, bytes.length);
|
||||
}
|
||||
|
||||
// TODO: Verify that this works for the general case (it probably won't)...
|
||||
// TODO: We might need a way to explicitly flush the encoder, or specify
|
||||
// that the encoder can't buffer. In that case, the encoder should probably
|
||||
// tell the EncoderStream how large buffer it prefers...
|
||||
public void write(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
|
||||
if (!flushOnWrite && pLength < buffer.remaining()) {
|
||||
// tell the EncoderStream how large buffer it prefers...
|
||||
public void write(final byte[] values, final int offset, final int length) throws IOException {
|
||||
if (!flushOnWrite && length < buffer.remaining()) {
|
||||
// Buffer data
|
||||
buffer.put(pBytes, pOffset, pLength);
|
||||
buffer.put(values, offset, length);
|
||||
}
|
||||
else {
|
||||
// Encode data already in the buffer
|
||||
encodeBuffer();
|
||||
|
||||
// Encode rest without buffering
|
||||
encoder.encode(out, ByteBuffer.wrap(pBytes, pOffset, pLength));
|
||||
encoder.encode(out, ByteBuffer.wrap(values, offset, length));
|
||||
}
|
||||
}
|
||||
|
||||
public void write(final int pByte) throws IOException {
|
||||
public void write(final int value) throws IOException {
|
||||
if (!buffer.hasRemaining()) {
|
||||
encodeBuffer(); // Resets bufferPos to 0
|
||||
}
|
||||
|
||||
buffer.put((byte) pByte);
|
||||
buffer.put((byte) value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,9 @@
|
||||
|
||||
package com.twelvemonkeys.xml;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.io.Writer;
|
||||
|
||||
import org.w3c.dom.DOMConfiguration;
|
||||
import org.w3c.dom.DOMImplementationList;
|
||||
import org.w3c.dom.Document;
|
||||
@@ -38,9 +41,6 @@ import org.w3c.dom.ls.DOMImplementationLS;
|
||||
import org.w3c.dom.ls.LSOutput;
|
||||
import org.w3c.dom.ls.LSSerializer;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.io.Writer;
|
||||
|
||||
/**
|
||||
* {@code DOMImplementationLS} backed implementation.
|
||||
*
|
||||
@@ -88,17 +88,6 @@ public final class DOMSerializer {
|
||||
output.setCharacterStream(pStream);
|
||||
}
|
||||
|
||||
/*
|
||||
// TODO: Is it useful?
|
||||
public void setNewLine(final String pNewLine) {
|
||||
serializer.setNewLine(pNewLine);
|
||||
}
|
||||
|
||||
public String getNewLine() {
|
||||
return serializer.getNewLine();
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Specifies wether the serializer should use indentation and optimize for
|
||||
* readability.
|
||||
@@ -169,13 +158,7 @@ public final class DOMSerializer {
|
||||
try {
|
||||
return DOMImplementationRegistry.newInstance();
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
catch (InstantiationException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
catch (IllegalAccessException e) {
|
||||
catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,16 +30,23 @@
|
||||
|
||||
package com.twelvemonkeys.xml;
|
||||
|
||||
import com.twelvemonkeys.lang.StringUtil;
|
||||
import org.w3c.dom.*;
|
||||
import org.xml.sax.SAXException;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import java.io.*;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Date;
|
||||
|
||||
import org.w3c.dom.*;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import com.twelvemonkeys.lang.StringUtil;
|
||||
|
||||
/**
|
||||
* XMLSerializer
|
||||
@@ -290,7 +297,7 @@ public class XMLSerializer {
|
||||
}
|
||||
|
||||
private static int appendAndEscape(final String pString, int pStart, final int pEnd, final StringBuilder pBuilder, final String pEntity) {
|
||||
pBuilder.append(pString.substring(pStart, pEnd));
|
||||
pBuilder.append(pString, pStart, pEnd);
|
||||
pBuilder.append(pEntity);
|
||||
return pEnd + 1;
|
||||
}
|
||||
@@ -527,8 +534,7 @@ public class XMLSerializer {
|
||||
builder = factory.newDocumentBuilder();
|
||||
}
|
||||
catch (ParserConfigurationException e) {
|
||||
//noinspection ThrowableInstanceNeverThrown BOGUS
|
||||
throw (IOException) new IOException(e.getMessage()).initCause(e);
|
||||
throw new IOException(e);
|
||||
}
|
||||
|
||||
DOMImplementation dom = builder.getDOMImplementation();
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 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.io.enc;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class DecoderStreamTest {
|
||||
|
||||
private final Random rng = new Random(5467809876546L);
|
||||
|
||||
private byte[] createData(final int length) {
|
||||
byte[] data = new byte[length];
|
||||
rng.nextBytes(data);
|
||||
return data;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecodeSingleBytes() throws IOException {
|
||||
byte[] data = createData(1327);
|
||||
|
||||
InputStream source = new ByteArrayInputStream(data);
|
||||
try (InputStream stream = new DecoderStream(source, new NullDecoder())) {
|
||||
for (byte datum : data) {
|
||||
int read = stream.read();
|
||||
assertNotEquals(-1, read);
|
||||
assertEquals(datum, (byte) read);
|
||||
}
|
||||
|
||||
assertEquals(-1, stream.read());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecodeArray() throws IOException {
|
||||
int length = 793;
|
||||
byte[] data = createData(length * 10);
|
||||
|
||||
InputStream source = new ByteArrayInputStream(data);
|
||||
byte[] result = new byte[477];
|
||||
|
||||
try (InputStream stream = new DecoderStream(source, new NullDecoder())) {
|
||||
int dataOffset = 0;
|
||||
while (dataOffset < data.length) {
|
||||
int count = stream.read(result);
|
||||
|
||||
assertFalse(count <= 0);
|
||||
assertArrayEquals(Arrays.copyOfRange(data, dataOffset, dataOffset + count), Arrays.copyOfRange(result, 0, count));
|
||||
|
||||
dataOffset += count;
|
||||
}
|
||||
|
||||
assertEquals(-1, stream.read());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecodeArrayOffset() throws IOException {
|
||||
int length = 793;
|
||||
byte[] data = createData(length * 10);
|
||||
|
||||
InputStream source = new ByteArrayInputStream(data);
|
||||
byte[] result = new byte[477];
|
||||
|
||||
try (InputStream stream = new DecoderStream(source, new NullDecoder())) {
|
||||
int dataOffset = 0;
|
||||
while (dataOffset < data.length) {
|
||||
int resultOffset = dataOffset % result.length;
|
||||
int count = stream.read(result, resultOffset, result.length - resultOffset);
|
||||
|
||||
assertFalse(count <= 0);
|
||||
assertArrayEquals(Arrays.copyOfRange(data, dataOffset + resultOffset, dataOffset + count), Arrays.copyOfRange(result, resultOffset, count));
|
||||
|
||||
dataOffset += count;
|
||||
}
|
||||
|
||||
assertEquals(-1, stream.read());
|
||||
}
|
||||
}
|
||||
|
||||
private static final class NullDecoder implements Decoder {
|
||||
@Override
|
||||
public int decode(InputStream stream, ByteBuffer buffer) throws IOException {
|
||||
int read = stream.read(buffer.array(), buffer.arrayOffset(), buffer.remaining());
|
||||
|
||||
if (read > 0) {
|
||||
// Set position, should be equivalent to using buffer.put(stream.read()) until EOF or buffer full
|
||||
buffer.position(read);
|
||||
}
|
||||
|
||||
return read;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 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.io.enc;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
||||
public class EncoderStreamTest {
|
||||
|
||||
private final Random rng = new Random(5467809876546L);
|
||||
|
||||
private byte[] createData(final int length) {
|
||||
byte[] data = new byte[length];
|
||||
rng.nextBytes(data);
|
||||
return data;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeSingleBytes() throws IOException {
|
||||
byte[] data = createData(1327);
|
||||
|
||||
ByteArrayOutputStream result = new ByteArrayOutputStream();
|
||||
try (OutputStream stream = new EncoderStream(result, new NullEncoder())) {
|
||||
for (byte datum : data) {
|
||||
stream.write(datum);
|
||||
}
|
||||
}
|
||||
|
||||
assertArrayEquals(data, result.toByteArray());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeArray() throws IOException {
|
||||
byte[] data = createData(1793);
|
||||
|
||||
ByteArrayOutputStream result = new ByteArrayOutputStream();
|
||||
try (OutputStream stream = new EncoderStream(result, new NullEncoder())) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
stream.write(data);
|
||||
}
|
||||
}
|
||||
|
||||
byte[] encoded = result.toByteArray();
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
assertArrayEquals(data, Arrays.copyOfRange(encoded, i * data.length, (i + 1) * data.length));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeArrayOffset() throws IOException {
|
||||
byte[] data = createData(87);
|
||||
|
||||
ByteArrayOutputStream result = new ByteArrayOutputStream();
|
||||
try (OutputStream stream = new EncoderStream(result, new NullEncoder())) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
stream.write(data, 13, 59);
|
||||
}
|
||||
}
|
||||
|
||||
byte[] original = Arrays.copyOfRange(data, 13, 13 + 59);
|
||||
byte[] encoded = result.toByteArray();
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
assertArrayEquals(original, Arrays.copyOfRange(encoded, i * original.length, (i + 1) * original.length));
|
||||
}
|
||||
}
|
||||
|
||||
private static final class NullEncoder implements Encoder {
|
||||
@Override
|
||||
public void encode(OutputStream stream, ByteBuffer buffer) throws IOException {
|
||||
stream.write(buffer.array(), buffer.arrayOffset(), buffer.remaining());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>3.7.0</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>common-lang</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
@@ -770,6 +770,7 @@ public final class StringUtil {
|
||||
*/
|
||||
|
||||
/*public*/
|
||||
@Deprecated
|
||||
static String formatNumber(long pNum, int pLen) throws IllegalArgumentException {
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
@@ -1464,6 +1465,7 @@ public final class StringUtil {
|
||||
*/
|
||||
|
||||
/*public*/
|
||||
@Deprecated
|
||||
static String removeSubstring(final String pSource, final char pBeginBoundaryChar, final char pEndBoundaryChar, final int pOffset) {
|
||||
StringBuilder filteredString = new StringBuilder();
|
||||
boolean insideDemarcatedArea = false;
|
||||
|
||||
@@ -157,6 +157,7 @@ public class Time {
|
||||
* @see #parseTime(String)
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated
|
||||
public String toString(String pFormatStr) {
|
||||
TimeFormat tf = new TimeFormat(pFormatStr);
|
||||
|
||||
@@ -174,6 +175,7 @@ public class Time {
|
||||
* @see #toString(String)
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated
|
||||
public static Time parseTime(String pStr) {
|
||||
TimeFormat tf = TimeFormat.getInstance();
|
||||
|
||||
|
||||
+1
@@ -111,6 +111,7 @@ import java.io.PrintStream;
|
||||
* @author <a href="mailto:eirik.torske@iconmedialab.no">Eirik Torske</a>
|
||||
* @deprecated Will probably be removed in the near future
|
||||
*/
|
||||
@Deprecated
|
||||
public class WildcardStringParser {
|
||||
// TODO: Get rid of this class
|
||||
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys</groupId>
|
||||
<artifactId>twelvemonkeys</artifactId>
|
||||
<version>3.7.0</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys</groupId>
|
||||
<artifactId>twelvemonkeys</artifactId>
|
||||
<version>3.7.0</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>com.twelvemonkeys.contrib</groupId>
|
||||
<artifactId>contrib</artifactId>
|
||||
|
||||
@@ -30,9 +30,14 @@ import static com.twelvemonkeys.contrib.tiff.TIFFUtilities.applyOrientation;
|
||||
public class EXIFUtilities {
|
||||
/**
|
||||
* Reads image and metadata, applies Exif orientation to image, and returns everything as an {@code IIOImage}.
|
||||
* The returned {@code IIOImage} will always contain an image and no raster, and
|
||||
* the {@code RenderedImage} may be safely cast to a {@code BufferedImage}.
|
||||
*
|
||||
* If no registered {@code ImageReader} claims to be able to read the input, {@code null} is returned.
|
||||
*
|
||||
* @param input a {@code URL}
|
||||
* @return an {@code IIOImage} containing the correctly oriented image and metadata including rotation info.
|
||||
* @return an {@code IIOImage} containing the correctly oriented image and metadata including rotation info, or
|
||||
* {@code null}.
|
||||
* @throws IOException if an error occurs during reading.
|
||||
*/
|
||||
public static IIOImage readWithOrientation(final URL input) throws IOException {
|
||||
@@ -43,9 +48,14 @@ public class EXIFUtilities {
|
||||
|
||||
/**
|
||||
* Reads image and metadata, applies Exif orientation to image, and returns everything as an {@code IIOImage}.
|
||||
* The returned {@code IIOImage} will always contain an image and no raster, and
|
||||
* the {@code RenderedImage} may be safely cast to a {@code BufferedImage}.
|
||||
*
|
||||
* If no registered {@code ImageReader} claims to be able to read the input, {@code null} is returned.
|
||||
*
|
||||
* @param input an {@code InputStream}
|
||||
* @return an {@code IIOImage} containing the correctly oriented image and metadata including rotation info.
|
||||
* @return an {@code IIOImage} containing the correctly oriented image and metadata including rotation info, or
|
||||
* {@code null}.
|
||||
* @throws IOException if an error occurs during reading.
|
||||
*/
|
||||
public static IIOImage readWithOrientation(final InputStream input) throws IOException {
|
||||
@@ -56,9 +66,14 @@ public class EXIFUtilities {
|
||||
|
||||
/**
|
||||
* Reads image and metadata, applies Exif orientation to image, and returns everything as an {@code IIOImage}.
|
||||
* The returned {@code IIOImage} will always contain an image and no raster, and
|
||||
* the {@code RenderedImage} may be safely cast to a {@code BufferedImage}.
|
||||
*
|
||||
* If no registered {@code ImageReader} claims to be able to read the input, {@code null} is returned.
|
||||
*
|
||||
* @param input a {@code File}
|
||||
* @return an {@code IIOImage} containing the correctly oriented image and metadata including rotation info.
|
||||
* @return an {@code IIOImage} containing the correctly oriented image and metadata including rotation info or
|
||||
* {@code null}.
|
||||
* @throws IOException if an error occurs during reading.
|
||||
*/
|
||||
public static IIOImage readWithOrientation(final File input) throws IOException {
|
||||
@@ -69,9 +84,14 @@ public class EXIFUtilities {
|
||||
|
||||
/**
|
||||
* Reads image and metadata, applies Exif orientation to image, and returns everything as an {@code IIOImage}.
|
||||
* The returned {@code IIOImage} will always contain an image and no raster, and
|
||||
* the {@code RenderedImage} may be safely cast to a {@code BufferedImage}.
|
||||
*
|
||||
* If no registered {@code ImageReader} claims to be able to read the input, {@code null} is returned.
|
||||
*
|
||||
* @param input an {@code ImageInputStream}
|
||||
* @return an {@code IIOImage} containing the correctly oriented image and metadata including rotation info.
|
||||
* @return an {@code IIOImage} containing the correctly oriented image and metadata including rotation info, or
|
||||
* {@code null}.
|
||||
* @throws IOException if an error occurs during reading.
|
||||
*/
|
||||
public static IIOImage readWithOrientation(final ImageInputStream input) throws IOException {
|
||||
|
||||
@@ -31,8 +31,9 @@
|
||||
package com.twelvemonkeys.contrib.tiff;
|
||||
|
||||
import com.twelvemonkeys.contrib.tiff.TIFFUtilities.TIFFExtension;
|
||||
import com.twelvemonkeys.imageio.plugins.tiff.TIFFMedataFormat;
|
||||
import com.twelvemonkeys.imageio.plugins.tiff.TIFFImageMetadataFormat;
|
||||
import com.twelvemonkeys.io.FileUtil;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.w3c.dom.Node;
|
||||
@@ -154,7 +155,7 @@ public class TIFFUtilitiesTest {
|
||||
reader.setInput(checkTest1);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Node metaData = reader.getImageMetadata(i)
|
||||
.getAsTree(TIFFMedataFormat.SUN_NATIVE_IMAGE_METADATA_FORMAT_NAME);
|
||||
.getAsTree(TIFFImageMetadataFormat.SUN_NATIVE_IMAGE_METADATA_FORMAT_NAME);
|
||||
short orientation = ((Number) expression.evaluate(metaData, XPathConstants.NUMBER)).shortValue();
|
||||
Assert.assertEquals(orientation, TIFFExtension.ORIENTATION_RIGHTTOP);
|
||||
}
|
||||
@@ -171,7 +172,7 @@ public class TIFFUtilitiesTest {
|
||||
reader.setInput(checkTest2);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Node metaData = reader.getImageMetadata(i)
|
||||
.getAsTree(TIFFMedataFormat.SUN_NATIVE_IMAGE_METADATA_FORMAT_NAME);
|
||||
.getAsTree(TIFFImageMetadataFormat.SUN_NATIVE_IMAGE_METADATA_FORMAT_NAME);
|
||||
short orientation = ((Number) expression.evaluate(metaData, XPathConstants.NUMBER)).shortValue();
|
||||
Assert.assertEquals(orientation, i == 1
|
||||
? TIFFExtension.ORIENTATION_BOTRIGHT
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.7.0</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-batik</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: Batik Plugin</name>
|
||||
@@ -27,9 +27,9 @@
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<systemPropertyVariables>
|
||||
<com.twelvemonkeys.imageio.plugins.svg.allowexternalresources>
|
||||
<com.twelvemonkeys.imageio.plugins.svg.allowExternalResources>
|
||||
true
|
||||
</com.twelvemonkeys.imageio.plugins.svg.allowexternalresources>
|
||||
</com.twelvemonkeys.imageio.plugins.svg.allowExternalResources>
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
</plugin>
|
||||
@@ -48,6 +48,13 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.11.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.xmlgraphics</groupId>
|
||||
<artifactId>batik-rasterizer-ext</artifactId>
|
||||
@@ -68,13 +75,6 @@
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.xmlgraphics</groupId>
|
||||
<artifactId>xmlgraphics-commons</artifactId>
|
||||
<version>2.2</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.xmlgraphics</groupId>
|
||||
<artifactId>batik-anim</artifactId>
|
||||
@@ -98,7 +98,7 @@
|
||||
<!--
|
||||
There seems to be some weirdness in the
|
||||
Batik/FOP poms (Batik depends on FOP 0.20-5) that screws things up,
|
||||
making everything end up depending on Batik 1.5, not 1.6
|
||||
making everything end up depending on Batik 1.5, not the specified version
|
||||
-->
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
|
||||
+103
-95
@@ -34,10 +34,10 @@ 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;
|
||||
@@ -45,11 +45,14 @@ import org.apache.batik.gvt.GraphicsNode;
|
||||
import org.apache.batik.gvt.renderer.ConcreteImageRendererFactory;
|
||||
import org.apache.batik.gvt.renderer.ImageRenderer;
|
||||
import org.apache.batik.gvt.renderer.ImageRendererFactory;
|
||||
import org.apache.batik.transcoder.*;
|
||||
import org.apache.batik.transcoder.SVGAbstractTranscoder;
|
||||
import org.apache.batik.transcoder.TranscoderException;
|
||||
import org.apache.batik.transcoder.TranscoderInput;
|
||||
import org.apache.batik.transcoder.TranscoderOutput;
|
||||
import org.apache.batik.transcoder.TranscodingHints;
|
||||
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;
|
||||
@@ -59,10 +62,8 @@ import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import java.awt.*;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Dimension2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.geom.*;
|
||||
import java.awt.image.*;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
@@ -76,12 +77,13 @@ import java.util.Map;
|
||||
* @author Harald Kuhr
|
||||
* @author Inpspired by code from the Batik Team
|
||||
* @version $Id: $
|
||||
* @see <A href="http://www.mail-archive.com/batik-dev@xml.apache.org/msg00992.html">batik-dev</A>
|
||||
* @see <a href="http://www.mail-archive.com/batik-dev@xml.apache.org/msg00992.html">batik-dev</a>
|
||||
*/
|
||||
public class SVGImageReader extends ImageReaderBase {
|
||||
|
||||
final static boolean DEFAULT_ALLOW_EXTERNAL_RESOURCES =
|
||||
"true".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.imageio.plugins.svg.allowexternalresources"));
|
||||
"true".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.imageio.plugins.svg.allowExternalResources",
|
||||
System.getProperty("com.twelvemonkeys.imageio.plugins.svg.allowexternalresources")));
|
||||
|
||||
private Rasterizer rasterizer;
|
||||
private boolean allowExternalResources = DEFAULT_ALLOW_EXTERNAL_RESOURCES;
|
||||
@@ -132,6 +134,7 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
// Set ImageReadParams as hints
|
||||
// Note: The cast to Map invokes a different method that preserves
|
||||
// unset defaults, DO NOT REMOVE!
|
||||
//noinspection rawtypes
|
||||
rasterizer.setTranscodingHints((Map) paramsToHints(svgParam));
|
||||
}
|
||||
|
||||
@@ -146,29 +149,23 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
BufferedImage destination = getDestination(pParam, getImageTypes(pIndex), size.width, size.height);
|
||||
|
||||
// Read in the image, using the Batik Transcoder
|
||||
processImageStarted(pIndex);
|
||||
|
||||
BufferedImage image = rasterizer.getImage();
|
||||
|
||||
Graphics2D g = destination.createGraphics();
|
||||
try {
|
||||
processImageStarted(pIndex);
|
||||
|
||||
BufferedImage image = rasterizer.getImage();
|
||||
|
||||
Graphics2D g = destination.createGraphics();
|
||||
try {
|
||||
g.setComposite(AlphaComposite.Src);
|
||||
g.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
|
||||
g.drawImage(image, 0, 0, null); // TODO: Dest offset?
|
||||
}
|
||||
finally {
|
||||
g.dispose();
|
||||
}
|
||||
|
||||
processImageComplete();
|
||||
|
||||
return destination;
|
||||
g.setComposite(AlphaComposite.Src);
|
||||
g.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
|
||||
g.drawImage(image, 0, 0, null); // TODO: Dest offset?
|
||||
}
|
||||
catch (TranscoderException e) {
|
||||
Throwable cause = unwrapException(e);
|
||||
throw new IIOException(cause.getMessage(), cause);
|
||||
finally {
|
||||
g.dispose();
|
||||
}
|
||||
|
||||
processImageComplete();
|
||||
|
||||
return destination;
|
||||
}
|
||||
|
||||
private static Throwable unwrapException(TranscoderException ex) {
|
||||
@@ -183,11 +180,11 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
|
||||
// Set dimensions
|
||||
Dimension size = pParam.getSourceRenderSize();
|
||||
Dimension origSize = new Dimension(getWidth(0), getHeight(0));
|
||||
Rectangle viewBox = rasterizer.getViewBox();
|
||||
if (size == null) {
|
||||
// SVG is not a pixel based format, but we'll scale it, according to
|
||||
// the subsampling for compatibility
|
||||
size = getSourceRenderSizeFromSubsamping(pParam, origSize);
|
||||
size = getSourceRenderSizeFromSubsamping(pParam, viewBox.getSize());
|
||||
}
|
||||
|
||||
if (size != null) {
|
||||
@@ -207,8 +204,8 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
}
|
||||
else {
|
||||
// Need to resize here...
|
||||
double xScale = size.getWidth() / origSize.getWidth();
|
||||
double yScale = size.getHeight() / origSize.getHeight();
|
||||
double xScale = size.getWidth() / viewBox.getWidth();
|
||||
double yScale = size.getHeight() / viewBox.getHeight();
|
||||
|
||||
hints.put(ImageTranscoder.KEY_WIDTH, (float) (region.getWidth() * xScale));
|
||||
hints.put(ImageTranscoder.KEY_HEIGHT, (float) (region.getHeight() * yScale));
|
||||
@@ -216,7 +213,7 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
}
|
||||
else if (size != null) {
|
||||
// Allow non-uniform scaling
|
||||
hints.put(ImageTranscoder.KEY_AOI, new Rectangle(origSize));
|
||||
hints.put(ImageTranscoder.KEY_AOI, viewBox);
|
||||
}
|
||||
|
||||
// Background color
|
||||
@@ -231,7 +228,7 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
private Dimension getSourceRenderSizeFromSubsamping(ImageReadParam pParam, Dimension pOrigSize) {
|
||||
if (pParam.getSourceXSubsampling() > 1 || pParam.getSourceYSubsampling() > 1) {
|
||||
return new Dimension((int) (pOrigSize.width / (float) pParam.getSourceXSubsampling()),
|
||||
(int) (pOrigSize.height / (float) pParam.getSourceYSubsampling()));
|
||||
(int) (pOrigSize.height / (float) pParam.getSourceYSubsampling()));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -242,25 +239,16 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
|
||||
public int getWidth(int pIndex) throws IOException {
|
||||
checkBounds(pIndex);
|
||||
try {
|
||||
return rasterizer.getDefaultWidth();
|
||||
}
|
||||
catch (TranscoderException e) {
|
||||
throw new IIOException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
return rasterizer.getDefaultWidth();
|
||||
}
|
||||
|
||||
public int getHeight(int pIndex) throws IOException {
|
||||
checkBounds(pIndex);
|
||||
try {
|
||||
return rasterizer.getDefaultHeight();
|
||||
}
|
||||
catch (TranscoderException e) {
|
||||
throw new IIOException(e.getMessage(), e);
|
||||
}
|
||||
return rasterizer.getDefaultHeight();
|
||||
}
|
||||
|
||||
public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IOException {
|
||||
public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) {
|
||||
return Collections.singleton(ImageTypeSpecifier.createFromRenderedImage(rasterizer.createImage(1, 1))).iterator();
|
||||
}
|
||||
|
||||
@@ -271,12 +259,11 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
* and needs major refactoring!
|
||||
* </p>
|
||||
*/
|
||||
private class Rasterizer extends SVGAbstractTranscoder /*ImageTranscoder*/ {
|
||||
|
||||
private class Rasterizer extends SVGAbstractTranscoder {
|
||||
private BufferedImage image;
|
||||
private TranscoderInput transcoderInput;
|
||||
private float defaultWidth;
|
||||
private float defaultHeight;
|
||||
private final Rectangle2D viewBox = new Rectangle2D.Float();
|
||||
private final Dimension defaultSize = new Dimension();
|
||||
private boolean initialized = false;
|
||||
private SVGOMDocument document;
|
||||
private String uri;
|
||||
@@ -289,7 +276,7 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
}
|
||||
|
||||
// This is cheating... We don't fully transcode after all
|
||||
protected void transcode(Document document, final String uri, final TranscoderOutput output) throws TranscoderException {
|
||||
protected void transcode(Document document, final String uri, final TranscoderOutput output) {
|
||||
// Sets up root, curTxf & curAoi
|
||||
// ----
|
||||
if (document != null) {
|
||||
@@ -337,54 +324,66 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
// ----
|
||||
SVGSVGElement rootElement = svgDoc.getRootElement();
|
||||
|
||||
// get the 'width' and 'height' attributes of the SVG document
|
||||
UnitProcessor.Context uctx
|
||||
= UnitProcessor.createContext(ctx, rootElement);
|
||||
// Get the viewBox
|
||||
String viewBoxStr = rootElement.getAttributeNS(null, SVGConstants.SVG_VIEW_BOX_ATTRIBUTE);
|
||||
if (viewBoxStr.length() != 0) {
|
||||
float[] rect = ViewBox.parseViewBoxAttribute(rootElement, viewBoxStr, null);
|
||||
viewBox.setFrame(rect[0], rect[1], rect[2], rect[3]);
|
||||
}
|
||||
|
||||
// Get the 'width' and 'height' attributes of the SVG document
|
||||
double width = 0;
|
||||
double height = 0;
|
||||
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);
|
||||
width = UnitProcessor.svgToUserSpace(widthStr, SVGConstants.SVG_WIDTH_ATTRIBUTE, UnitProcessor.HORIZONTAL_LENGTH, uctx);
|
||||
}
|
||||
if(!StringUtil.isEmpty(heightStr)){
|
||||
defaultHeight = UnitProcessor.svgToUserSpace(heightStr, SVGConstants.SVG_HEIGHT_ATTRIBUTE, UnitProcessor.VERTICAL_LENGTH, uctx);
|
||||
if (!StringUtil.isEmpty(heightStr)) {
|
||||
height = UnitProcessor.svgToUserSpace(heightStr, SVGConstants.SVG_HEIGHT_ATTRIBUTE, UnitProcessor.VERTICAL_LENGTH, uctx);
|
||||
}
|
||||
|
||||
boolean hasWidth = defaultWidth > 0.0;
|
||||
boolean hasHeight = defaultHeight > 0.0;
|
||||
boolean hasWidth = width > 0.0;
|
||||
boolean hasHeight = height > 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 (!viewBox.isEmpty()) {
|
||||
// If one dimension is given, calculate other by aspect ratio in viewBox
|
||||
if (hasWidth) {
|
||||
defaultHeight = defaultWidth * rect[3] / rect[2];
|
||||
height = width * viewBox.getHeight() / viewBox.getWidth();
|
||||
}
|
||||
else if (hasHeight) {
|
||||
defaultWidth = defaultHeight * rect[2] / rect[3];
|
||||
width = height * viewBox.getWidth() / viewBox.getHeight();
|
||||
}
|
||||
else {
|
||||
defaultWidth = rect[2];
|
||||
defaultHeight = rect[3];
|
||||
// ...or use viewBox if no dimension is given
|
||||
width = viewBox.getWidth();
|
||||
height = viewBox.getHeight();
|
||||
}
|
||||
}
|
||||
else {
|
||||
// No viewBox, just assume square size
|
||||
if (hasHeight) {
|
||||
defaultWidth = defaultHeight;
|
||||
width = height;
|
||||
}
|
||||
else if (hasWidth) {
|
||||
defaultHeight = defaultWidth;
|
||||
height = width;
|
||||
}
|
||||
else {
|
||||
// fallback to batik default sizes
|
||||
defaultWidth = 400;
|
||||
defaultHeight = 400;
|
||||
// ...or finally fall back to Batik default sizes
|
||||
width = 400;
|
||||
height = 400;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We now have a size, in the rare case we don't have a viewBox; set it to this size
|
||||
defaultSize.setSize(width, height);
|
||||
if (viewBox.isEmpty()) {
|
||||
viewBox.setRect(0, 0, width, height);
|
||||
}
|
||||
|
||||
// Hack to work around exception above
|
||||
if (root != null) {
|
||||
gvtRoot = root;
|
||||
@@ -397,7 +396,7 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
ctx = null;
|
||||
}
|
||||
|
||||
private BufferedImage readImage() throws TranscoderException {
|
||||
private BufferedImage readImage() throws IOException {
|
||||
init();
|
||||
|
||||
if (abortRequested()) {
|
||||
@@ -422,7 +421,8 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
}
|
||||
|
||||
if (gvtRoot == null) {
|
||||
throw exception;
|
||||
Throwable cause = unwrapException(exception);
|
||||
throw new IIOException(cause.getMessage(), cause);
|
||||
}
|
||||
}
|
||||
ctx = context;
|
||||
@@ -440,7 +440,7 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
|
||||
|
||||
// ----
|
||||
setImageSize(defaultWidth, defaultHeight);
|
||||
setImageSize(defaultSize.width, defaultSize.height);
|
||||
|
||||
if (abortRequested()) {
|
||||
processReadAborted();
|
||||
@@ -454,18 +454,17 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
|
||||
try {
|
||||
Px = ViewBox.getViewTransform(ref, root, width, height, null);
|
||||
|
||||
}
|
||||
catch (BridgeException ex) {
|
||||
throw new TranscoderException(ex);
|
||||
throw new IIOException(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
if (Px.isIdentity() && (width != defaultWidth || height != defaultHeight)) {
|
||||
if (Px.isIdentity() && (width != defaultSize.width || height != defaultSize.height)) {
|
||||
// The document has no viewBox, we need to resize it by hand.
|
||||
// we want to keep the document size ratio
|
||||
float xscale, yscale;
|
||||
xscale = width / defaultWidth;
|
||||
yscale = height / defaultHeight;
|
||||
xscale = width / defaultSize.width;
|
||||
yscale = height / defaultSize.height;
|
||||
float scale = Math.min(xscale, yscale);
|
||||
Px = AffineTransform.getScaleInstance(scale, scale);
|
||||
}
|
||||
@@ -515,7 +514,7 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
catch (BridgeException ex) {
|
||||
throw new TranscoderException(ex);
|
||||
throw new IIOException(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
this.root = gvtRoot;
|
||||
@@ -584,9 +583,7 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
return dest;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
TranscoderException exception = new TranscoderException(ex.getMessage());
|
||||
exception.initCause(ex);
|
||||
throw exception;
|
||||
throw new IIOException(ex.getMessage(), ex);
|
||||
}
|
||||
finally {
|
||||
if (context != null) {
|
||||
@@ -595,7 +592,7 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void init() throws TranscoderException {
|
||||
private synchronized void init() throws IIOException {
|
||||
if (!initialized) {
|
||||
if (transcoderInput == null) {
|
||||
throw new IllegalStateException("input == null");
|
||||
@@ -603,11 +600,17 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
|
||||
initialized = true;
|
||||
|
||||
super.transcode(transcoderInput, null);
|
||||
try {
|
||||
super.transcode(transcoderInput, null);
|
||||
}
|
||||
catch (TranscoderException e) {
|
||||
Throwable cause = unwrapException(e);
|
||||
throw new IIOException(cause.getMessage(), cause);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private BufferedImage getImage() throws TranscoderException {
|
||||
private BufferedImage getImage() throws IOException {
|
||||
if (image == null) {
|
||||
image = readImage();
|
||||
}
|
||||
@@ -615,14 +618,19 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
return image;
|
||||
}
|
||||
|
||||
int getDefaultWidth() throws TranscoderException {
|
||||
int getDefaultWidth() throws IOException {
|
||||
init();
|
||||
return (int) Math.ceil(defaultWidth);
|
||||
return defaultSize.width;
|
||||
}
|
||||
|
||||
int getDefaultHeight() throws TranscoderException {
|
||||
int getDefaultHeight() throws IOException {
|
||||
init();
|
||||
return (int) Math.ceil(defaultHeight);
|
||||
return defaultSize.height;
|
||||
}
|
||||
|
||||
Rectangle getViewBox() throws IOException {
|
||||
init();
|
||||
return viewBox.getBounds();
|
||||
}
|
||||
|
||||
void setInput(final TranscoderInput pInput) {
|
||||
|
||||
+143
-141
@@ -1,141 +1,143 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 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.wmf;
|
||||
|
||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||
import com.twelvemonkeys.imageio.plugins.svg.SVGImageReader;
|
||||
import com.twelvemonkeys.imageio.plugins.svg.SVGReadParam;
|
||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||
import org.apache.batik.transcoder.TranscoderException;
|
||||
import org.apache.batik.transcoder.TranscoderInput;
|
||||
import org.apache.batik.transcoder.TranscoderOutput;
|
||||
import org.apache.batik.transcoder.wmf.tosvg.WMFTranscoder;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.*;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* WMFImageReader class description.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haku $
|
||||
* @version $Id: WMFImageReader.java,v 1.0 29.jul.2004 13:00:59 haku Exp $
|
||||
*/
|
||||
// TODO: Probably possible to do less wrapping/unwrapping of data...
|
||||
// TODO: Consider using temp file instead of in-memory stream
|
||||
public final class WMFImageReader extends ImageReaderBase {
|
||||
|
||||
private SVGImageReader reader = null;
|
||||
|
||||
public WMFImageReader(final ImageReaderSpi pProvider) {
|
||||
super(pProvider);
|
||||
}
|
||||
|
||||
protected void resetMembers() {
|
||||
if (reader != null) {
|
||||
reader.dispose();
|
||||
}
|
||||
|
||||
reader = null;
|
||||
}
|
||||
|
||||
public BufferedImage read(int pIndex, ImageReadParam pParam) throws IOException {
|
||||
init();
|
||||
|
||||
processImageStarted(pIndex);
|
||||
|
||||
BufferedImage image = reader.read(pIndex, pParam);
|
||||
if (abortRequested()) {
|
||||
processReadAborted();
|
||||
return image;
|
||||
}
|
||||
processImageProgress(100f);
|
||||
|
||||
processImageComplete();
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
private synchronized void init() throws IOException {
|
||||
// Need the extra test, to avoid throwing an IOException from the Transcoder
|
||||
if (imageInput == null) {
|
||||
throw new IllegalStateException("input == null");
|
||||
}
|
||||
|
||||
if (reader == null) {
|
||||
WMFTranscoder transcoder = new WMFTranscoder();
|
||||
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
Writer writer = new OutputStreamWriter(output, "UTF8");
|
||||
try {
|
||||
TranscoderInput in = new TranscoderInput(IIOUtil.createStreamAdapter(imageInput));
|
||||
TranscoderOutput out = new TranscoderOutput(writer);
|
||||
|
||||
// TODO: Transcodinghints?
|
||||
|
||||
transcoder.transcode(in, out);
|
||||
}
|
||||
catch (TranscoderException e) {
|
||||
throw new IIOException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
reader = new SVGImageReader(getOriginatingProvider());
|
||||
reader.setInput(ImageIO.createImageInputStream(new ByteArrayInputStream(output.toByteArray())));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageReadParam getDefaultReadParam() {
|
||||
return new SVGReadParam();
|
||||
}
|
||||
|
||||
public int getWidth(int pIndex) throws IOException {
|
||||
init();
|
||||
return reader.getWidth(pIndex);
|
||||
}
|
||||
|
||||
public int getHeight(int pIndex) throws IOException {
|
||||
init();
|
||||
return reader.getHeight(pIndex);
|
||||
}
|
||||
|
||||
public Iterator<ImageTypeSpecifier> getImageTypes(final int pImageIndex) throws IOException {
|
||||
init();
|
||||
return reader.getImageTypes(pImageIndex);
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2008, 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.wmf;
|
||||
|
||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||
import com.twelvemonkeys.imageio.plugins.svg.SVGImageReader;
|
||||
import com.twelvemonkeys.imageio.plugins.svg.SVGReadParam;
|
||||
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
|
||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||
|
||||
import org.apache.batik.transcoder.TranscoderException;
|
||||
import org.apache.batik.transcoder.TranscoderInput;
|
||||
import org.apache.batik.transcoder.TranscoderOutput;
|
||||
import org.apache.batik.transcoder.wmf.tosvg.WMFTranscoder;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import java.awt.image.*;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* WMFImageReader class description.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haku $
|
||||
* @version $Id: WMFImageReader.java,v 1.0 29.jul.2004 13:00:59 haku Exp $
|
||||
*/
|
||||
// TODO: Probably possible to do less wrapping/unwrapping of data...
|
||||
public final class WMFImageReader extends ImageReaderBase {
|
||||
|
||||
private SVGImageReader reader = null;
|
||||
|
||||
public WMFImageReader(final ImageReaderSpi pProvider) {
|
||||
super(pProvider);
|
||||
}
|
||||
|
||||
protected void resetMembers() {
|
||||
if (reader != null) {
|
||||
reader.dispose();
|
||||
}
|
||||
|
||||
reader = null;
|
||||
}
|
||||
|
||||
public BufferedImage read(int pIndex, ImageReadParam pParam) throws IOException {
|
||||
init();
|
||||
|
||||
processImageStarted(pIndex);
|
||||
|
||||
BufferedImage image = reader.read(pIndex, pParam);
|
||||
if (abortRequested()) {
|
||||
processReadAborted();
|
||||
return image;
|
||||
}
|
||||
processImageProgress(100f);
|
||||
|
||||
processImageComplete();
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
private void init() throws IOException {
|
||||
// Need the extra test, to avoid throwing an IOException from the Transcoder
|
||||
if (imageInput == null) {
|
||||
throw new IllegalStateException("input == null");
|
||||
}
|
||||
|
||||
if (reader == null) {
|
||||
WMFTranscoder transcoder = new WMFTranscoder();
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream(8192);
|
||||
|
||||
try (Writer writer = new OutputStreamWriter(output, StandardCharsets.UTF_8)) {
|
||||
TranscoderInput in = new TranscoderInput(IIOUtil.createStreamAdapter(imageInput));
|
||||
TranscoderOutput out = new TranscoderOutput(writer);
|
||||
|
||||
// TODO: Transcodinghints?
|
||||
|
||||
transcoder.transcode(in, out);
|
||||
}
|
||||
catch (TranscoderException e) {
|
||||
throw new IIOException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
reader = new SVGImageReader(getOriginatingProvider());
|
||||
reader.setInput(new ByteArrayImageInputStream(output.toByteArray()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageReadParam getDefaultReadParam() {
|
||||
return new SVGReadParam();
|
||||
}
|
||||
|
||||
public int getWidth(int pIndex) throws IOException {
|
||||
init();
|
||||
return reader.getWidth(pIndex);
|
||||
}
|
||||
|
||||
public int getHeight(int pIndex) throws IOException {
|
||||
init();
|
||||
return reader.getHeight(pIndex);
|
||||
}
|
||||
|
||||
public Iterator<ImageTypeSpecifier> getImageTypes(final int pImageIndex) throws IOException {
|
||||
init();
|
||||
return reader.getImageTypes(pImageIndex);
|
||||
}
|
||||
}
|
||||
|
||||
+74
-9
@@ -43,8 +43,7 @@ import javax.imageio.event.IIOReadWarningListener;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ImagingOpException;
|
||||
import java.awt.image.*;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URISyntaxException;
|
||||
@@ -53,9 +52,10 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
@@ -194,12 +194,12 @@ public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader>
|
||||
TestData redSquare = new TestData(getClassLoaderResource("/svg/red-square.svg"), dim);
|
||||
reader.setInput(redSquare.getInputStream());
|
||||
BufferedImage imageRed = reader.read(0, param);
|
||||
assertEquals(0xFF0000, imageRed.getRGB(50, 50) & 0xFFFFFF);
|
||||
assertRGBEquals("Expected all red", 0xFF0000, imageRed.getRGB(50, 50) & 0xFFFFFF, 0);
|
||||
|
||||
TestData blueSquare = new TestData(getClassLoaderResource("/svg/blue-square.svg"), dim);
|
||||
reader.setInput(blueSquare.getInputStream());
|
||||
BufferedImage imageBlue = reader.read(0, param);
|
||||
assertEquals(0x0000FF, imageBlue.getRGB(50, 50) & 0xFFFFFF);
|
||||
assertRGBEquals("Expected all blue", 0x0000FF, imageBlue.getRGB(50, 50) & 0xFFFFFF, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -225,7 +225,7 @@ public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader>
|
||||
assertEquals(500, image.getHeight());
|
||||
|
||||
// CSS and embedded resources all go!
|
||||
verifyZeroInteractions(listener);
|
||||
verifyNoInteractions(listener);
|
||||
}
|
||||
finally {
|
||||
reader.dispose();
|
||||
@@ -266,7 +266,7 @@ public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader>
|
||||
assertEquals(500, image.getHeight());
|
||||
|
||||
// No more warnings now that the base URI is set
|
||||
verifyZeroInteractions(listener);
|
||||
verifyNoInteractions(listener);
|
||||
}
|
||||
finally {
|
||||
reader.dispose();
|
||||
@@ -339,4 +339,69 @@ public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader>
|
||||
reader.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadWitSourceRenderSize() throws IOException {
|
||||
URL resource = getClassLoaderResource("/svg/circle.svg");
|
||||
|
||||
SVGImageReader reader = createReader();
|
||||
|
||||
TestData data = new TestData(resource, (Dimension) null);
|
||||
try (ImageInputStream stream = data.getInputStream()) {
|
||||
reader.setInput(stream);
|
||||
|
||||
SVGReadParam param = reader.getDefaultReadParam();
|
||||
param.setSourceRenderSize(new Dimension(100, 100));
|
||||
BufferedImage image = reader.read(0, param);
|
||||
|
||||
assertNotNull(image);
|
||||
assertEquals(100, image.getWidth());
|
||||
assertEquals(100, image.getHeight());
|
||||
|
||||
// Some quick samples
|
||||
assertRGBEquals("Expected transparent corner", 0, image.getRGB(0, 0), 0);
|
||||
assertRGBEquals("Expected transparent corner", 0, image.getRGB(99, 0), 0);
|
||||
assertRGBEquals("Expected transparent corner", 0, image.getRGB(0, 99), 0);
|
||||
assertRGBEquals("Expected transparent corner", 0, image.getRGB(99, 99), 0);
|
||||
assertRGBEquals("Expected red center", 0xffff0000, image.getRGB(50, 50), 0);
|
||||
}
|
||||
finally {
|
||||
reader.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadWitSourceRenderSizeViewBoxNegativeXY() throws IOException {
|
||||
URL resource = getClassLoaderResource("/svg/Android_robot.svg");
|
||||
|
||||
SVGImageReader reader = createReader();
|
||||
|
||||
TestData data = new TestData(resource, (Dimension) null);
|
||||
try (ImageInputStream stream = data.getInputStream()) {
|
||||
reader.setInput(stream);
|
||||
|
||||
SVGReadParam param = reader.getDefaultReadParam();
|
||||
param.setSourceRenderSize(new Dimension(219, 256)); // Aspect scaled to 256 boxed
|
||||
BufferedImage image = reader.read(0, param);
|
||||
|
||||
assertNotNull(image);
|
||||
assertEquals(219, image.getWidth());
|
||||
assertEquals(256, image.getHeight());
|
||||
|
||||
// Some quick samples
|
||||
assertRGBEquals("Expected transparent corner", 0, image.getRGB(0, 0), 0);
|
||||
assertRGBEquals("Expected transparent corner", 0, image.getRGB(218, 0), 0);
|
||||
assertRGBEquals("Expected transparent corner", 0, image.getRGB(0, 255), 0);
|
||||
assertRGBEquals("Expected transparent corner", 0, image.getRGB(218, 255), 0);
|
||||
assertRGBEquals("Expected green head", 0xffa4c639, image.getRGB(109, 20), 25);
|
||||
assertRGBEquals("Expected green center", 0xffa4c639, image.getRGB(109, 128), 25);
|
||||
assertRGBEquals("Expected green feet", 0xffa4c639, image.getRGB(80, 246), 25);
|
||||
assertRGBEquals("Expected green feet", 0xffa4c639, image.getRGB(130, 246), 25);
|
||||
assertRGBEquals("Expected white edge", 0xffffffff, image.getRGB(0, 128), 0);
|
||||
assertRGBEquals("Expected white edge", 0xffffffff, image.getRGB(218, 128), 0);
|
||||
}
|
||||
finally {
|
||||
reader.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 50 50" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
xml:space="preserve"
|
||||
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2">
|
||||
<circle cx="25" cy="25" r="25" fill="red"/></svg>
|
||||
|
After Width: | Height: | Size: 436 B |
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.7.0</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-bmp</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: BMP plugin</name>
|
||||
|
||||
+45
-49
@@ -30,15 +30,14 @@
|
||||
|
||||
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 com.twelvemonkeys.imageio.ImageReaderBase;
|
||||
import com.twelvemonkeys.imageio.stream.SubImageInputStream;
|
||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
||||
import com.twelvemonkeys.imageio.util.ProgressListenerBase;
|
||||
import com.twelvemonkeys.io.LittleEndianDataInputStream;
|
||||
import com.twelvemonkeys.io.enc.DecoderStream;
|
||||
import com.twelvemonkeys.xml.XMLSerializer;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.ImageIO;
|
||||
@@ -51,15 +50,15 @@ import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.metadata.IIOMetadataFormatImpl;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
|
||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||
import com.twelvemonkeys.imageio.stream.SubImageInputStream;
|
||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
||||
import com.twelvemonkeys.imageio.util.ProgressListenerBase;
|
||||
import com.twelvemonkeys.io.LittleEndianDataInputStream;
|
||||
import com.twelvemonkeys.io.enc.DecoderStream;
|
||||
import com.twelvemonkeys.xml.XMLSerializer;
|
||||
import java.awt.*;
|
||||
import java.awt.color.*;
|
||||
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;
|
||||
|
||||
/**
|
||||
* ImageReader for Microsoft Windows Bitmap (BMP) format.
|
||||
@@ -82,7 +81,7 @@ public final class BMPImageReader extends ImageReaderBase {
|
||||
super(new BMPImageReaderSpi());
|
||||
}
|
||||
|
||||
protected BMPImageReader(final ImageReaderSpi pProvider) {
|
||||
BMPImageReader(final ImageReaderSpi pProvider) {
|
||||
super(pProvider);
|
||||
}
|
||||
|
||||
@@ -363,14 +362,18 @@ public final class BMPImageReader extends ImageReaderBase {
|
||||
|
||||
processImageStarted(imageIndex);
|
||||
for (int y = 0; y < height; y++) {
|
||||
switch (header.getBitCount()) {
|
||||
int bitCount = header.getBitCount();
|
||||
switch (bitCount) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 4:
|
||||
case 8:
|
||||
case 24:
|
||||
byte[] rowDataByte = ((DataBufferByte) rowRaster.getDataBuffer()).getData();
|
||||
readRowByte(input, height, srcRegion, xSub, ySub, rowDataByte, destRaster, clippedRow, y);
|
||||
int bitsPerSample = bitCount == 24 ? 8 : bitCount;
|
||||
int samplesPerPixel = bitCount == 24 ? 3 : 1;
|
||||
|
||||
readRowByte(input, height, srcRegion, xSub, ySub, bitsPerSample, samplesPerPixel, rowDataByte, destRaster, clippedRow, y);
|
||||
break;
|
||||
|
||||
case 16:
|
||||
@@ -384,7 +387,7 @@ public final class BMPImageReader extends ImageReaderBase {
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new AssertionError("Unsupported pixel depth: " + header.getBitCount());
|
||||
throw new AssertionError("Unsupported pixel depth: " + bitCount);
|
||||
}
|
||||
|
||||
processImageProgress(100f * y / height);
|
||||
@@ -481,9 +484,14 @@ public final class BMPImageReader extends ImageReaderBase {
|
||||
}
|
||||
|
||||
private void readRowByte(final DataInput input, final int height, final Rectangle srcRegion, final int xSub, final int ySub,
|
||||
int bitsPerSample, int samplesPerPixel,
|
||||
final byte[] rowDataByte, final WritableRaster destChannel, final Raster srcChannel, final int y) throws IOException {
|
||||
// Flip into position?
|
||||
int srcY = !header.topDown ? height - 1 - y : y;
|
||||
int dstY = (srcY - srcRegion.y) / ySub;
|
||||
|
||||
// If subsampled or outside source region, skip entire row
|
||||
if (y % ySub != 0 || height - 1 - y < srcRegion.y || height - 1 - y >= srcRegion.y + srcRegion.height) {
|
||||
if (srcY % ySub != 0 || srcY < srcRegion.y || srcY >= srcRegion.y + srcRegion.height) {
|
||||
input.skipBytes(rowDataByte.length);
|
||||
|
||||
return;
|
||||
@@ -493,24 +501,20 @@ public final class BMPImageReader extends ImageReaderBase {
|
||||
|
||||
// Subsample horizontal
|
||||
if (xSub != 1) {
|
||||
for (int x = 0; x < srcRegion.width / xSub; x++) {
|
||||
rowDataByte[srcRegion.x + x] = rowDataByte[srcRegion.x + x * xSub];
|
||||
}
|
||||
IIOUtil.subsampleRow(rowDataByte, srcRegion.x, srcRegion.width, rowDataByte, 0, samplesPerPixel, bitsPerSample, xSub);
|
||||
}
|
||||
|
||||
if (header.topDown) {
|
||||
destChannel.setDataElements(0, y, srcChannel);
|
||||
} else {
|
||||
// Flip into position
|
||||
int dstY = (height - 1 - y - srcRegion.y) / ySub;
|
||||
destChannel.setDataElements(0, dstY, srcChannel);
|
||||
}
|
||||
destChannel.setDataElements(0, dstY, srcChannel);
|
||||
}
|
||||
|
||||
private void readRowUShort(final DataInput input, final int height, final Rectangle srcRegion, final int xSub, final int ySub,
|
||||
final short[] rowDataUShort, final WritableRaster destChannel, final Raster srcChannel, final int y) throws IOException {
|
||||
// Flip into position?
|
||||
int srcY = !header.topDown ? height - 1 - y : y;
|
||||
int dstY = (srcY - srcRegion.y) / ySub;
|
||||
|
||||
// If subsampled or outside source region, skip entire row
|
||||
if (y % ySub != 0 || height - 1 - y < srcRegion.y || height - 1 - y >= srcRegion.y + srcRegion.height) {
|
||||
if (srcY % ySub != 0 || srcY < srcRegion.y || srcY >= srcRegion.y + srcRegion.height) {
|
||||
input.skipBytes(rowDataUShort.length * 2 + (rowDataUShort.length % 2) * 2);
|
||||
|
||||
return;
|
||||
@@ -530,19 +534,17 @@ public final class BMPImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
if (header.topDown) {
|
||||
destChannel.setDataElements(0, y, srcChannel);
|
||||
} else {
|
||||
// Flip into position
|
||||
int dstY = (height - 1 - y - srcRegion.y) / ySub;
|
||||
destChannel.setDataElements(0, dstY, srcChannel);
|
||||
}
|
||||
destChannel.setDataElements(0, dstY, srcChannel);
|
||||
}
|
||||
|
||||
private void readRowInt(final DataInput input, final int height, final Rectangle srcRegion, final int xSub, final int ySub,
|
||||
final int[] rowDataInt, final WritableRaster destChannel, final Raster srcChannel, final int y) throws IOException {
|
||||
// Flip into position?
|
||||
int srcY = !header.topDown ? height - 1 - y : y;
|
||||
int dstY = (srcY - srcRegion.y) / ySub;
|
||||
|
||||
// If subsampled or outside source region, skip entire row
|
||||
if (y % ySub != 0 || height - 1 - y < srcRegion.y || height - 1 - y >= srcRegion.y + srcRegion.height) {
|
||||
if (srcY % ySub != 0 || srcY < srcRegion.y || srcY >= srcRegion.y + srcRegion.height) {
|
||||
input.skipBytes(rowDataInt.length * 4);
|
||||
|
||||
return;
|
||||
@@ -557,13 +559,7 @@ public final class BMPImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
if (header.topDown) {
|
||||
destChannel.setDataElements(0, y, srcChannel);
|
||||
} else {
|
||||
// Flip into position
|
||||
int dstY = (height - 1 - y - srcRegion.y) / ySub;
|
||||
destChannel.setDataElements(0, dstY, srcChannel);
|
||||
}
|
||||
destChannel.setDataElements(0, dstY, srcChannel);
|
||||
}
|
||||
|
||||
// TODO: Candidate util method
|
||||
|
||||
+1
-1
@@ -116,7 +116,7 @@ public final class BMPImageReaderSpi extends ImageReaderSpiBase {
|
||||
}
|
||||
}
|
||||
|
||||
public ImageReader createReaderInstance(final Object pExtension) throws IOException {
|
||||
public ImageReader createReaderInstance(final Object pExtension) {
|
||||
return new BMPImageReader(this);
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -32,8 +32,8 @@ package com.twelvemonkeys.imageio.plugins.bmp;
|
||||
|
||||
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.ArgumentMatchers.anyFloat;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@@ -295,7 +295,7 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
|
||||
// At least imageStarted and imageComplete, plus any number of imageProgress
|
||||
InOrder ordered = inOrder(listener);
|
||||
ordered.verify(listener).imageStarted(reader, 0);
|
||||
ordered.verify(listener, atLeastOnce()).imageProgress(eq(reader), anyInt());
|
||||
ordered.verify(listener, atLeastOnce()).imageProgress(eq(reader), anyFloat());
|
||||
ordered.verify(listener).imageComplete(reader);
|
||||
}
|
||||
|
||||
@@ -318,7 +318,7 @@ public class BMPImageReaderTest extends ImageReaderAbstractTest<BMPImageReader>
|
||||
// At least imageStarted and imageComplete, plus any number of imageProgress
|
||||
InOrder ordered = inOrder(listener);
|
||||
ordered.verify(listener).imageStarted(reader, 0);
|
||||
ordered.verify(listener, atLeastOnce()).imageProgress(eq(reader), anyInt());
|
||||
ordered.verify(listener, atLeastOnce()).imageProgress(eq(reader), anyFloat());
|
||||
ordered.verify(listener).imageComplete(reader);
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 257 KiB |
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.7.0</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-clippath</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: Photoshop Path Support</name>
|
||||
|
||||
+1
@@ -39,6 +39,7 @@ import java.io.IOException;
|
||||
*
|
||||
* @deprecated Use {@link AdobePathReader} instead. This class will be removed in a future release.
|
||||
*/
|
||||
@Deprecated
|
||||
public final class AdobePathBuilder {
|
||||
|
||||
private final AdobePathReader delegate;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.7.0</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: Core</name>
|
||||
|
||||
+563
@@ -0,0 +1,563 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.color;
|
||||
|
||||
import com.twelvemonkeys.io.FileUtil;
|
||||
import com.twelvemonkeys.lang.Platform;
|
||||
import com.twelvemonkeys.lang.SystemUtil;
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.color.ICC_ColorSpace;
|
||||
import java.awt.color.ICC_Profile;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Properties;
|
||||
|
||||
import static com.twelvemonkeys.imageio.color.ColorSpaces.DEBUG;
|
||||
|
||||
/**
|
||||
* A helper class for working with ICC color profiles.
|
||||
* <p>
|
||||
* Standard ICC color profiles are read from system-specific locations
|
||||
* for known operating systems.
|
||||
* </p>
|
||||
* <p>
|
||||
* Color profiles may be configured by placing a property-file
|
||||
* {@code com/twelvemonkeys/imageio/color/icc_profiles.properties}
|
||||
* on the classpath, specifying the full path to the profiles.
|
||||
* ICC color profiles are probably already present on your system, or
|
||||
* can be downloaded from
|
||||
* <a href="http://www.color.org/profiles2.xalter">ICC</a>,
|
||||
* <a href="http://www.adobe.com/downloads/">Adobe</a> or other places.
|
||||
* * </p>
|
||||
* <p>
|
||||
* Example property file:
|
||||
* </p>
|
||||
* <pre>
|
||||
* # icc_profiles.properties
|
||||
* ADOBE_RGB_1998=/path/to/Adobe RGB 1998.icc
|
||||
* GENERIC_CMYK=/path/to/Generic CMYK.icc
|
||||
* </pre>
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: ColorSpaces.java,v 1.0 24.01.11 17.51 haraldk Exp$
|
||||
*/
|
||||
public final class ColorProfiles {
|
||||
/**
|
||||
* We need special ICC profile handling for KCMS vs LCMS. Delegate to specific strategy.
|
||||
*/
|
||||
private final static ICCProfileSanitizer profileCleaner = ICCProfileSanitizer.Factory.get();
|
||||
|
||||
static final int ICC_PROFILE_MAGIC = 'a' << 24 | 'c' << 16 | 's' << 8 | 'p';
|
||||
static final int ICC_PROFILE_HEADER_SIZE = 128;
|
||||
|
||||
static {
|
||||
// In case we didn't activate through SPI already
|
||||
ProfileDeferralActivator.activateProfiles();
|
||||
}
|
||||
|
||||
private ColorProfiles() {
|
||||
}
|
||||
|
||||
static byte[] getProfileHeaderWithProfileId(final ICC_Profile profile) {
|
||||
// Get *entire profile data*... :-/
|
||||
return getProfileHeaderWithProfileId(profile.getData());
|
||||
}
|
||||
|
||||
static byte[] getProfileHeaderWithProfileId(byte[] data) {
|
||||
// ICC profile header is the first 128 bytes
|
||||
byte[] header = Arrays.copyOf(data, ICC_PROFILE_HEADER_SIZE);
|
||||
|
||||
// Clear out preferred CMM, platform & creator, as these don't affect the profile in any way
|
||||
// - LCMS updates CMM + creator to "lcms" and platform to current platform
|
||||
// - KCMS keeps the values in the file...
|
||||
Arrays.fill(header, ICC_Profile.icHdrCmmId, ICC_Profile.icHdrCmmId + 4, (byte) 0);
|
||||
Arrays.fill(header, ICC_Profile.icHdrPlatform, ICC_Profile.icHdrPlatform + 4, (byte) 0);
|
||||
// + Clear out rendering intent, as this may be updated by application
|
||||
Arrays.fill(header, ICC_Profile.icHdrRenderingIntent, ICC_Profile.icHdrRenderingIntent + 4, (byte) 0);
|
||||
Arrays.fill(header, ICC_Profile.icHdrCreator, ICC_Profile.icHdrCreator + 4, (byte) 0);
|
||||
|
||||
// Clear out any existing MD5, as it is no longer correct
|
||||
Arrays.fill(header, ICC_Profile.icHdrProfileID, ICC_Profile.icHdrProfileID + 16, (byte) 0);
|
||||
|
||||
// Generate new MD5 and store in header
|
||||
byte[] md5 = computeMD5(header, data);
|
||||
System.arraycopy(md5, 0, header, ICC_Profile.icHdrProfileID, md5.length);
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
private static byte[] computeMD5(byte[] header, byte[] data) {
|
||||
try {
|
||||
MessageDigest digest = MessageDigest.getInstance("MD5");
|
||||
digest.update(header, 0, ICC_PROFILE_HEADER_SIZE);
|
||||
digest.update(data, ICC_PROFILE_HEADER_SIZE, data.length - ICC_PROFILE_HEADER_SIZE);
|
||||
return digest.digest();
|
||||
}
|
||||
catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalStateException("Missing MD5 MessageDigest");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether an ICC color profile is equal to the default sRGB profile.
|
||||
*
|
||||
* @param profile the ICC profile to test. May not be {@code null}.
|
||||
* @return {@code true} if {@code profile} is equal to the default sRGB profile.
|
||||
* @throws IllegalArgumentException if {@code profile} is {@code null}
|
||||
* @see java.awt.color.ColorSpace#CS_sRGB
|
||||
* @see java.awt.color.ColorSpace#isCS_sRGB()
|
||||
*/
|
||||
public static boolean isCS_sRGB(final ICC_Profile profile) {
|
||||
Validate.notNull(profile, "profile");
|
||||
|
||||
return profile.getColorSpaceType() == ColorSpace.TYPE_RGB && Arrays.equals(getProfileHeaderWithProfileId(profile), sRGB.header);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether an ICC color profile is equal to the default GRAY profile.
|
||||
*
|
||||
* @param profile the ICC profile to test. May not be {@code null}.
|
||||
* @return {@code true} if {@code profile} is equal to the default GRAY profile.
|
||||
* @throws IllegalArgumentException if {@code profile} is {@code null}
|
||||
* @see java.awt.color.ColorSpace#CS_GRAY
|
||||
*/
|
||||
public static boolean isCS_GRAY(final ICC_Profile profile) {
|
||||
Validate.notNull(profile, "profile");
|
||||
|
||||
return profile.getColorSpaceType() == ColorSpace.TYPE_GRAY && Arrays.equals(getProfileHeaderWithProfileId(profile), GRAY.header);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether an ICC color profile is known to cause problems for {@link java.awt.image.ColorConvertOp}.
|
||||
* <p>
|
||||
* <em>
|
||||
* Note that this method only tests if a color conversion using this profile is known to fail.
|
||||
* There's no guarantee that the color conversion will succeed even if this method returns {@code false}.
|
||||
* </em>
|
||||
* </p>
|
||||
*
|
||||
* @param profile the ICC color profile. May not be {@code null}.
|
||||
* @return {@code true} if known to be offending, {@code false} otherwise
|
||||
* @throws IllegalArgumentException if {@code profile} is {@code null}
|
||||
*/
|
||||
static boolean isOffendingColorProfile(final ICC_Profile profile) {
|
||||
Validate.notNull(profile, "profile");
|
||||
|
||||
// NOTE:
|
||||
// Several embedded ICC color profiles are non-compliant with Java pre JDK7 and throws CMMException
|
||||
// The problem with these embedded ICC profiles seems to be the rendering intent
|
||||
// being 1 (01000000) - "Media Relative Colormetric" in the offending profiles,
|
||||
// and 0 (00000000) - "Perceptual" in the good profiles
|
||||
// (that is 1 single bit of difference right there.. ;-)
|
||||
// See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7064516
|
||||
|
||||
// This is particularly annoying, as the byte copying isn't really necessary,
|
||||
// except the getRenderingIntent method is package protected in java.awt.color
|
||||
byte[] header = profile.getData(ICC_Profile.icSigHead);
|
||||
|
||||
return header[ICC_Profile.icHdrRenderingIntent] != 0 || header[ICC_Profile.icHdrRenderingIntent + 1] != 0
|
||||
|| header[ICC_Profile.icHdrRenderingIntent + 2] != 0 || header[ICC_Profile.icHdrRenderingIntent + 3] > 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether an ICC color profile is valid.
|
||||
* Invalid profiles are known to cause problems for {@link java.awt.image.ColorConvertOp}.
|
||||
* <p>
|
||||
* <em>
|
||||
* Note that this method only tests if a color conversion using this profile is known to fail.
|
||||
* There's no guarantee that the color conversion will succeed even if this method returns {@code false}.
|
||||
* </em>
|
||||
* </p>
|
||||
*
|
||||
* @param profile the ICC color profile. May not be {@code null}.
|
||||
* @return {@code profile} if valid.
|
||||
* @throws IllegalArgumentException if {@code profile} is {@code null}
|
||||
* @throws java.awt.color.CMMException if {@code profile} is invalid.
|
||||
*/
|
||||
public static ICC_Profile validateProfile(final ICC_Profile profile) {
|
||||
// Fix profile before validation
|
||||
profileCleaner.fixProfile(profile);
|
||||
ColorSpaces.validateColorSpace(new ICC_ColorSpace(profile)); // TODO: Should use createColorSpace and cache if good?
|
||||
|
||||
return profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an ICC Profile from the given input stream, as-is, with no validation.
|
||||
*
|
||||
* This method behaves exactly like {@code ICC_Profile.getInstance(input)}.
|
||||
*
|
||||
* @param input the input stream to read from, may not be {@code null}
|
||||
* @return an {@code ICC_Profile} object as read from the input stream.
|
||||
* @throws IOException If an I/O error occurs while reading the stream.
|
||||
* @throws IllegalArgumentException If {@code input} is {@code null}
|
||||
* or the stream does not contain valid ICC Profile data.
|
||||
* @see ICC_Profile#getInstance(InputStream)
|
||||
* @see #readProfile(InputStream)
|
||||
*/
|
||||
public static ICC_Profile readProfileRaw(final InputStream input) throws IOException {
|
||||
Validate.notNull(input, "input");
|
||||
|
||||
return ICC_Profile.getInstance(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an ICC Profile from the given input stream, with extra validation.
|
||||
*
|
||||
* If a matching profile already exists in cache, the cached instance is returned.
|
||||
*
|
||||
* @param input the input stream to read from, may not be {@code null}
|
||||
* @return an {@code ICC_Profile} object as read from the input stream.
|
||||
* @throws IOException If an I/O error occurs while reading the stream.
|
||||
* @throws IllegalArgumentException If {@code input} is {@code null}
|
||||
* or the stream does not contain valid ICC Profile data.
|
||||
* @see ICC_Profile#getInstance(InputStream)
|
||||
*/
|
||||
public static ICC_Profile readProfile(final InputStream input) throws IOException {
|
||||
Validate.notNull(input, "input");
|
||||
|
||||
DataInputStream dataInput = new DataInputStream(input);
|
||||
byte[] header = new byte[ICC_PROFILE_HEADER_SIZE];
|
||||
try {
|
||||
dataInput.readFully(header);
|
||||
|
||||
int size = validateHeaderAndGetSize(header);
|
||||
byte[] data = Arrays.copyOf(header, size);
|
||||
dataInput.readFully(data, header.length, size - header.length);
|
||||
|
||||
return createProfile(data);
|
||||
}
|
||||
catch (EOFException e) {
|
||||
throw new IllegalArgumentException("Truncated ICC Profile data", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an ICC Profile from the given byte array, as-is, with no validation.
|
||||
*
|
||||
* This method behaves exactly like {@code ICC_Profile.getInstance(input)},
|
||||
* except that extraneous bytes at the end of the array is ignored.
|
||||
*
|
||||
* @param input the byte array to create a profile from, may not be {@code null}
|
||||
* @return an {@code ICC_Profile} object created from the byte array
|
||||
* @throws IllegalArgumentException If {@code input} is {@code null}
|
||||
* or the byte array does not contain valid ICC Profile data.
|
||||
* @see ICC_Profile#getInstance(byte[])
|
||||
* @see #createProfile(byte[])
|
||||
*/
|
||||
public static ICC_Profile createProfileRaw(final byte[] input) {
|
||||
int size = validateHeaderAndGetSize(input);
|
||||
|
||||
// Unlike the InputStream version, the byte version of ICC_Profile.getInstance()
|
||||
// does not discard extra bytes at the end. We'll chop them off here for convenience
|
||||
return ICC_Profile.getInstance(limit(input, size));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an ICC Profile from the given byte array, with extra validation.
|
||||
* Extraneous bytes at the end of the array are ignored.
|
||||
*
|
||||
* If a matching profile already exists in cache, the cached instance is returned.
|
||||
*
|
||||
* @param input the byte array to create a profile from, may not be {@code null}
|
||||
* @return an {@code ICC_Profile} object created from the byte array
|
||||
* @throws IllegalArgumentException If {@code input} is {@code null}
|
||||
* or the byte array does not contain valid ICC Profile data.
|
||||
* @see ICC_Profile#getInstance(byte[])
|
||||
*/
|
||||
public static ICC_Profile createProfile(final byte[] input) {
|
||||
int size = validateAndGetSize(input);
|
||||
|
||||
// Look up in cache before returning, these are already validated
|
||||
byte[] profileHeader = getProfileHeaderWithProfileId(input);
|
||||
ICC_Profile internal = getInternalProfile(profileHeader);
|
||||
if (internal != null) {
|
||||
return internal;
|
||||
}
|
||||
|
||||
ICC_ColorSpace cached = ColorSpaces.getCachedCS(profileHeader);
|
||||
if (cached != null) {
|
||||
return cached.getProfile();
|
||||
}
|
||||
|
||||
ICC_Profile profile = ICC_Profile.getInstance(limit(input, size));
|
||||
|
||||
// We'll validate & cache by creating a color space and returning its profile...
|
||||
// TODO: Rewrite with separate cache for profiles...
|
||||
return ColorSpaces.createColorSpace(profile).getProfile();
|
||||
}
|
||||
|
||||
private static byte[] limit(byte[] input, int size) {
|
||||
return input.length == size ? input : Arrays.copyOf(input, size);
|
||||
}
|
||||
|
||||
private static int validateAndGetSize(byte[] input) {
|
||||
int size = validateHeaderAndGetSize(input);
|
||||
|
||||
if (size < 0 || size > input.length) {
|
||||
throw new IllegalArgumentException("Truncated ICC profile data, length < " + size + ": " + input.length);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
private static int validateHeaderAndGetSize(byte[] input) {
|
||||
Validate.notNull(input, "input");
|
||||
|
||||
if (input.length < ICC_PROFILE_HEADER_SIZE) { // Can't be less than size of ICC header
|
||||
throw new IllegalArgumentException("Truncated ICC profile data, length < 128: " + input.length);
|
||||
}
|
||||
|
||||
int size = intBigEndian(input, ICC_Profile.icHdrSize);
|
||||
|
||||
if (intBigEndian(input, ICC_Profile.icHdrMagic) != ICC_PROFILE_MAGIC) {
|
||||
throw new IllegalArgumentException("Not an ICC profile, missing file signature");
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
private static ICC_Profile getInternalProfile(final byte[] profileHeader) {
|
||||
int profileCSType = getCsType(profileHeader);
|
||||
|
||||
if (profileCSType == ColorSpace.TYPE_RGB && Arrays.equals(profileHeader, ColorProfiles.sRGB.header)) {
|
||||
return ICC_Profile.getInstance(ColorSpace.CS_sRGB);
|
||||
}
|
||||
else if (profileCSType == ColorSpace.TYPE_GRAY && Arrays.equals(profileHeader, ColorProfiles.GRAY.header)) {
|
||||
return ICC_Profile.getInstance(ColorSpace.CS_GRAY);
|
||||
}
|
||||
else if (profileCSType == ColorSpace.TYPE_3CLR && Arrays.equals(profileHeader, ColorProfiles.PYCC.header)) {
|
||||
return ICC_Profile.getInstance(ColorSpace.CS_PYCC);
|
||||
}
|
||||
else if (profileCSType == ColorSpace.TYPE_RGB && Arrays.equals(profileHeader, ColorProfiles.LINEAR_RGB.header)) {
|
||||
return ICC_Profile.getInstance(ColorSpace.CS_LINEAR_RGB);
|
||||
}
|
||||
else if (profileCSType == ColorSpace.TYPE_XYZ && Arrays.equals(profileHeader, ColorProfiles.CIEXYZ.header)) {
|
||||
return ICC_Profile.getInstance(ColorSpace.CS_CIEXYZ);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int intBigEndian(byte[] data, int index) {
|
||||
return (data[index] & 0xff) << 24 | (data[index + 1] & 0xff) << 16 | (data[index + 2] & 0xff) << 8 | (data[index + 3] & 0xff);
|
||||
}
|
||||
|
||||
private static int getCsType(byte[] profileHeader) {
|
||||
int csSig = intBigEndian(profileHeader, ICC_Profile.icHdrColorSpace);
|
||||
|
||||
switch (csSig) {
|
||||
case ICC_Profile.icSigXYZData:
|
||||
return ColorSpace.TYPE_XYZ;
|
||||
case ICC_Profile.icSigLabData:
|
||||
return ColorSpace.TYPE_Lab;
|
||||
case ICC_Profile.icSigLuvData:
|
||||
return ColorSpace.TYPE_Luv;
|
||||
case ICC_Profile.icSigYCbCrData:
|
||||
return ColorSpace.TYPE_YCbCr;
|
||||
case ICC_Profile.icSigYxyData:
|
||||
return ColorSpace.TYPE_Yxy;
|
||||
case ICC_Profile.icSigRgbData:
|
||||
return ColorSpace.TYPE_RGB;
|
||||
case ICC_Profile.icSigGrayData:
|
||||
return ColorSpace.TYPE_GRAY;
|
||||
case ICC_Profile.icSigHsvData:
|
||||
return ColorSpace.TYPE_HSV;
|
||||
case ICC_Profile.icSigHlsData:
|
||||
return ColorSpace.TYPE_HLS;
|
||||
case ICC_Profile.icSigCmykData:
|
||||
return ColorSpace.TYPE_CMYK;
|
||||
// Note: There is no TYPE_* 10...
|
||||
case ICC_Profile.icSigCmyData:
|
||||
return ColorSpace.TYPE_CMY;
|
||||
case ICC_Profile.icSigSpace2CLR:
|
||||
return ColorSpace.TYPE_2CLR;
|
||||
case ICC_Profile.icSigSpace3CLR:
|
||||
return ColorSpace.TYPE_3CLR;
|
||||
case ICC_Profile.icSigSpace4CLR:
|
||||
return ColorSpace.TYPE_4CLR;
|
||||
case ICC_Profile.icSigSpace5CLR:
|
||||
return ColorSpace.TYPE_5CLR;
|
||||
case ICC_Profile.icSigSpace6CLR:
|
||||
return ColorSpace.TYPE_6CLR;
|
||||
case ICC_Profile.icSigSpace7CLR:
|
||||
return ColorSpace.TYPE_7CLR;
|
||||
case ICC_Profile.icSigSpace8CLR:
|
||||
return ColorSpace.TYPE_8CLR;
|
||||
case ICC_Profile.icSigSpace9CLR:
|
||||
return ColorSpace.TYPE_9CLR;
|
||||
case ICC_Profile.icSigSpaceACLR:
|
||||
return ColorSpace.TYPE_ACLR;
|
||||
case ICC_Profile.icSigSpaceBCLR:
|
||||
return ColorSpace.TYPE_BCLR;
|
||||
case ICC_Profile.icSigSpaceCCLR:
|
||||
return ColorSpace.TYPE_CCLR;
|
||||
case ICC_Profile.icSigSpaceDCLR:
|
||||
return ColorSpace.TYPE_DCLR;
|
||||
case ICC_Profile.icSigSpaceECLR:
|
||||
return ColorSpace.TYPE_ECLR;
|
||||
case ICC_Profile.icSigSpaceFCLR:
|
||||
return ColorSpace.TYPE_FCLR;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid ICC color space signature: " + csSig); // TODO: fourCC?
|
||||
}
|
||||
}
|
||||
|
||||
static ICC_Profile readProfileFromClasspathResource(@SuppressWarnings("SameParameterValue") final String profilePath) {
|
||||
InputStream stream = ColorSpaces.class.getResourceAsStream(profilePath);
|
||||
|
||||
if (stream != null) {
|
||||
if (DEBUG) {
|
||||
System.out.println("Loading profile from classpath resource: " + profilePath);
|
||||
}
|
||||
|
||||
try {
|
||||
return ICC_Profile.getInstance(stream);
|
||||
}
|
||||
catch (@SuppressWarnings("CatchMayIgnoreException") IOException ignore) {
|
||||
if (DEBUG) {
|
||||
ignore.printStackTrace();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
FileUtil.close(stream);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static ICC_Profile readProfileFromPath(final String profilePath) {
|
||||
if (profilePath != null) {
|
||||
if (DEBUG) {
|
||||
System.out.println("Loading profile from: " + profilePath);
|
||||
}
|
||||
|
||||
try {
|
||||
return ICC_Profile.getInstance(profilePath);
|
||||
}
|
||||
catch (@SuppressWarnings("CatchMayIgnoreException") SecurityException | IOException ignore) {
|
||||
if (DEBUG) {
|
||||
ignore.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static void fixProfile(ICC_Profile profile) {
|
||||
profileCleaner.fixProfile(profile);
|
||||
}
|
||||
|
||||
static boolean validationAltersProfileHeader() {
|
||||
return profileCleaner.validationAltersProfileHeader();
|
||||
}
|
||||
|
||||
// Cache header profile data to avoid excessive array creation/copying. Use static inner class for on-demand lazy init
|
||||
static class sRGB {
|
||||
static final byte[] header = getProfileHeaderWithProfileId(ICC_Profile.getInstance(ColorSpace.CS_sRGB));
|
||||
}
|
||||
|
||||
static class CIEXYZ {
|
||||
static final byte[] header = getProfileHeaderWithProfileId(ICC_Profile.getInstance(ColorSpace.CS_CIEXYZ));
|
||||
}
|
||||
|
||||
static class PYCC {
|
||||
static final byte[] header = getProfileHeaderWithProfileId(ICC_Profile.getInstance(ColorSpace.CS_PYCC));
|
||||
}
|
||||
|
||||
static class GRAY {
|
||||
static final byte[] header = getProfileHeaderWithProfileId(ICC_Profile.getInstance(ColorSpace.CS_GRAY));
|
||||
}
|
||||
|
||||
static class LINEAR_RGB {
|
||||
static final byte[] header = getProfileHeaderWithProfileId(ICC_Profile.getInstance(ColorSpace.CS_LINEAR_RGB));
|
||||
}
|
||||
|
||||
static class Profiles {
|
||||
// TODO: Honour java.iccprofile.path property?
|
||||
private static final Properties PROFILES = loadProfiles();
|
||||
|
||||
private static Properties loadProfiles() {
|
||||
Properties systemDefaults;
|
||||
|
||||
try {
|
||||
systemDefaults = SystemUtil.loadProperties(ColorSpaces.class, "com/twelvemonkeys/imageio/color/icc_profiles_" + Platform.os().id());
|
||||
}
|
||||
catch (@SuppressWarnings("CatchMayIgnoreException") SecurityException | IOException ignore) {
|
||||
System.err.printf(
|
||||
"Warning: Could not load system default ICC profile locations from %s, will use bundled fallback profiles.\n",
|
||||
ignore.getMessage()
|
||||
);
|
||||
|
||||
if (DEBUG) {
|
||||
ignore.printStackTrace();
|
||||
}
|
||||
|
||||
systemDefaults = null;
|
||||
}
|
||||
|
||||
// Create map with defaults and add user overrides if any
|
||||
Properties profiles = new Properties(systemDefaults);
|
||||
|
||||
try {
|
||||
Properties userOverrides = SystemUtil.loadProperties(
|
||||
ColorSpaces.class,
|
||||
"com/twelvemonkeys/imageio/color/icc_profiles"
|
||||
);
|
||||
profiles.putAll(userOverrides);
|
||||
}
|
||||
catch (SecurityException | IOException ignore) {
|
||||
// Most likely, this file won't be there
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
System.out.println("User ICC profiles: " + profiles);
|
||||
System.out.println("System ICC profiles : " + systemDefaults);
|
||||
}
|
||||
|
||||
return profiles;
|
||||
}
|
||||
|
||||
static String getPath(final String profileName) {
|
||||
return PROFILES.getProperty(profileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
+40
-243
@@ -30,23 +30,17 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.color;
|
||||
|
||||
import com.twelvemonkeys.io.FileUtil;
|
||||
import com.twelvemonkeys.lang.Platform;
|
||||
import com.twelvemonkeys.lang.SystemUtil;
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
import com.twelvemonkeys.util.LRUHashMap;
|
||||
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.color.ICC_ColorSpace;
|
||||
import java.awt.color.ICC_Profile;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import static com.twelvemonkeys.imageio.color.ColorProfiles.*;
|
||||
|
||||
/**
|
||||
* A helper class for working with ICC color profiles and color spaces.
|
||||
@@ -83,9 +77,6 @@ public final class ColorSpaces {
|
||||
|
||||
final static boolean DEBUG = "true".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.imageio.color.debug"));
|
||||
|
||||
/** We need special ICC profile handling for KCMS vs LCMS. Delegate to specific strategy. */
|
||||
private final static ICCProfileSanitizer profileCleaner = ICCProfileSanitizer.Factory.get();
|
||||
|
||||
// NOTE: java.awt.color.ColorSpace.CS_* uses 1000-1004, we'll use 5000+ to not interfere with future additions
|
||||
|
||||
/** The Adobe RGB 1998 (or compatible) color space. Either read from disk or built-in. */
|
||||
@@ -96,24 +87,17 @@ public final class ColorSpaces {
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public static final int CS_GENERIC_CMYK = 5001;
|
||||
|
||||
// TODO: Move to ColorProfiles OR cache ICC_ColorSpace instead?
|
||||
// Weak references to hold the color spaces while cached
|
||||
private static WeakReference<ICC_Profile> adobeRGB1998 = new WeakReference<>(null);
|
||||
private static WeakReference<ICC_Profile> genericCMYK = new WeakReference<>(null);
|
||||
|
||||
// Cache for the latest used color spaces
|
||||
private static final Map<Key, ICC_ColorSpace> cache = new LRUHashMap<>(10);
|
||||
private static final Map<Key, ICC_ColorSpace> cache = new LRUHashMap<>(16);
|
||||
|
||||
static {
|
||||
try {
|
||||
// Force invocation of ProfileDeferralMgr.activateProfiles() to avoid JDK-6986863
|
||||
ICC_Profile.getInstance(ColorSpace.CS_sRGB).getData();
|
||||
}
|
||||
catch (Throwable disasters) {
|
||||
System.err.println("ICC Color Profile not properly activated due to the exception below.");
|
||||
System.err.println("Expect to see JDK-6986863 in action, and consider filing a bug report to your JRE provider.");
|
||||
|
||||
disasters.printStackTrace();
|
||||
}
|
||||
// In case we didn't activate through SPI already
|
||||
ProfileDeferralActivator.activateProfiles();
|
||||
}
|
||||
|
||||
private ColorSpaces() {}
|
||||
@@ -134,7 +118,7 @@ public final class ColorSpaces {
|
||||
Validate.notNull(profile, "profile");
|
||||
|
||||
// Fix profile before lookup/create
|
||||
profileCleaner.fixProfile(profile);
|
||||
fixProfile(profile);
|
||||
|
||||
byte[] profileHeader = getProfileHeaderWithProfileId(profile);
|
||||
|
||||
@@ -146,53 +130,20 @@ public final class ColorSpaces {
|
||||
return getCachedOrCreateCS(profile, profileHeader);
|
||||
}
|
||||
|
||||
private static byte[] getProfileHeaderWithProfileId(final ICC_Profile profile) {
|
||||
// Get *entire profile data*... :-/
|
||||
byte[] data = profile.getData();
|
||||
|
||||
// Clear out preferred CMM, platform & creator, as these does not affect the profile in any way
|
||||
// - LCMS updates CMM + creator to "lcms" and platform to current platform
|
||||
// - KCMS keeps the values in the file...
|
||||
Arrays.fill(data, ICC_Profile.icHdrCmmId, ICC_Profile.icHdrCmmId + 4, (byte) 0);
|
||||
Arrays.fill(data, ICC_Profile.icHdrPlatform, ICC_Profile.icHdrPlatform + 4, (byte) 0);
|
||||
// + Clear out rendering intent, as this may be updated by application
|
||||
Arrays.fill(data, ICC_Profile.icHdrRenderingIntent, ICC_Profile.icHdrRenderingIntent + 4, (byte) 0);
|
||||
Arrays.fill(data, ICC_Profile.icHdrCreator, ICC_Profile.icHdrCreator + 4, (byte) 0);
|
||||
|
||||
// Clear out any existing MD5, as it is no longer correct
|
||||
Arrays.fill(data, ICC_Profile.icHdrProfileID, ICC_Profile.icHdrProfileID + 16, (byte) 0);
|
||||
|
||||
// Generate new MD5 and store in header
|
||||
byte[] md5 = computeMD5(data);
|
||||
System.arraycopy(md5, 0, data, ICC_Profile.icHdrProfileID, md5.length);
|
||||
|
||||
// ICC profile header is the first 128 bytes
|
||||
return Arrays.copyOf(data, 128);
|
||||
}
|
||||
|
||||
private static byte[] computeMD5(byte[] data) {
|
||||
try {
|
||||
return MessageDigest.getInstance("MD5").digest(data);
|
||||
}
|
||||
catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalStateException("Missing MD5 MessageDigest");
|
||||
}
|
||||
}
|
||||
|
||||
private static ICC_ColorSpace getInternalCS(final int profileCSType, final byte[] profileHeader) {
|
||||
if (profileCSType == ColorSpace.TYPE_RGB && Arrays.equals(profileHeader, sRGB.header)) {
|
||||
static ICC_ColorSpace getInternalCS(final int profileCSType, final byte[] profileHeader) {
|
||||
if (profileCSType == ColorSpace.TYPE_RGB && Arrays.equals(profileHeader, ColorProfiles.sRGB.header)) {
|
||||
return (ICC_ColorSpace) ColorSpace.getInstance(ColorSpace.CS_sRGB);
|
||||
}
|
||||
else if (profileCSType == ColorSpace.TYPE_GRAY && Arrays.equals(profileHeader, GRAY.header)) {
|
||||
else if (profileCSType == ColorSpace.TYPE_GRAY && Arrays.equals(profileHeader, ColorProfiles.GRAY.header)) {
|
||||
return (ICC_ColorSpace) ColorSpace.getInstance(ColorSpace.CS_GRAY);
|
||||
}
|
||||
else if (profileCSType == ColorSpace.TYPE_3CLR && Arrays.equals(profileHeader, PYCC.header)) {
|
||||
else if (profileCSType == ColorSpace.TYPE_3CLR && Arrays.equals(profileHeader, ColorProfiles.PYCC.header)) {
|
||||
return (ICC_ColorSpace) ColorSpace.getInstance(ColorSpace.CS_PYCC);
|
||||
}
|
||||
else if (profileCSType == ColorSpace.TYPE_RGB && Arrays.equals(profileHeader, LINEAR_RGB.header)) {
|
||||
else if (profileCSType == ColorSpace.TYPE_RGB && Arrays.equals(profileHeader, ColorProfiles.LINEAR_RGB.header)) {
|
||||
return (ICC_ColorSpace) ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB);
|
||||
}
|
||||
else if (profileCSType == ColorSpace.TYPE_XYZ && Arrays.equals(profileHeader, CIEXYZ.header)) {
|
||||
else if (profileCSType == ColorSpace.TYPE_XYZ && Arrays.equals(profileHeader, ColorProfiles.CIEXYZ.header)) {
|
||||
return (ICC_ColorSpace) ColorSpace.getInstance(ColorSpace.CS_CIEXYZ);
|
||||
}
|
||||
|
||||
@@ -203,26 +154,36 @@ public final class ColorSpaces {
|
||||
Key key = new Key(profileHeader);
|
||||
|
||||
synchronized (cache) {
|
||||
ICC_ColorSpace cs = cache.get(key);
|
||||
ICC_ColorSpace cs = getCachedCS(key);
|
||||
|
||||
if (cs == null) {
|
||||
cs = new ICC_ColorSpace(profile);
|
||||
|
||||
validateColorSpace(cs);
|
||||
|
||||
// On LCMS, validation *alters* the profile header, need to re-generate key
|
||||
key = profileCleaner.validationAltersProfileHeader()
|
||||
? new Key(getProfileHeaderWithProfileId(cs.getProfile()))
|
||||
: key;
|
||||
|
||||
cache.put(key, cs);
|
||||
|
||||
// On LCMS, validation *alters* the profile header, need to re-generate key
|
||||
if (ColorProfiles.validationAltersProfileHeader()) {
|
||||
cache.put(new Key(getProfileHeaderWithProfileId(cs.getProfile())), cs);
|
||||
}
|
||||
}
|
||||
|
||||
return cs;
|
||||
}
|
||||
}
|
||||
|
||||
private static void validateColorSpace(final ICC_ColorSpace cs) {
|
||||
private static ICC_ColorSpace getCachedCS(Key profileKey) {
|
||||
synchronized (cache) {
|
||||
return cache.get(profileKey);
|
||||
}
|
||||
}
|
||||
|
||||
static ICC_ColorSpace getCachedCS(final byte[] profileHeader) {
|
||||
return getCachedCS(new Key(profileHeader));
|
||||
}
|
||||
|
||||
static void validateColorSpace(final ICC_ColorSpace cs) {
|
||||
// Validate the color space, to avoid caching bad profiles/color spaces
|
||||
// Will throw IllegalArgumentException or CMMException if the profile is bad
|
||||
cs.fromRGB(new float[] {0.999f, 0.5f, 0.001f});
|
||||
@@ -233,73 +194,27 @@ public final class ColorSpaces {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether an ICC color profile is equal to the default sRGB profile.
|
||||
*
|
||||
* @param profile the ICC profile to test. May not be {@code null}.
|
||||
* @return {@code true} if {@code profile} is equal to the default sRGB profile.
|
||||
* @throws IllegalArgumentException if {@code profile} is {@code null}
|
||||
*
|
||||
* @see java.awt.color.ColorSpace#isCS_sRGB()
|
||||
* @deprecated Use {@link ColorProfiles#isCS_sRGB(ICC_Profile)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean isCS_sRGB(final ICC_Profile profile) {
|
||||
Validate.notNull(profile, "profile");
|
||||
|
||||
return profile.getColorSpaceType() == ColorSpace.TYPE_RGB && Arrays.equals(getProfileHeaderWithProfileId(profile), sRGB.header);
|
||||
return ColorProfiles.isCS_sRGB(profile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether an ICC color profile is known to cause problems for {@link java.awt.image.ColorConvertOp}.
|
||||
* <p>
|
||||
* <em>
|
||||
* Note that this method only tests if a color conversion using this profile is known to fail.
|
||||
* There's no guarantee that the color conversion will succeed even if this method returns {@code false}.
|
||||
* </em>
|
||||
* </p>
|
||||
*
|
||||
* @param profile the ICC color profile. May not be {@code null}.
|
||||
* @return {@code true} if known to be offending, {@code false} otherwise
|
||||
* @throws IllegalArgumentException if {@code profile} is {@code null}
|
||||
* @deprecated Use {@link ColorProfiles#isCS_GRAY(ICC_Profile)} instead.
|
||||
*/
|
||||
static boolean isOffendingColorProfile(final ICC_Profile profile) {
|
||||
Validate.notNull(profile, "profile");
|
||||
|
||||
// NOTE:
|
||||
// Several embedded ICC color profiles are non-compliant with Java pre JDK7 and throws CMMException
|
||||
// The problem with these embedded ICC profiles seems to be the rendering intent
|
||||
// being 1 (01000000) - "Media Relative Colormetric" in the offending profiles,
|
||||
// and 0 (00000000) - "Perceptual" in the good profiles
|
||||
// (that is 1 single bit of difference right there.. ;-)
|
||||
// See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7064516
|
||||
|
||||
// This is particularly annoying, as the byte copying isn't really necessary,
|
||||
// except the getRenderingIntent method is package protected in java.awt.color
|
||||
byte[] header = profile.getData(ICC_Profile.icSigHead);
|
||||
|
||||
return header[ICC_Profile.icHdrRenderingIntent] != 0 || header[ICC_Profile.icHdrRenderingIntent + 1] != 0
|
||||
|| header[ICC_Profile.icHdrRenderingIntent + 2] != 0 || header[ICC_Profile.icHdrRenderingIntent + 3] > 3;
|
||||
@Deprecated
|
||||
public static boolean isCS_GRAY(final ICC_Profile profile) {
|
||||
return ColorProfiles.isCS_GRAY(profile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether an ICC color profile is valid.
|
||||
* Invalid profiles are known to cause problems for {@link java.awt.image.ColorConvertOp}.
|
||||
* <p>
|
||||
* <em>
|
||||
* Note that this method only tests if a color conversion using this profile is known to fail.
|
||||
* There's no guarantee that the color conversion will succeed even if this method returns {@code false}.
|
||||
* </em>
|
||||
* </p>
|
||||
*
|
||||
* @param profile the ICC color profile. May not be {@code null}.
|
||||
* @return {@code profile} if valid.
|
||||
* @throws IllegalArgumentException if {@code profile} is {@code null}
|
||||
* @throws java.awt.color.CMMException if {@code profile} is invalid.
|
||||
* @deprecated Use {@link ColorProfiles#validateProfile(ICC_Profile)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public static ICC_Profile validateProfile(final ICC_Profile profile) {
|
||||
// Fix profile before validation
|
||||
profileCleaner.fixProfile(profile);
|
||||
validateColorSpace(new ICC_ColorSpace(profile));
|
||||
|
||||
return profile;
|
||||
return ColorProfiles.validateProfile(profile);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -382,50 +297,6 @@ public final class ColorSpaces {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
private static ICC_Profile readProfileFromClasspathResource(final String profilePath) {
|
||||
InputStream stream = ColorSpaces.class.getResourceAsStream(profilePath);
|
||||
|
||||
if (stream != null) {
|
||||
if (DEBUG) {
|
||||
System.out.println("Loading profile from classpath resource: " + profilePath);
|
||||
}
|
||||
|
||||
try {
|
||||
return ICC_Profile.getInstance(stream);
|
||||
}
|
||||
catch (IOException ignore) {
|
||||
if (DEBUG) {
|
||||
ignore.printStackTrace();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
FileUtil.close(stream);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static ICC_Profile readProfileFromPath(final String profilePath) {
|
||||
if (profilePath != null) {
|
||||
if (DEBUG) {
|
||||
System.out.println("Loading profile from: " + profilePath);
|
||||
}
|
||||
|
||||
try {
|
||||
return ICC_Profile.getInstance(profilePath);
|
||||
}
|
||||
catch (SecurityException | IOException ignore) {
|
||||
if (DEBUG) {
|
||||
ignore.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static final class Key {
|
||||
private final byte[] data;
|
||||
|
||||
@@ -448,78 +319,4 @@ public final class ColorSpaces {
|
||||
return getClass().getSimpleName() + "@" + Integer.toHexString(hashCode());
|
||||
}
|
||||
}
|
||||
|
||||
// Cache header profile data to avoid excessive array creation/copying in static inner class for on-demand lazy init
|
||||
private static class sRGB {
|
||||
private static final byte[] header = getProfileHeaderWithProfileId(ICC_Profile.getInstance(ColorSpace.CS_sRGB));
|
||||
}
|
||||
|
||||
private static class CIEXYZ {
|
||||
private static final byte[] header = getProfileHeaderWithProfileId(ICC_Profile.getInstance(ColorSpace.CS_CIEXYZ));
|
||||
}
|
||||
|
||||
private static class PYCC {
|
||||
private static final byte[] header = getProfileHeaderWithProfileId(ICC_Profile.getInstance(ColorSpace.CS_PYCC));
|
||||
}
|
||||
|
||||
private static class GRAY {
|
||||
private static final byte[] header = getProfileHeaderWithProfileId(ICC_Profile.getInstance(ColorSpace.CS_GRAY));
|
||||
}
|
||||
|
||||
private static class LINEAR_RGB {
|
||||
private static final byte[] header = getProfileHeaderWithProfileId(ICC_Profile.getInstance(ColorSpace.CS_LINEAR_RGB));
|
||||
}
|
||||
|
||||
private static class Profiles {
|
||||
// TODO: Honour java.iccprofile.path property?
|
||||
private static final Properties PROFILES = loadProfiles();
|
||||
|
||||
private static Properties loadProfiles() {
|
||||
Properties systemDefaults;
|
||||
|
||||
try {
|
||||
systemDefaults = SystemUtil.loadProperties(
|
||||
ColorSpaces.class,
|
||||
"com/twelvemonkeys/imageio/color/icc_profiles_" + Platform.os().id()
|
||||
);
|
||||
}
|
||||
catch (SecurityException | IOException ignore) {
|
||||
System.err.printf(
|
||||
"Warning: Could not load system default ICC profile locations from %s, will use bundled fallback profiles.\n",
|
||||
ignore.getMessage()
|
||||
);
|
||||
|
||||
if (DEBUG) {
|
||||
ignore.printStackTrace();
|
||||
}
|
||||
|
||||
systemDefaults = null;
|
||||
}
|
||||
|
||||
// Create map with defaults and add user overrides if any
|
||||
Properties profiles = new Properties(systemDefaults);
|
||||
|
||||
try {
|
||||
Properties userOverrides = SystemUtil.loadProperties(
|
||||
ColorSpaces.class,
|
||||
"com/twelvemonkeys/imageio/color/icc_profiles"
|
||||
);
|
||||
profiles.putAll(userOverrides);
|
||||
}
|
||||
catch (SecurityException | IOException ignore) {
|
||||
// Most likely, this file won't be there
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
System.out.println("User ICC profiles: " + profiles);
|
||||
System.out.println("System ICC profiles : " + systemDefaults);
|
||||
}
|
||||
|
||||
return profiles;
|
||||
}
|
||||
|
||||
static String getPath(final String profileName) {
|
||||
return PROFILES.getProperty(profileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+20
-20
@@ -54,8 +54,8 @@ public final class DiscreteAlphaIndexColorModel extends ColorModel {
|
||||
// Our IndexColorModel delegate
|
||||
private final IndexColorModel icm;
|
||||
|
||||
private final int extraSamples;
|
||||
private final int samples;
|
||||
private final boolean hasAlpha;
|
||||
|
||||
/**
|
||||
* Creates a {@code DiscreteAlphaIndexColorModel}, delegating color map look-ups
|
||||
@@ -86,33 +86,33 @@ public final class DiscreteAlphaIndexColorModel extends ColorModel {
|
||||
);
|
||||
|
||||
this.icm = icm;
|
||||
this.extraSamples = extraSamples;
|
||||
this.samples = 1 + extraSamples;
|
||||
this.hasAlpha = hasAlpha;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumComponents() {
|
||||
return samples;
|
||||
return getNumColorComponents() + extraSamples;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getRed(final int pixel) {
|
||||
public int getRed(final int pixel) {
|
||||
return icm.getRed(pixel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getGreen(final int pixel) {
|
||||
public int getGreen(final int pixel) {
|
||||
return icm.getGreen(pixel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getBlue(final int pixel) {
|
||||
public int getBlue(final int pixel) {
|
||||
return icm.getBlue(pixel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getAlpha(final int pixel) {
|
||||
return hasAlpha ? (int) ((((float) pixel) / ((1 << getComponentSize(3))-1)) * 255.0f + 0.5f) : 0xff;
|
||||
public int getAlpha(final int pixel) {
|
||||
return hasAlpha() ? (int) ((((float) pixel) / ((1 << getComponentSize(3)) - 1)) * 255.0f + 0.5f) : 0xff;
|
||||
}
|
||||
|
||||
private int getSample(final Object inData, final int index) {
|
||||
@@ -120,15 +120,15 @@ public final class DiscreteAlphaIndexColorModel extends ColorModel {
|
||||
|
||||
switch (transferType) {
|
||||
case DataBuffer.TYPE_BYTE:
|
||||
byte bdata[] = (byte[]) inData;
|
||||
byte[] bdata = (byte[]) inData;
|
||||
pixel = bdata[index] & 0xff;
|
||||
break;
|
||||
case DataBuffer.TYPE_USHORT:
|
||||
short sdata[] = (short[]) inData;
|
||||
short[] sdata = (short[]) inData;
|
||||
pixel = sdata[index] & 0xffff;
|
||||
break;
|
||||
case DataBuffer.TYPE_INT:
|
||||
int idata[] = (int[]) inData;
|
||||
int[] idata = (int[]) inData;
|
||||
pixel = idata[index];
|
||||
break;
|
||||
default:
|
||||
@@ -139,27 +139,27 @@ public final class DiscreteAlphaIndexColorModel extends ColorModel {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getRed(final Object inData) {
|
||||
public int getRed(final Object inData) {
|
||||
return getRed(getSample(inData, 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getGreen(final Object inData) {
|
||||
public int getGreen(final Object inData) {
|
||||
return getGreen(getSample(inData, 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getBlue(final Object inData) {
|
||||
public int getBlue(final Object inData) {
|
||||
return getBlue(getSample(inData, 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getAlpha(final Object inData) {
|
||||
return hasAlpha ? getAlpha(getSample(inData, 1)) : 0xff;
|
||||
public int getAlpha(final Object inData) {
|
||||
return hasAlpha() ? getAlpha(getSample(inData, 1)) : 0xff;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final SampleModel createCompatibleSampleModel(final int w, final int h) {
|
||||
public SampleModel createCompatibleSampleModel(final int w, final int h) {
|
||||
return new PixelInterleavedSampleModel(transferType, w, h, samples, w * samples, createOffsets(samples));
|
||||
}
|
||||
|
||||
@@ -174,17 +174,17 @@ public final class DiscreteAlphaIndexColorModel extends ColorModel {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isCompatibleSampleModel(final SampleModel sm) {
|
||||
public boolean isCompatibleSampleModel(final SampleModel sm) {
|
||||
return sm instanceof PixelInterleavedSampleModel && sm.getNumBands() == samples;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final WritableRaster createCompatibleWritableRaster(final int w, final int h) {
|
||||
public WritableRaster createCompatibleWritableRaster(final int w, final int h) {
|
||||
return Raster.createWritableRaster(createCompatibleSampleModel(w, h), new Point(0, 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isCompatibleRaster(final Raster raster) {
|
||||
public boolean isCompatibleRaster(final Raster raster) {
|
||||
int size = raster.getSampleModel().getSampleSize(0);
|
||||
return ((raster.getTransferType() == transferType) &&
|
||||
(raster.getNumBands() == samples) && ((1 << size) >= icm.getMapSize()));
|
||||
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.color;
|
||||
|
||||
import javax.imageio.spi.ImageInputStreamSpi;
|
||||
import javax.imageio.spi.ServiceRegistry;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.File;
|
||||
import java.util.Locale;
|
||||
|
||||
import static com.twelvemonkeys.imageio.util.IIOUtil.deregisterProvider;
|
||||
|
||||
/**
|
||||
* This class exists to force early invocation of {@code ProfileDeferralMgr.activateProfiles()},
|
||||
* in an attempt to avoid JDK-6986863 and related bugs in Java < 17.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @see <a href="https://bugs.openjdk.java.net/browse/JDK-6986863">JDK-6986863</a>
|
||||
*/
|
||||
final class ProfileDeferralActivator {
|
||||
|
||||
static {
|
||||
activateProfilesInternal();
|
||||
}
|
||||
|
||||
private static void activateProfilesInternal() {
|
||||
try {
|
||||
// Force invocation of ProfileDeferralMgr.activateProfiles() to avoid JDK-6986863 and friends.
|
||||
// Relies on static initializer in ColorConvertOp to actually invoke ProfileDeferralMgr.activateProfiles()
|
||||
Class.forName("java.awt.image.ColorConvertOp");
|
||||
}
|
||||
catch (Throwable disasters) {
|
||||
System.err.println("ProfileDeferralMgr.activateProfiles() failed. ICC Color Profiles may not work properly, see stack trace below.");
|
||||
System.err.println("For more information, see https://bugs.openjdk.java.net/browse/JDK-6986863");
|
||||
System.err.println("Please upgrade to Java 17 or later where this bug is fixed, or ask your JRE provider to backport the fix.");
|
||||
System.err.println();
|
||||
System.err.println("If you can't update to Java 17, a possible workaround is to add");
|
||||
System.err.println("\tClass.forName(\"java.awt.image.ColorConvertOp\");");
|
||||
System.err.println("*early* in your application startup code, to force profile activation before profiles are accessed.");
|
||||
System.err.println();
|
||||
|
||||
disasters.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
static void activateProfiles() {
|
||||
// This method exists for other classes in the package to
|
||||
// ensure this class' static initializer is run.
|
||||
}
|
||||
|
||||
/**
|
||||
* This is not a service provider, but exploits the SPI mechanism as a hook to force early profile activation.
|
||||
*/
|
||||
public static final class Spi extends ImageInputStreamSpi {
|
||||
@Override public void onRegistration(ServiceRegistry registry, Class<?> category) {
|
||||
activateProfiles();
|
||||
|
||||
deregisterProvider(registry, this, category);
|
||||
}
|
||||
|
||||
@Override public String getDescription(Locale locale) {
|
||||
return getClass().getName();
|
||||
}
|
||||
|
||||
@Override public ImageInputStream createInputStreamInstance(Object input, boolean useCache, File cacheDir) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
+87
-31
@@ -45,36 +45,82 @@ public final class YCbCrConverter {
|
||||
private final static int CENTERJSAMPLE = 128;
|
||||
private final static int ONE_HALF = 1 << (SCALEBITS - 1);
|
||||
|
||||
private final static int[] Cr_R_LUT = new int[MAXJSAMPLE + 1];
|
||||
private final static int[] Cb_B_LUT = new int[MAXJSAMPLE + 1];
|
||||
private final static int[] Cr_G_LUT = new int[MAXJSAMPLE + 1];
|
||||
private final static int[] Cb_G_LUT = new int[MAXJSAMPLE + 1];
|
||||
private final static class JPEG {
|
||||
private final static int[] Cr_R_LUT = new int[MAXJSAMPLE + 1];
|
||||
private final static int[] Cb_B_LUT = new int[MAXJSAMPLE + 1];
|
||||
private final static int[] Cr_G_LUT = new int[MAXJSAMPLE + 1];
|
||||
private final static int[] Cb_G_LUT = new int[MAXJSAMPLE + 1];
|
||||
|
||||
/**
|
||||
* Initializes tables for YCC->RGB color space conversion.
|
||||
*/
|
||||
private static void buildYCCtoRGBtable() {
|
||||
if (ColorSpaces.DEBUG) {
|
||||
System.err.println("Building YCC conversion table");
|
||||
/**
|
||||
* Initializes tables for YCC->RGB color space conversion.
|
||||
*/
|
||||
private static void buildYCCtoRGBtable() {
|
||||
if (ColorSpaces.DEBUG) {
|
||||
System.err.println("Building JPEG YCbCr conversion table");
|
||||
}
|
||||
|
||||
for (int i = 0, x = -CENTERJSAMPLE; i <= MAXJSAMPLE; i++, x++) {
|
||||
// i is the actual input pixel value, in the range 0..MAXJSAMPLE
|
||||
// The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE
|
||||
// Cr=>R value is nearest int to 1.40200 * x
|
||||
Cr_R_LUT[i] = (int) ((1.40200 * (1 << SCALEBITS) + 0.5) * x + ONE_HALF) >> SCALEBITS;
|
||||
// Cb=>B value is nearest int to 1.77200 * x
|
||||
Cb_B_LUT[i] = (int) ((1.77200 * (1 << SCALEBITS) + 0.5) * x + ONE_HALF) >> SCALEBITS;
|
||||
// Cr=>G value is scaled-up -0.71414 * x
|
||||
Cr_G_LUT[i] = -(int) (0.71414 * (1 << SCALEBITS) + 0.5) * x;
|
||||
// Cb=>G value is scaled-up -0.34414 * x
|
||||
// We also add in ONE_HALF so that need not do it in inner loop
|
||||
Cb_G_LUT[i] = -(int) ((0.34414) * (1 << SCALEBITS) + 0.5) * x + ONE_HALF;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0, x = -CENTERJSAMPLE; i <= MAXJSAMPLE; i++, x++) {
|
||||
// i is the actual input pixel value, in the range 0..MAXJSAMPLE
|
||||
// The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE
|
||||
// Cr=>R value is nearest int to 1.40200 * x
|
||||
Cr_R_LUT[i] = (int) ((1.40200 * (1 << SCALEBITS) + 0.5) * x + ONE_HALF) >> SCALEBITS;
|
||||
// Cb=>B value is nearest int to 1.77200 * x
|
||||
Cb_B_LUT[i] = (int) ((1.77200 * (1 << SCALEBITS) + 0.5) * x + ONE_HALF) >> SCALEBITS;
|
||||
// Cr=>G value is scaled-up -0.71414 * x
|
||||
Cr_G_LUT[i] = -(int) (0.71414 * (1 << SCALEBITS) + 0.5) * x;
|
||||
// Cb=>G value is scaled-up -0.34414 * x
|
||||
// We also add in ONE_HALF so that need not do it in inner loop
|
||||
Cb_G_LUT[i] = -(int) ((0.34414) * (1 << SCALEBITS) + 0.5) * x + ONE_HALF;
|
||||
static {
|
||||
buildYCCtoRGBtable();
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
buildYCCtoRGBtable();
|
||||
private final static class ITU_R_601 {
|
||||
private final static int[] Cr_R_LUT = new int[MAXJSAMPLE + 1];
|
||||
private final static int[] Cb_B_LUT = new int[MAXJSAMPLE + 1];
|
||||
private final static int[] Cr_G_LUT = new int[MAXJSAMPLE + 1];
|
||||
private final static int[] Cb_G_LUT = new int[MAXJSAMPLE + 1];
|
||||
private final static int[] Y_LUT = new int[MAXJSAMPLE + 1];
|
||||
|
||||
/**
|
||||
* Initializes tables for YCC->RGB color space conversion.
|
||||
*/
|
||||
private static void buildYCCtoRGBtable() {
|
||||
if (ColorSpaces.DEBUG) {
|
||||
System.err.println("Building ITU-R REC.601 YCbCr conversion table");
|
||||
}
|
||||
|
||||
for (int i = 0, x = -CENTERJSAMPLE; i <= MAXJSAMPLE; i++, x++) {
|
||||
// i is the actual input pixel value, in the range 0..MAXJSAMPLE
|
||||
// The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE
|
||||
|
||||
// Y'CbCr to RGB conversion, using values from BT.601 specification:
|
||||
// R = 1.16438 * (Y'-16) + 1.59603 * (Cr-128)
|
||||
// G = 1.16438 * (Y'-16) - 0.39176 * (Cb-128) - 0.81297 * (Cr-128)
|
||||
// B = 1.16438 * (Y'-16) + 2.01723 * (Cb-128)
|
||||
|
||||
// Cr=>R value is nearest int to 1.59603 * x
|
||||
Cr_R_LUT[i] = ((int) (1.59603 * (1 << SCALEBITS) + 0.5) * x + ONE_HALF) >> SCALEBITS;
|
||||
// Cb=>B value is nearest int to 2.01723 * x
|
||||
Cb_B_LUT[i] = ((int) (2.01723 * (1 << SCALEBITS) + 0.5) * x + ONE_HALF) >> SCALEBITS;
|
||||
// Cr=>G value is scaled-up -0.81297 * x
|
||||
Cr_G_LUT[i] = -(int) (0.81297 * (1 << SCALEBITS) + 0.5) * x;
|
||||
// Cb=>G value is scaled-up -0.39176 * x
|
||||
// We also add in ONE_HALF so that need not do it in inner loop
|
||||
Cb_G_LUT[i] = -(int) ((0.39176) * (1 << SCALEBITS) + 0.5) * x + ONE_HALF;
|
||||
|
||||
// Y`=>RGB
|
||||
Y_LUT[i] = ((int) (1.16438 * (1 << SCALEBITS) + 0.5) * (i - 16) + ONE_HALF) >> SCALEBITS;
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
buildYCCtoRGBtable();
|
||||
}
|
||||
}
|
||||
|
||||
public static void convertYCbCr2RGB(final byte[] yCbCr, final byte[] rgb, final double[] coefficients, double[] referenceBW, final int offset) {
|
||||
@@ -108,17 +154,27 @@ public final class YCbCrConverter {
|
||||
rgb[offset + 1] = clamp(green);
|
||||
}
|
||||
|
||||
public static void convertYCbCr2RGB(final byte[] yCbCr, final byte[] rgb, final int offset) {
|
||||
int y = yCbCr[offset] & 0xff;
|
||||
int cr = yCbCr[offset + 2] & 0xff;
|
||||
public static void convertJPEGYCbCr2RGB(final byte[] yCbCr, final byte[] rgb, final int offset) {
|
||||
int y = yCbCr[offset ] & 0xff;
|
||||
int cb = yCbCr[offset + 1] & 0xff;
|
||||
int cr = yCbCr[offset + 2] & 0xff;
|
||||
|
||||
rgb[offset] = clamp(y + Cr_R_LUT[cr]);
|
||||
rgb[offset + 1] = clamp(y + (Cb_G_LUT[cb] + Cr_G_LUT[cr] >> SCALEBITS));
|
||||
rgb[offset + 2] = clamp(y + Cb_B_LUT[cb]);
|
||||
rgb[offset ] = clamp(y + JPEG.Cr_R_LUT[cr]);
|
||||
rgb[offset + 1] = clamp(y + (JPEG.Cb_G_LUT[cb] + JPEG.Cr_G_LUT[cr] >> SCALEBITS));
|
||||
rgb[offset + 2] = clamp(y + JPEG.Cb_B_LUT[cb]);
|
||||
}
|
||||
|
||||
private static byte clamp(int val) {
|
||||
public static void convertRec601YCbCr2RGB(final byte[] yCbCr, final byte[] rgb, final int offset) {
|
||||
int y = yCbCr[offset ] & 0xff;
|
||||
int cb = yCbCr[offset + 1] & 0xff;
|
||||
int cr = yCbCr[offset + 2] & 0xff;
|
||||
|
||||
rgb[offset ] = clamp(ITU_R_601.Y_LUT[y] + ITU_R_601.Cr_R_LUT[cr]);
|
||||
rgb[offset + 1] = clamp(ITU_R_601.Y_LUT[y] + (ITU_R_601.Cr_G_LUT[cr] + ITU_R_601.Cb_G_LUT[cb] >> SCALEBITS));
|
||||
rgb[offset + 2] = clamp(ITU_R_601.Y_LUT[y] + ITU_R_601.Cb_B_LUT[cb]);
|
||||
}
|
||||
|
||||
private static byte clamp(final int val) {
|
||||
return (byte) Math.max(0, Math.min(255, val));
|
||||
}
|
||||
}
|
||||
|
||||
+23
-15
@@ -93,8 +93,8 @@ public final class BufferedFileImageInputStream extends ImageInputStreamImpl {
|
||||
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
private boolean fillBuffer() throws IOException {
|
||||
bufferPos = 0;
|
||||
int length = raf.read(buffer, 0, buffer.length);
|
||||
bufferPos = 0;
|
||||
bufferLimit = max(length, 0);
|
||||
|
||||
return bufferLimit > 0;
|
||||
@@ -125,27 +125,35 @@ public final class BufferedFileImageInputStream extends ImageInputStreamImpl {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(final byte[] pBuffer, final int pOffset, final int pLength) throws IOException {
|
||||
public int read(final byte[] bytes, final int offset, final int length) throws IOException {
|
||||
checkClosed();
|
||||
bitOffset = 0;
|
||||
|
||||
if (bufferEmpty()) {
|
||||
// Bypass buffer if buffer is empty for reads longer than buffer
|
||||
if (pLength >= buffer.length) {
|
||||
return readDirect(pBuffer, pOffset, pLength);
|
||||
if (length >= buffer.length) {
|
||||
return readDirect(bytes, offset, length);
|
||||
}
|
||||
else if (!fillBuffer()) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return readBuffered(pBuffer, pOffset, pLength);
|
||||
int fromBuffer = readBuffered(bytes, offset, length);
|
||||
|
||||
if (length > fromBuffer) {
|
||||
// Due to known bugs in certain JDK-bundled ImageIO plugins expecting read to behave as readFully,
|
||||
// we'll read as much as possible from the buffer, and the rest directly after
|
||||
return fromBuffer + max(0, readDirect(bytes, offset + fromBuffer, length - fromBuffer));
|
||||
}
|
||||
|
||||
return fromBuffer;
|
||||
}
|
||||
|
||||
private int readDirect(final byte[] pBuffer, final int pOffset, final int pLength) throws IOException {
|
||||
private int readDirect(final byte[] bytes, final int offset, final int length) throws IOException {
|
||||
// Invalidate the buffer, as its contents is no longer in sync with the stream's position.
|
||||
bufferLimit = 0;
|
||||
int read = raf.read(pBuffer, pOffset, pLength);
|
||||
int read = raf.read(bytes, offset, length);
|
||||
|
||||
if (read > 0) {
|
||||
streamPos += read;
|
||||
@@ -154,17 +162,17 @@ public final class BufferedFileImageInputStream extends ImageInputStreamImpl {
|
||||
return read;
|
||||
}
|
||||
|
||||
private int readBuffered(final byte[] pBuffer, final int pOffset, final int pLength) {
|
||||
private int readBuffered(final byte[] bytes, final int offset, final int length) {
|
||||
// Read as much as possible from buffer
|
||||
int length = Math.min(bufferLimit - bufferPos, pLength);
|
||||
int available = Math.min(bufferLimit - bufferPos, length);
|
||||
|
||||
if (length > 0) {
|
||||
System.arraycopy(buffer, bufferPos, pBuffer, pOffset, length);
|
||||
bufferPos += length;
|
||||
streamPos += length;
|
||||
if (available > 0) {
|
||||
System.arraycopy(buffer, bufferPos, bytes, offset, available);
|
||||
bufferPos += available;
|
||||
streamPos += available;
|
||||
}
|
||||
|
||||
return length;
|
||||
return available;
|
||||
}
|
||||
|
||||
public long length() {
|
||||
@@ -237,7 +245,7 @@ public final class BufferedFileImageInputStream extends ImageInputStreamImpl {
|
||||
|
||||
// Optimized to not invalidate buffer if new position is within current buffer
|
||||
long newBufferPos = bufferPos + position - streamPos;
|
||||
if (newBufferPos >= 0 && newBufferPos <= bufferLimit) {
|
||||
if (newBufferPos >= 0 && newBufferPos < bufferLimit) {
|
||||
bufferPos = (int) newBufferPos;
|
||||
}
|
||||
else {
|
||||
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.stream;
|
||||
|
||||
import javax.imageio.stream.ImageInputStreamImpl;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import static com.twelvemonkeys.lang.Validate.isTrue;
|
||||
import static com.twelvemonkeys.lang.Validate.notNull;
|
||||
|
||||
/**
|
||||
* An {@code ImageInputStream} that adapts an {@code InputSteam},
|
||||
* by reading directly from the stream without and form of caching or buffering.
|
||||
* <p>
|
||||
* Note: This is <em>not</em> a general-purpose {@code ImageInputStream}, and is designed for reading large chunks,
|
||||
* typically of pixel data, from an {@code InputStream}.
|
||||
* It does <em>not</em> support backwards seeking, or reading bits.
|
||||
* </p>
|
||||
*/
|
||||
public final class DirectImageInputStream extends ImageInputStreamImpl {
|
||||
private final InputStream stream;
|
||||
private final long length;
|
||||
|
||||
public DirectImageInputStream(final InputStream stream) {
|
||||
this(stream, -1L);
|
||||
}
|
||||
|
||||
public DirectImageInputStream(final InputStream stream, long length) {
|
||||
this.stream = notNull(stream, "stream");
|
||||
this.length = isTrue(length >= 0L || length == -1L, length, "negative length: %d");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
bitOffset = 0;
|
||||
streamPos++;
|
||||
return stream.read();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(final byte[] bytes, int off, int len) throws IOException {
|
||||
bitOffset = 0;
|
||||
|
||||
int read = stream.read(bytes, off, len);
|
||||
if (read > 0) {
|
||||
streamPos += read;
|
||||
}
|
||||
|
||||
return read;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seek(long pos) throws IOException {
|
||||
checkClosed();
|
||||
|
||||
if (pos < streamPos) {
|
||||
// Handle as if flushedPos == streamPos at any time
|
||||
throw new IndexOutOfBoundsException("pos < flushedPos");
|
||||
}
|
||||
|
||||
bitOffset = 0;
|
||||
|
||||
while (streamPos < pos) {
|
||||
long skipped = stream.skip(pos - streamPos);
|
||||
|
||||
if (skipped <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
streamPos += skipped;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getFlushedPosition() {
|
||||
// Handle as if flushedPos == streamPos at any time
|
||||
return streamPos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long length() {
|
||||
return length;
|
||||
}
|
||||
|
||||
@SuppressWarnings("RedundantThrows")
|
||||
@Override
|
||||
public int readBit() throws IOException {
|
||||
throw new UnsupportedOperationException("Bit reading not supported");
|
||||
}
|
||||
|
||||
@SuppressWarnings("RedundantThrows")
|
||||
@Override
|
||||
public long readBits(int numBits) throws IOException {
|
||||
throw new UnsupportedOperationException("Bit reading not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
// We could seek to EOF here, but the usual case
|
||||
|
||||
stream.close();
|
||||
super.close();
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -81,7 +81,7 @@ class IIOInputStreamAdapter extends InputStream {
|
||||
|
||||
private IIOInputStreamAdapter(ImageInputStream pInput, long pLength, boolean pHasLength) {
|
||||
Validate.notNull(pInput, "stream");
|
||||
Validate.isTrue(!pHasLength || pLength >= 0, pLength, "length < 0: %f");
|
||||
Validate.isTrue(!pHasLength || pLength >= 0, pLength, "length < 0: %d");
|
||||
|
||||
input = pInput;
|
||||
left = pLength;
|
||||
|
||||
@@ -45,6 +45,7 @@ import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Iterator;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
@@ -158,33 +159,39 @@ public final class IIOUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* THIS METHOD WILL ME MOVED/RENAMED, DO NOT USE.
|
||||
* THIS METHOD WILL BE MOVED/RENAMED, DO NOT USE.
|
||||
*
|
||||
* @param registry the registry to unregister from.
|
||||
* @param provider the provider to unregister.
|
||||
* @param category the category to unregister from.
|
||||
*/
|
||||
public static <T> void deregisterProvider(final ServiceRegistry registry, final IIOServiceProvider provider, final Class<T> category) {
|
||||
// http://www.ibm.com/developerworks/java/library/j-jtp04298.html
|
||||
registry.deregisterServiceProvider(category.cast(provider), category);
|
||||
}
|
||||
|
||||
/**
|
||||
* THIS METHOD WILL ME MOVED/RENAMED, DO NOT USE.
|
||||
* THIS METHOD WILL BE MOVED/RENAMED, DO NOT USE.
|
||||
*
|
||||
* @param registry the registry to lookup from.
|
||||
* @param providerClassName name of the provider class.
|
||||
* @param category provider category
|
||||
*
|
||||
* @return the provider instance, or {@code null}.
|
||||
* @return the provider instance, or {@code null} if not found
|
||||
*/
|
||||
public static <T> T lookupProviderByName(final ServiceRegistry registry, final String providerClassName, Class<T> category) {
|
||||
try {
|
||||
return category.cast(registry.getServiceProviderByClass(Class.forName(providerClassName)));
|
||||
}
|
||||
catch (ClassNotFoundException ignore) {
|
||||
return null;
|
||||
// NOTE: While more verbose, this is more OSGi-friendly than using
|
||||
// registry.getServiceProviderByClass(Class.forName(providerClassName))
|
||||
Iterator<T> providers = registry.getServiceProviders(category, true);
|
||||
|
||||
while (providers.hasNext()) {
|
||||
T provider = providers.next();
|
||||
|
||||
if (provider.getClass().getName().equals(providerClassName)) {
|
||||
return provider;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.util;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.*;
|
||||
|
||||
import static com.twelvemonkeys.lang.Validate.notNull;
|
||||
|
||||
/**
|
||||
* A class containing various raster utility methods.
|
||||
*/
|
||||
public final class RasterUtils {
|
||||
|
||||
private RasterUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a raster with {@code DataBuffer.TYPE_BYTE} transfer type.
|
||||
* Works for any raster from a {@code BufferedImage.TYPE_INT_*} image
|
||||
*
|
||||
* @param raster a {@code Raster} with either transfer type {@code DataBuffer.TYPE_BYTE}
|
||||
* or {@code DataBuffer.TYPE_INT} with `SinglePixelPackedSampleModel`, not {@code null}.
|
||||
* @return a raster with {@code DataBuffer.TYPE_BYTE} transfer type.
|
||||
* @throws IllegalArgumentException if {@code raster} does not have transfer type {@code DataBuffer.TYPE_BYTE}
|
||||
* or {@code DataBuffer.TYPE_INT} with `SinglePixelPackedSampleModel`
|
||||
* @throws NullPointerException if {@code raster} is {@code null}.
|
||||
*/
|
||||
public static Raster asByteRaster(final Raster raster) {
|
||||
return asByteRaster0(raster);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a writable raster with {@code DataBuffer.TYPE_BYTE} transfer type.
|
||||
* Works for any raster from a {@code BufferedImage.TYPE_INT_*} image.
|
||||
*
|
||||
* @param raster a {@code WritableRaster} with either transfer type {@code DataBuffer.TYPE_BYTE}
|
||||
* or {@code DataBuffer.TYPE_INT} with `SinglePixelPackedSampleModel`, not {@code null}.
|
||||
* @return a writable raster with {@code DataBuffer.TYPE_BYTE} transfer type.
|
||||
* @throws IllegalArgumentException if {@code raster} does not have transfer type {@code DataBuffer.TYPE_BYTE}
|
||||
* or {@code DataBuffer.TYPE_INT} with `SinglePixelPackedSampleModel`
|
||||
* @throws NullPointerException if {@code raster} is {@code null}.
|
||||
*/
|
||||
public static WritableRaster asByteRaster(final WritableRaster raster) {
|
||||
return (WritableRaster) asByteRaster0(raster);
|
||||
}
|
||||
|
||||
private static Raster asByteRaster0(final Raster raster) {
|
||||
switch (raster.getTransferType()) {
|
||||
case DataBuffer.TYPE_BYTE:
|
||||
return raster;
|
||||
case DataBuffer.TYPE_INT:
|
||||
SampleModel sampleModel = raster.getSampleModel();
|
||||
|
||||
if (!(sampleModel instanceof SinglePixelPackedSampleModel)) {
|
||||
throw new IllegalArgumentException(String.format("Requires SinglePixelPackedSampleModel, %s not supported", sampleModel.getClass().getSimpleName()));
|
||||
}
|
||||
|
||||
final int bands = 4;
|
||||
final DataBufferInt buffer = (DataBufferInt) raster.getDataBuffer();
|
||||
|
||||
int w = raster.getWidth();
|
||||
int h = raster.getHeight();
|
||||
int size = buffer.getSize();
|
||||
|
||||
return new WritableRaster(
|
||||
new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE, w, h, bands, w * bands, createBandOffsets((SinglePixelPackedSampleModel) sampleModel)),
|
||||
new DataBuffer(DataBuffer.TYPE_BYTE, size * bands) {
|
||||
final int[] MASKS = {
|
||||
0xffffff00,
|
||||
0xffff00ff,
|
||||
0xff00ffff,
|
||||
0x00ffffff,
|
||||
};
|
||||
|
||||
@Override
|
||||
public int getElem(int bank, int i) {
|
||||
int index = i / bands;
|
||||
int shift = (i % bands) * 8;
|
||||
|
||||
return (buffer.getElem(index) >>> shift) & 0xff;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setElem(int bank, int i, int val) {
|
||||
int index = i / bands;
|
||||
int element = i % bands;
|
||||
int shift = element * 8;
|
||||
|
||||
int value = (buffer.getElem(index) & MASKS[element]) | ((val & 0xff) << shift);
|
||||
buffer.setElem(index, value);
|
||||
}
|
||||
}, new Point()) {
|
||||
};
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException(String.format("Raster type %d not supported", raster.getTransferType()));
|
||||
}
|
||||
}
|
||||
|
||||
private static int[] createBandOffsets(final SinglePixelPackedSampleModel sampleModel) {
|
||||
notNull(sampleModel, "sampleModel");
|
||||
|
||||
int[] masks = sampleModel.getBitMasks();
|
||||
int[] offs = new int[masks.length];
|
||||
|
||||
for (int i = 0; i < masks.length; i++) {
|
||||
int mask = masks[i];
|
||||
int off = 0;
|
||||
|
||||
// TODO: FixMe! This only works for standard 8 bit masks (0xFF)
|
||||
if (mask != 0) {
|
||||
while ((mask & 0xFF) == 0) {
|
||||
mask >>>= 8;
|
||||
off++;
|
||||
}
|
||||
}
|
||||
|
||||
offs[i] = off;
|
||||
}
|
||||
|
||||
return offs;
|
||||
}
|
||||
}
|
||||
+2
@@ -1,2 +1,4 @@
|
||||
com.twelvemonkeys.imageio.stream.BufferedFileImageInputStreamSpi
|
||||
com.twelvemonkeys.imageio.stream.BufferedRAFImageInputStreamSpi
|
||||
# Use SPI loading as a hook for early profile activation
|
||||
com.twelvemonkeys.imageio.color.ProfileDeferralActivator$Spi
|
||||
|
||||
+247
@@ -0,0 +1,247 @@
|
||||
package com.twelvemonkeys.imageio.color;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.color.ICC_ColorSpace;
|
||||
import java.awt.color.ICC_Profile;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class ColorProfilesTest {
|
||||
@Test
|
||||
public void testCreateColorSpaceFromBrokenProfileIsFixedCS_sRGB() {
|
||||
ICC_Profile internal = ICC_Profile.getInstance(ColorSpace.CS_sRGB);
|
||||
ICC_Profile profile = createBrokenProfile(internal);
|
||||
assertNotSame(internal, profile); // Sanity check
|
||||
|
||||
assertTrue(ColorProfiles.isOffendingColorProfile(profile));
|
||||
|
||||
ICC_ColorSpace created = ColorSpaces.createColorSpace(profile);
|
||||
assertSame(ColorSpace.getInstance(ColorSpace.CS_sRGB), created);
|
||||
assertTrue(created.isCS_sRGB());
|
||||
}
|
||||
|
||||
private ICC_Profile createBrokenProfile(ICC_Profile internal) {
|
||||
byte[] data = internal.getData();
|
||||
data[ICC_Profile.icHdrRenderingIntent] = 1; // Intent: 1 == Relative Colormetric Little Endian
|
||||
data[ICC_Profile.icHdrRenderingIntent + 1] = 0;
|
||||
data[ICC_Profile.icHdrRenderingIntent + 2] = 0;
|
||||
data[ICC_Profile.icHdrRenderingIntent + 3] = 0;
|
||||
return ICC_Profile.getInstance(data);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsOffendingColorProfile() {
|
||||
ICC_Profile broken = createBrokenProfile(ICC_Profile.getInstance(ColorSpace.CS_GRAY));
|
||||
assertTrue(ColorProfiles.isOffendingColorProfile(broken));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsCS_sRGBTrue() {
|
||||
assertTrue(ColorProfiles.isCS_sRGB(ICC_Profile.getInstance(ColorSpace.CS_sRGB)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsCS_sRGBFalse() {
|
||||
assertFalse(ColorProfiles.isCS_sRGB(ICC_Profile.getInstance(ColorSpace.CS_LINEAR_RGB)));
|
||||
assertFalse(ColorProfiles.isCS_sRGB(ICC_Profile.getInstance(ColorSpace.CS_CIEXYZ)));
|
||||
assertFalse(ColorProfiles.isCS_sRGB(ICC_Profile.getInstance(ColorSpace.CS_GRAY)));
|
||||
assertFalse(ColorProfiles.isCS_sRGB(ICC_Profile.getInstance(ColorSpace.CS_PYCC)));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testIsCS_sRGBNull() {
|
||||
ColorProfiles.isCS_sRGB(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsCS_GRAYTrue() {
|
||||
assertTrue(ColorProfiles.isCS_GRAY(ICC_Profile.getInstance(ColorSpace.CS_GRAY)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsCS_GRAYFalse() {
|
||||
assertFalse(ColorProfiles.isCS_GRAY(ICC_Profile.getInstance(ColorSpace.CS_sRGB)));
|
||||
assertFalse(ColorProfiles.isCS_GRAY(ICC_Profile.getInstance(ColorSpace.CS_LINEAR_RGB)));
|
||||
assertFalse(ColorProfiles.isCS_GRAY(ICC_Profile.getInstance(ColorSpace.CS_CIEXYZ)));
|
||||
assertFalse(ColorProfiles.isCS_GRAY(ICC_Profile.getInstance(ColorSpace.CS_PYCC)));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testIsCS_GRAYNull() {
|
||||
ColorProfiles.isCS_GRAY(null);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testCreateProfileNull() {
|
||||
ColorProfiles.createProfile(null);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testReadProfileNull() throws IOException {
|
||||
ColorProfiles.readProfile(null);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testCreateProfileRawNull() {
|
||||
ColorProfiles.createProfileRaw(null);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testReadProfileRawNull() throws IOException {
|
||||
ColorProfiles.readProfileRaw(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateProfileRaw() throws IOException {
|
||||
byte[] data = ICC_Profile.getInstance(getClass().getResourceAsStream("/profiles/adobe_rgb_1998.icc")).getData();
|
||||
ICC_Profile profileRaw = ColorProfiles.createProfileRaw(data);
|
||||
assertArrayEquals(data, profileRaw.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadProfileRaw() throws IOException {
|
||||
byte[] data = ICC_Profile.getInstance(getClass().getResourceAsStream("/profiles/adobe_rgb_1998.icc")).getData();
|
||||
ICC_Profile profileRaw = ColorProfiles.readProfileRaw(getClass().getResourceAsStream("/profiles/adobe_rgb_1998.icc"));
|
||||
assertArrayEquals(data, profileRaw.getData());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testCreateProfileRawBadData() {
|
||||
ColorProfiles.createProfileRaw(new byte[5]);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testReadProfileRawBadData() throws IOException {
|
||||
// NOTE: The array here is larger, as there's a bug in OpenJDK 15 & 16, that throws
|
||||
// ArrayIndexOutOfBoundsException if the stream is shorter than the profile signature...
|
||||
ColorProfiles.readProfileRaw(new ByteArrayInputStream(new byte[40]));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testCreateProfileBadData() {
|
||||
ColorProfiles.createProfile(new byte[5]);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testReadProfileBadData() throws IOException {
|
||||
ColorProfiles.readProfile(new ByteArrayInputStream(new byte[5]));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testCreateProfileRawTruncated() throws IOException {
|
||||
byte[] data = ICC_Profile.getInstance(getClass().getResourceAsStream("/profiles/adobe_rgb_1998.icc")).getData();
|
||||
ColorProfiles.createProfileRaw(Arrays.copyOf(data, 200));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testReadProfileRawTruncated() throws IOException {
|
||||
byte[] data = ICC_Profile.getInstance(getClass().getResourceAsStream("/profiles/adobe_rgb_1998.icc")).getData();
|
||||
ColorProfiles.readProfileRaw(new ByteArrayInputStream(data, 0, 200));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testCreateProfileTruncated() throws IOException {
|
||||
byte[] data = ICC_Profile.getInstance(getClass().getResourceAsStream("/profiles/adobe_rgb_1998.icc")).getData();
|
||||
ColorProfiles.createProfile(Arrays.copyOf(data, 200));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testReadProfileTruncated() throws IOException {
|
||||
byte[] data = ICC_Profile.getInstance(getClass().getResourceAsStream("/profiles/adobe_rgb_1998.icc")).getData();
|
||||
ColorProfiles.readProfile(new ByteArrayInputStream(data, 0, 200));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testCreateProfileRawTruncatedHeader() throws IOException {
|
||||
byte[] data = ICC_Profile.getInstance(getClass().getResourceAsStream("/profiles/adobe_rgb_1998.icc")).getData();
|
||||
ColorProfiles.createProfileRaw(Arrays.copyOf(data, 125));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testReadProfileRawTruncatedHeader() throws IOException {
|
||||
byte[] data = ICC_Profile.getInstance(getClass().getResourceAsStream("/profiles/adobe_rgb_1998.icc")).getData();
|
||||
ColorProfiles.readProfileRaw(new ByteArrayInputStream(data, 0, 125));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testCreateProfileTruncatedHeader() throws IOException {
|
||||
byte[] data = ICC_Profile.getInstance(getClass().getResourceAsStream("/profiles/adobe_rgb_1998.icc")).getData();
|
||||
ColorProfiles.createProfile(Arrays.copyOf(data, 125));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testReadProfileTruncatedHeader() throws IOException {
|
||||
byte[] data = ICC_Profile.getInstance(getClass().getResourceAsStream("/profiles/adobe_rgb_1998.icc")).getData();
|
||||
ColorProfiles.readProfile(new ByteArrayInputStream(data, 0, 125));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateProfileBytesSame() throws IOException {
|
||||
ICC_Profile profile = ICC_Profile.getInstance(getClass().getResourceAsStream("/profiles/adobe_rgb_1998.icc"));
|
||||
ICC_Profile profile1 = ColorProfiles.createProfile(profile.getData());
|
||||
ICC_Profile profile2 = ColorProfiles.createProfile(profile.getData());
|
||||
|
||||
assertEquals(profile1, profile2);
|
||||
assertSame(profile1, profile2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadProfileInputStreamSame() throws IOException {
|
||||
ICC_Profile profile1 = ColorProfiles.readProfile(getClass().getResourceAsStream("/profiles/adobe_rgb_1998.icc"));
|
||||
ICC_Profile profile2 = ColorProfiles.readProfile(getClass().getResourceAsStream("/profiles/adobe_rgb_1998.icc"));
|
||||
|
||||
assertEquals(profile1, profile2);
|
||||
assertSame(profile1, profile2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadProfileDifferent() throws IOException {
|
||||
// These profiles are extracted from various JPEGs, and have the exact same profile header (but are different profiles)...
|
||||
ICC_Profile profile1 = ColorProfiles.readProfile(getClass().getResourceAsStream("/profiles/adobe_rgb_1998.icc"));
|
||||
ICC_Profile profile2 = ColorProfiles.readProfile(getClass().getResourceAsStream("/profiles/color_match_rgb.icc"));
|
||||
|
||||
assertNotSame(profile1, profile2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateProfileBytesSameAsCached() throws IOException {
|
||||
ICC_Profile profile = ICC_Profile.getInstance(getClass().getResourceAsStream("/profiles/adobe_rgb_1998.icc"));
|
||||
ICC_ColorSpace cs1 = ColorSpaces.createColorSpace(profile);
|
||||
ICC_Profile profile2 = ColorProfiles.createProfile(profile.getData());
|
||||
|
||||
assertEquals(cs1.getProfile(), profile2);
|
||||
assertSame(cs1.getProfile(), profile2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadProfileInputStreamSameAsCached() throws IOException {
|
||||
ICC_ColorSpace cs1 = ColorSpaces.createColorSpace(ICC_Profile.getInstance(getClass().getResourceAsStream("/profiles/adobe_rgb_1998.icc")));
|
||||
ICC_Profile profile2 = ColorProfiles.readProfile(getClass().getResourceAsStream("/profiles/adobe_rgb_1998.icc"));
|
||||
|
||||
assertEquals(cs1.getProfile(), profile2);
|
||||
assertSame(cs1.getProfile(), profile2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateProfileBytesSameAsInternal() {
|
||||
ICC_Profile profile1 = ICC_Profile.getInstance(ColorSpace.CS_sRGB);
|
||||
ICC_Profile profile2 = ColorProfiles.createProfile(ICC_Profile.getInstance(ColorSpace.CS_sRGB).getData());
|
||||
|
||||
assertEquals(profile1, profile2);
|
||||
assertSame(profile1, profile2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadProfileInputStreamSameAsInternal() throws IOException {
|
||||
ICC_Profile profile1 = ICC_Profile.getInstance(ColorSpace.CS_sRGB);
|
||||
ICC_Profile profile2 = ColorProfiles.readProfile(new ByteArrayInputStream(ICC_Profile.getInstance(ColorSpace.CS_sRGB).getData()));
|
||||
|
||||
assertEquals(profile1, profile2);
|
||||
assertSame(profile1, profile2);
|
||||
}
|
||||
}
|
||||
+25
-29
@@ -90,34 +90,6 @@ public class ColorSpacesTest {
|
||||
assertTrue(created.isCS_sRGB());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateColorSpaceFromBrokenProfileIsFixedCS_sRGB() {
|
||||
ICC_Profile internal = ICC_Profile.getInstance(ColorSpace.CS_sRGB);
|
||||
ICC_Profile profile = createBrokenProfile(internal);
|
||||
assertNotSame(internal, profile); // Sanity check
|
||||
|
||||
assertTrue(ColorSpaces.isOffendingColorProfile(profile));
|
||||
|
||||
ICC_ColorSpace created = ColorSpaces.createColorSpace(profile);
|
||||
assertSame(ColorSpace.getInstance(ColorSpace.CS_sRGB), created);
|
||||
assertTrue(created.isCS_sRGB());
|
||||
}
|
||||
|
||||
private ICC_Profile createBrokenProfile(ICC_Profile internal) {
|
||||
byte[] data = internal.getData();
|
||||
data[ICC_Profile.icHdrRenderingIntent] = 1; // Intent: 1 == Relative Colormetric Little Endian
|
||||
data[ICC_Profile.icHdrRenderingIntent + 1] = 0;
|
||||
data[ICC_Profile.icHdrRenderingIntent + 2] = 0;
|
||||
data[ICC_Profile.icHdrRenderingIntent + 3] = 0;
|
||||
return ICC_Profile.getInstance(data);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsOffendingColorProfile() {
|
||||
ICC_Profile broken = createBrokenProfile(ICC_Profile.getInstance(ColorSpace.CS_GRAY));
|
||||
assertTrue(ColorSpaces.isOffendingColorProfile(broken));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateColorSpaceFromKnownProfileReturnsInternalCS_GRAY() {
|
||||
ICC_Profile profile = ICC_Profile.getInstance(ColorSpace.CS_GRAY);
|
||||
@@ -166,11 +138,13 @@ public class ColorSpacesTest {
|
||||
assertEquals(ColorSpace.TYPE_CMYK, ColorSpaces.getColorSpace(ColorSpaces.CS_GENERIC_CMYK).getType());
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Test
|
||||
public void testIsCS_sRGBTrue() {
|
||||
assertTrue(ColorSpaces.isCS_sRGB(ICC_Profile.getInstance(ColorSpace.CS_sRGB)));
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Test
|
||||
public void testIsCS_sRGBFalse() {
|
||||
assertFalse(ColorSpaces.isCS_sRGB(ICC_Profile.getInstance(ColorSpace.CS_LINEAR_RGB)));
|
||||
@@ -179,14 +153,36 @@ public class ColorSpacesTest {
|
||||
assertFalse(ColorSpaces.isCS_sRGB(ICC_Profile.getInstance(ColorSpace.CS_PYCC)));
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testIsCS_sRGBNull() {
|
||||
ColorSpaces.isCS_sRGB(null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Test
|
||||
public void testIsCS_GRAYTrue() {
|
||||
assertTrue(ColorSpaces.isCS_GRAY(ICC_Profile.getInstance(ColorSpace.CS_GRAY)));
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Test
|
||||
public void testIsCS_GRAYFalse() {
|
||||
assertFalse(ColorSpaces.isCS_GRAY(ICC_Profile.getInstance(ColorSpace.CS_sRGB)));
|
||||
assertFalse(ColorSpaces.isCS_GRAY(ICC_Profile.getInstance(ColorSpace.CS_LINEAR_RGB)));
|
||||
assertFalse(ColorSpaces.isCS_GRAY(ICC_Profile.getInstance(ColorSpace.CS_CIEXYZ)));
|
||||
assertFalse(ColorSpaces.isCS_GRAY(ICC_Profile.getInstance(ColorSpace.CS_PYCC)));
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testIsCS_GRAYNull() {
|
||||
ColorSpaces.isCS_GRAY(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEqualHeadersDifferentProfile() throws IOException {
|
||||
// These profiles are extracted from various JPEGs, and have the exact same profile header...
|
||||
// These profiles are extracted from various JPEGs, and have the exact same profile header (but are different)...
|
||||
ICC_Profile profile1 = ICC_Profile.getInstance(getClass().getResourceAsStream("/profiles/adobe_rgb_1998.icc"));
|
||||
ICC_Profile profile2 = ICC_Profile.getInstance(getClass().getResourceAsStream("/profiles/color_match_rgb.icc"));
|
||||
|
||||
|
||||
+22
-1
@@ -37,7 +37,9 @@ import java.awt.image.*;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class DiscreteAlphaIndexColorModelTest {
|
||||
|
||||
@@ -204,6 +206,25 @@ public class DiscreteAlphaIndexColorModelTest {
|
||||
assertThat(raster.getTransferType(), CoreMatchers.equalTo(DataBuffer.TYPE_BYTE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNumComponents() {
|
||||
int[] colors = createIntLut(1 << 8);
|
||||
IndexColorModel icm = new IndexColorModel(8, colors.length, colors, 0, false, -1, DataBuffer.TYPE_BYTE);
|
||||
|
||||
ColorModel colorModelDiscreteAlpha = new DiscreteAlphaIndexColorModel(icm, 1, true);
|
||||
ColorModel colorModelDiscreteAlphaExtra = new DiscreteAlphaIndexColorModel(icm, 2, true);
|
||||
ColorModel colorModelNoAlphaExtra = new DiscreteAlphaIndexColorModel(icm, 42, false);
|
||||
|
||||
assertEquals(3, colorModelDiscreteAlpha.getNumColorComponents());
|
||||
assertEquals(4, colorModelDiscreteAlpha.getNumComponents());
|
||||
|
||||
assertEquals(3, colorModelDiscreteAlphaExtra.getNumColorComponents());
|
||||
assertEquals(5, colorModelDiscreteAlphaExtra.getNumComponents()); // Questionable
|
||||
|
||||
assertEquals(3, colorModelNoAlphaExtra.getNumColorComponents());
|
||||
assertEquals(45, colorModelNoAlphaExtra.getNumComponents()); // Questionable
|
||||
}
|
||||
|
||||
private static int[] createIntLut(final int count) {
|
||||
int[] lut = new int[count];
|
||||
|
||||
|
||||
-2
@@ -39,8 +39,6 @@ import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
public class KCMSSanitizerStrategyTest {
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package com.twelvemonkeys.imageio.color;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.spi.ImageInputStreamSpi;
|
||||
import javax.imageio.spi.ServiceRegistry;
|
||||
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
public class ProfileDeferralActivatorTest {
|
||||
|
||||
@Test
|
||||
public void testActivateProfiles() {
|
||||
// Should just run with no exceptions...
|
||||
ProfileDeferralActivator.activateProfiles();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSpiRegistration() {
|
||||
ProfileDeferralActivator.Spi spi = new ProfileDeferralActivator.Spi();
|
||||
ServiceRegistry registry = mock(ServiceRegistry.class);
|
||||
Class<ImageInputStreamSpi> category = ImageInputStreamSpi.class;
|
||||
|
||||
spi.onRegistration(registry, category);
|
||||
|
||||
verify(registry, only()).deregisterServiceProvider(spi, category);
|
||||
}
|
||||
}
|
||||
+197
-154
@@ -67,8 +67,9 @@ public class BufferedFileImageInputStreamTest {
|
||||
|
||||
@Test
|
||||
public void testCreate() throws IOException {
|
||||
BufferedFileImageInputStream stream = new BufferedFileImageInputStream(File.createTempFile("empty", ".tmp"));
|
||||
assertEquals("Data length should be same as stream length", 0, stream.length());
|
||||
try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(File.createTempFile("empty", ".tmp"))) {
|
||||
assertEquals("Data length should be same as stream length", 0, stream.length());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -104,12 +105,12 @@ public class BufferedFileImageInputStreamTest {
|
||||
byte[] data = new byte[1024 * 1024];
|
||||
File file = randomDataToFile(data);
|
||||
|
||||
BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file);
|
||||
try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file)) {
|
||||
assertEquals("File length should be same as stream length", file.length(), stream.length());
|
||||
|
||||
assertEquals("File length should be same as stream length", file.length(), stream.length());
|
||||
|
||||
for (byte value : data) {
|
||||
assertEquals("Wrong data read", value & 0xff, stream.read());
|
||||
for (byte value : data) {
|
||||
assertEquals("Wrong data read", value & 0xff, stream.read());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,15 +119,15 @@ public class BufferedFileImageInputStreamTest {
|
||||
byte[] data = new byte[1024 * 1024];
|
||||
File file = randomDataToFile(data);
|
||||
|
||||
BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file);
|
||||
try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file)) {
|
||||
assertEquals("File length should be same as stream length", file.length(), stream.length());
|
||||
|
||||
assertEquals("File length should be same as stream length", file.length(), stream.length());
|
||||
byte[] result = new byte[1024];
|
||||
|
||||
byte[] result = new byte[1024];
|
||||
|
||||
for (int i = 0; i < data.length / result.length; i++) {
|
||||
stream.readFully(result);
|
||||
assertTrue("Wrong data read: " + i, rangeEquals(data, i * result.length, result, 0, result.length));
|
||||
for (int i = 0; i < data.length / result.length; i++) {
|
||||
stream.readFully(result);
|
||||
assertTrue("Wrong data read: " + i, rangeEquals(data, i * result.length, result, 0, result.length));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,16 +136,16 @@ public class BufferedFileImageInputStreamTest {
|
||||
byte[] data = new byte[1024 * 14];
|
||||
File file = randomDataToFile(data);
|
||||
|
||||
BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file);
|
||||
try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file)) {
|
||||
assertEquals("File length should be same as stream length", file.length(), stream.length());
|
||||
|
||||
assertEquals("File length should be same as stream length", file.length(), stream.length());
|
||||
byte[] result = new byte[7];
|
||||
|
||||
byte[] result = new byte[7];
|
||||
|
||||
for (int i = 0; i < data.length / result.length; i += 2) {
|
||||
stream.readFully(result);
|
||||
stream.skipBytes(result.length);
|
||||
assertTrue("Wrong data read: " + i, rangeEquals(data, i * result.length, result, 0, result.length));
|
||||
for (int i = 0; i < data.length / result.length; i += 2) {
|
||||
stream.readFully(result);
|
||||
stream.skipBytes(result.length);
|
||||
assertTrue("Wrong data read: " + i, rangeEquals(data, i * result.length, result, 0, result.length));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,19 +154,35 @@ public class BufferedFileImageInputStreamTest {
|
||||
byte[] data = new byte[1024 * 18];
|
||||
File file = randomDataToFile(data);
|
||||
|
||||
BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file);
|
||||
try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file)) {
|
||||
assertEquals("File length should be same as stream length", file.length(), stream.length());
|
||||
|
||||
assertEquals("File length should be same as stream length", file.length(), stream.length());
|
||||
byte[] result = new byte[9];
|
||||
|
||||
byte[] result = new byte[9];
|
||||
for (int i = 0; i < data.length / result.length; i++) {
|
||||
// Read backwards
|
||||
long newPos = stream.length() - result.length - i * result.length;
|
||||
stream.seek(newPos);
|
||||
assertEquals("Wrong stream position", newPos, stream.getStreamPosition());
|
||||
stream.readFully(result);
|
||||
assertTrue("Wrong data read: " + i, rangeEquals(data, (int) newPos, result, 0, result.length));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < data.length / result.length; i++) {
|
||||
// Read backwards
|
||||
long newPos = stream.length() - result.length - i * result.length;
|
||||
stream.seek(newPos);
|
||||
assertEquals("Wrong stream position", newPos, stream.getStreamPosition());
|
||||
stream.readFully(result);
|
||||
assertTrue("Wrong data read: " + i, rangeEquals(data, (int) newPos, result, 0, result.length));
|
||||
@Test
|
||||
public void testReadOutsideDataSeek0Read() throws IOException {
|
||||
byte[] data = new byte[256];
|
||||
File file = randomDataToFile(data);
|
||||
|
||||
try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file)) {
|
||||
assertEquals("File length should be same as stream length", file.length(), stream.length());
|
||||
|
||||
byte[] buffer = new byte[data.length * 2];
|
||||
stream.read(buffer);
|
||||
stream.seek(0);
|
||||
assertNotEquals(-1, stream.read());
|
||||
assertNotEquals(-1, stream.read(buffer));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,10 +193,10 @@ public class BufferedFileImageInputStreamTest {
|
||||
long value = ByteBuffer.wrap(bytes).getLong();
|
||||
|
||||
// Create stream
|
||||
ImageInputStream stream = new BufferedFileImageInputStream(file);
|
||||
|
||||
for (int i = 1; i <= 64; i++) {
|
||||
assertEquals(String.format("bit %d differ", i), (value << (i - 1L)) >>> 63L, stream.readBit());
|
||||
try (ImageInputStream stream = new BufferedFileImageInputStream(file)) {
|
||||
for (int i = 1; i <= 64; i++) {
|
||||
assertEquals(String.format("bit %d differ", i), (value << (i - 1L)) >>> 63L, stream.readBit());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,12 +207,12 @@ public class BufferedFileImageInputStreamTest {
|
||||
long value = ByteBuffer.wrap(bytes).getLong();
|
||||
|
||||
// Create stream
|
||||
ImageInputStream stream = new BufferedFileImageInputStream(file);
|
||||
|
||||
for (int i = 1; i <= 64; i++) {
|
||||
stream.seek(0);
|
||||
assertEquals(String.format("bit %d differ", i), value >>> (64L - i), stream.readBits(i));
|
||||
assertEquals(i % 8, stream.getBitOffset());
|
||||
try (ImageInputStream stream = new BufferedFileImageInputStream(file)) {
|
||||
for (int i = 1; i <= 64; i++) {
|
||||
stream.seek(0);
|
||||
assertEquals(String.format("bit %d differ", i), value >>> (64L - i), stream.readBits(i));
|
||||
assertEquals(i % 8, stream.getBitOffset());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,13 +223,13 @@ public class BufferedFileImageInputStreamTest {
|
||||
long value = ByteBuffer.wrap(bytes).getLong();
|
||||
|
||||
// Create stream
|
||||
ImageInputStream stream = new BufferedFileImageInputStream(file);
|
||||
|
||||
for (int i = 1; i <= 60; i++) {
|
||||
stream.seek(0);
|
||||
stream.setBitOffset(i % 8);
|
||||
assertEquals(String.format("bit %d differ", i), (value << (i % 8)) >>> (64L - i), stream.readBits(i));
|
||||
assertEquals(i * 2 % 8, stream.getBitOffset());
|
||||
try (ImageInputStream stream = new BufferedFileImageInputStream(file)) {
|
||||
for (int i = 1; i <= 60; i++) {
|
||||
stream.seek(0);
|
||||
stream.setBitOffset(i % 8);
|
||||
assertEquals(String.format("bit %d differ", i), (value << (i % 8)) >>> (64L - i), stream.readBits(i));
|
||||
assertEquals(i * 2 % 8, stream.getBitOffset());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,35 +238,37 @@ public class BufferedFileImageInputStreamTest {
|
||||
byte[] bytes = new byte[8743]; // Slightly more than one buffer size
|
||||
File file = randomDataToFile(bytes);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
|
||||
final ImageInputStream stream = new BufferedFileImageInputStream(file);
|
||||
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 2; i++) {
|
||||
assertEquals(buffer.getShort(), stream.readShort());
|
||||
}
|
||||
try (final ImageInputStream stream = new BufferedFileImageInputStream(file)) {
|
||||
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readShort();
|
||||
for (int i = 0; i < bytes.length / 2; i++) {
|
||||
assertEquals(buffer.getShort(), stream.readShort());
|
||||
}
|
||||
});
|
||||
|
||||
stream.seek(0);
|
||||
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
buffer.position(0);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readShort();
|
||||
}
|
||||
});
|
||||
|
||||
for (int i = 0; i < bytes.length / 2; i++) {
|
||||
assertEquals(buffer.getShort(), stream.readShort());
|
||||
}
|
||||
stream.seek(0);
|
||||
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
buffer.position(0);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readShort();
|
||||
for (int i = 0; i < bytes.length / 2; i++) {
|
||||
assertEquals(buffer.getShort(), stream.readShort());
|
||||
}
|
||||
});
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readShort();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -257,35 +276,37 @@ public class BufferedFileImageInputStreamTest {
|
||||
byte[] bytes = new byte[8743]; // Slightly more than one buffer size
|
||||
File file = randomDataToFile(bytes);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
|
||||
final ImageInputStream stream = new BufferedFileImageInputStream(file);
|
||||
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 4; i++) {
|
||||
assertEquals(buffer.getInt(), stream.readInt());
|
||||
}
|
||||
try (final ImageInputStream stream = new BufferedFileImageInputStream(file)) {
|
||||
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readInt();
|
||||
for (int i = 0; i < bytes.length / 4; i++) {
|
||||
assertEquals(buffer.getInt(), stream.readInt());
|
||||
}
|
||||
});
|
||||
|
||||
stream.seek(0);
|
||||
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
buffer.position(0);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readInt();
|
||||
}
|
||||
});
|
||||
|
||||
for (int i = 0; i < bytes.length / 4; i++) {
|
||||
assertEquals(buffer.getInt(), stream.readInt());
|
||||
}
|
||||
stream.seek(0);
|
||||
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
buffer.position(0);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readInt();
|
||||
for (int i = 0; i < bytes.length / 4; i++) {
|
||||
assertEquals(buffer.getInt(), stream.readInt());
|
||||
}
|
||||
});
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readInt();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -293,35 +314,37 @@ public class BufferedFileImageInputStreamTest {
|
||||
byte[] bytes = new byte[8743]; // Slightly more than one buffer size
|
||||
File file = randomDataToFile(bytes);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
|
||||
final ImageInputStream stream = new BufferedFileImageInputStream(file);
|
||||
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 8; i++) {
|
||||
assertEquals(buffer.getLong(), stream.readLong());
|
||||
}
|
||||
try (final ImageInputStream stream = new BufferedFileImageInputStream(file)) {
|
||||
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readLong();
|
||||
for (int i = 0; i < bytes.length / 8; i++) {
|
||||
assertEquals(buffer.getLong(), stream.readLong());
|
||||
}
|
||||
});
|
||||
|
||||
stream.seek(0);
|
||||
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
buffer.position(0);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readLong();
|
||||
}
|
||||
});
|
||||
|
||||
for (int i = 0; i < bytes.length / 8; i++) {
|
||||
assertEquals(buffer.getLong(), stream.readLong());
|
||||
}
|
||||
stream.seek(0);
|
||||
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
buffer.position(0);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readLong();
|
||||
for (int i = 0; i < bytes.length / 8; i++) {
|
||||
assertEquals(buffer.getLong(), stream.readLong());
|
||||
}
|
||||
});
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readLong();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -329,49 +352,50 @@ public class BufferedFileImageInputStreamTest {
|
||||
byte[] bytes = new byte[9];
|
||||
File file = randomDataToFile(bytes);
|
||||
|
||||
final ImageInputStream stream = new BufferedFileImageInputStream(file);
|
||||
stream.seek(1000);
|
||||
try (final ImageInputStream stream = new BufferedFileImageInputStream(file)) {
|
||||
stream.seek(1000);
|
||||
|
||||
assertEquals(-1, stream.read());
|
||||
assertEquals(-1, stream.read(new byte[1], 0, 1));
|
||||
assertEquals(-1, stream.read());
|
||||
assertEquals(-1, stream.read(new byte[1], 0, 1));
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readFully(new byte[1]);
|
||||
}
|
||||
});
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readByte();
|
||||
}
|
||||
});
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readShort();
|
||||
}
|
||||
});
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readInt();
|
||||
}
|
||||
});
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readLong();
|
||||
}
|
||||
});
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readFully(new byte[1]);
|
||||
}
|
||||
});
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readByte();
|
||||
}
|
||||
});
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readShort();
|
||||
}
|
||||
});
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readInt();
|
||||
}
|
||||
});
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readLong();
|
||||
}
|
||||
});
|
||||
|
||||
stream.seek(0);
|
||||
for (byte value : bytes) {
|
||||
assertEquals(value, stream.readByte());
|
||||
stream.seek(0);
|
||||
for (byte value : bytes) {
|
||||
assertEquals(value, stream.readByte());
|
||||
}
|
||||
|
||||
assertEquals(-1, stream.read());
|
||||
}
|
||||
|
||||
assertEquals(-1, stream.read());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -383,4 +407,23 @@ public class BufferedFileImageInputStreamTest {
|
||||
stream.close();
|
||||
verify(mock, only()).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWorkaroundForWBMPImageReaderExpectsReadToBehaveAsReadFully() throws IOException {
|
||||
// See #606 for details.
|
||||
// Bug in JDK WBMPImageReader, uses read(byte[], int, int) instead of readFully(byte[], int, int).
|
||||
// Ie: Relies on read to return all bytes at once, without blocking
|
||||
int size = BufferedFileImageInputStream.DEFAULT_BUFFER_SIZE * 7;
|
||||
byte[] bytes = new byte[size];
|
||||
File file = randomDataToFile(bytes);
|
||||
|
||||
try (BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file)) {
|
||||
byte[] result = new byte[size];
|
||||
int head = stream.read(result, 0, 12); // Provoke a buffered read
|
||||
int len = stream.read(result, 12, size - 12); // Rest of buffer + direct read
|
||||
|
||||
assertEquals(size, len + head);
|
||||
assertArrayEquals(bytes, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -53,6 +53,7 @@ import static org.mockito.Mockito.*;
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: BufferedImageInputStreamTest.java,v 1.0 Jun 30, 2008 3:07:42 PM haraldk Exp$
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class BufferedImageInputStreamTest {
|
||||
private final Random random = new Random(3450972865211L);
|
||||
|
||||
@@ -433,7 +434,7 @@ public class BufferedImageInputStreamTest {
|
||||
* and {@code pFirstOffset == pSecondOffset}.
|
||||
* Otherwise {@code false}.
|
||||
*/
|
||||
static boolean rangeEquals(byte[] pFirst, int pFirstOffset, byte[] pSecond, int pSecondOffset, int pLength) {
|
||||
public static boolean rangeEquals(byte[] pFirst, int pFirstOffset, byte[] pSecond, int pSecondOffset, int pLength) {
|
||||
if (pFirst == pSecond && pFirstOffset == pSecondOffset) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Executable
+380
@@ -0,0 +1,380 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.stream;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.function.ThrowingRunnable;
|
||||
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Random;
|
||||
|
||||
import static com.twelvemonkeys.imageio.stream.BufferedImageInputStreamTest.rangeEquals;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* NonSeekableImageInputStreamTest
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: NonSeekableImageInputStreamTest.java,v 1.0 Apr 21, 2009 10:58:48 AM haraldk Exp$
|
||||
*/
|
||||
public class DirectImageInputStreamTest {
|
||||
private final Random random = new Random(170984354357234566L);
|
||||
|
||||
private InputStream randomData(byte[] data) {
|
||||
random.nextBytes(data);
|
||||
|
||||
return new ByteArrayInputStream(data);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate() throws IOException {
|
||||
try (DirectImageInputStream stream = new DirectImageInputStream(new ByteArrayInputStream(new byte[0]), 0)) {
|
||||
assertEquals("Data length should be same as stream length", 0, stream.length());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateNullFile() throws IOException {
|
||||
try (@SuppressWarnings("unused") DirectImageInputStream stream = new DirectImageInputStream(null)) {
|
||||
fail("Expected IllegalArgumentException");
|
||||
}
|
||||
catch (IllegalArgumentException expected) {
|
||||
assertNotNull("Null exception message", expected.getMessage());
|
||||
String message = expected.getMessage().toLowerCase();
|
||||
assertTrue("Exception message does not contain parameter name", message.contains("stream"));
|
||||
assertTrue("Exception message does not contain null", message.contains("null"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRead() throws IOException {
|
||||
byte[] data = new byte[1024 * 1024];
|
||||
InputStream input = randomData(data);
|
||||
|
||||
try (DirectImageInputStream stream = new DirectImageInputStream(input)) {
|
||||
for (byte value : data) {
|
||||
assertEquals("Wrong data read", value & 0xff, stream.read());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadArray() throws IOException {
|
||||
byte[] data = new byte[1024 * 10];
|
||||
InputStream input = randomData(data);
|
||||
|
||||
try (DirectImageInputStream stream = new DirectImageInputStream(input)) {
|
||||
byte[] result = new byte[1024];
|
||||
|
||||
for (int i = 0; i < data.length / result.length; i++) {
|
||||
stream.readFully(result);
|
||||
assertTrue("Wrong data read: " + i, rangeEquals(data, i * result.length, result, 0, result.length));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadSkip() throws IOException {
|
||||
byte[] data = new byte[1024 * 14];
|
||||
InputStream input = randomData(data);
|
||||
|
||||
try (DirectImageInputStream stream = new DirectImageInputStream(input)) {
|
||||
byte[] result = new byte[7];
|
||||
|
||||
for (int i = 0; i < data.length / result.length; i += 2) {
|
||||
stream.readFully(result);
|
||||
stream.skipBytes(result.length);
|
||||
assertTrue("Wrong data read: " + i, rangeEquals(data, i * result.length, result, 0, result.length));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadSeek() throws IOException {
|
||||
byte[] data = new byte[24 * 18];
|
||||
InputStream input = randomData(data);
|
||||
|
||||
try (DirectImageInputStream stream = new DirectImageInputStream(input)) {
|
||||
byte[] result = new byte[9];
|
||||
|
||||
for (int i = 0; i < data.length / (2 * result.length); i++) {
|
||||
long newPos = i * 2 * result.length;
|
||||
stream.seek(newPos);
|
||||
assertEquals("Wrong stream position", newPos, stream.getStreamPosition());
|
||||
stream.readFully(result);
|
||||
assertTrue("Wrong data read: " + i, rangeEquals(data, (int) newPos, result, 0, result.length));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Ignore("Bit reading requires backwards seek or buffer...")
|
||||
@Test
|
||||
public void testReadBitRandom() throws IOException {
|
||||
byte[] bytes = new byte[8];
|
||||
InputStream input = randomData(bytes);
|
||||
long value = ByteBuffer.wrap(bytes).getLong();
|
||||
|
||||
// Create stream
|
||||
try (DirectImageInputStream stream = new DirectImageInputStream(input)) {
|
||||
for (int i = 1; i <= 64; i++) {
|
||||
assertEquals(String.format("bit %d differ", i), (value << (i - 1L)) >>> 63L, stream.readBit());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Ignore("Bit reading requires backwards seek or buffer...")
|
||||
@Test
|
||||
public void testReadBitsRandom() throws IOException {
|
||||
byte[] bytes = new byte[8];
|
||||
InputStream input = randomData(bytes);
|
||||
long value = ByteBuffer.wrap(bytes).getLong();
|
||||
|
||||
// Create stream
|
||||
try (DirectImageInputStream stream = new DirectImageInputStream(input)) {
|
||||
for (int i = 1; i <= 64; i++) {
|
||||
stream.seek(0);
|
||||
assertEquals(String.format("bit %d differ", i), value >>> (64L - i), stream.readBits(i));
|
||||
assertEquals(i % 8, stream.getBitOffset());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Ignore("Bit reading requires backwards seek or buffer...")
|
||||
@Test
|
||||
public void testReadBitsRandomOffset() throws IOException {
|
||||
byte[] bytes = new byte[8];
|
||||
InputStream input = randomData(bytes);
|
||||
long value = ByteBuffer.wrap(bytes).getLong();
|
||||
|
||||
// Create stream
|
||||
try (DirectImageInputStream stream = new DirectImageInputStream(input)) {
|
||||
for (int i = 1; i <= 60; i++) {
|
||||
stream.seek(0);
|
||||
stream.setBitOffset(i % 8);
|
||||
assertEquals(String.format("bit %d differ", i), (value << (i % 8)) >>> (64L - i), stream.readBits(i));
|
||||
assertEquals(i * 2L % 8, stream.getBitOffset());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadShort() throws IOException {
|
||||
byte[] bytes = new byte[31];
|
||||
InputStream input = randomData(bytes);
|
||||
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
try (DirectImageInputStream stream = new DirectImageInputStream(input)) {
|
||||
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 2; i++) {
|
||||
assertEquals(buffer.getShort(), stream.readShort());
|
||||
}
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readShort();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
try (DirectImageInputStream stream = new DirectImageInputStream(new ByteArrayInputStream(bytes))) {
|
||||
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
buffer.position(0);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 2; i++) {
|
||||
assertEquals(buffer.getShort(), stream.readShort());
|
||||
}
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readShort();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadInt() throws IOException {
|
||||
byte[] bytes = new byte[31];
|
||||
InputStream input = randomData(bytes);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
// Create stream
|
||||
try (DirectImageInputStream stream = new DirectImageInputStream(input)) {
|
||||
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 4; i++) {
|
||||
assertEquals(buffer.getInt(), stream.readInt());
|
||||
}
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readInt();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
try (DirectImageInputStream stream = new DirectImageInputStream(new ByteArrayInputStream(bytes))) {
|
||||
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
buffer.position(0);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 4; i++) {
|
||||
assertEquals(buffer.getInt(), stream.readInt());
|
||||
}
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readInt();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadLong() throws IOException {
|
||||
byte[] bytes = new byte[8743]; // Slightly more than one buffer size
|
||||
InputStream input = randomData(bytes);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
try (DirectImageInputStream stream = new DirectImageInputStream(input)) {
|
||||
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 8; i++) {
|
||||
assertEquals(buffer.getLong(), stream.readLong());
|
||||
}
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readLong();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
try (DirectImageInputStream stream = new DirectImageInputStream(new ByteArrayInputStream(bytes))) {
|
||||
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
buffer.position(0);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 8; i++) {
|
||||
assertEquals(buffer.getLong(), stream.readLong());
|
||||
}
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readLong();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSeekPastEOF() throws IOException {
|
||||
byte[] bytes = new byte[9];
|
||||
InputStream input = randomData(bytes);
|
||||
|
||||
try (DirectImageInputStream stream = new DirectImageInputStream(input)) {
|
||||
stream.seek(1000);
|
||||
|
||||
assertEquals(-1, stream.read());
|
||||
assertEquals(-1, stream.read(new byte[1], 0, 1));
|
||||
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readFully(new byte[1]);
|
||||
}
|
||||
});
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readByte();
|
||||
}
|
||||
});
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readShort();
|
||||
}
|
||||
});
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readInt();
|
||||
}
|
||||
});
|
||||
assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
stream.readLong();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
try (DirectImageInputStream stream = new DirectImageInputStream(new ByteArrayInputStream(bytes))) {
|
||||
for (byte value : bytes) {
|
||||
assertEquals(value, stream.readByte());
|
||||
}
|
||||
|
||||
assertEquals(-1, stream.read());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClose() throws IOException {
|
||||
// Create wrapper stream
|
||||
InputStream input = mock(InputStream.class);
|
||||
ImageInputStream stream = new DirectImageInputStream(input);
|
||||
|
||||
stream.close();
|
||||
verify(input, only()).close();
|
||||
}
|
||||
}
|
||||
+51
-14
@@ -45,9 +45,8 @@ import javax.imageio.spi.IIORegistry;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.RenderedImage;
|
||||
import java.awt.image.SampleModel;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.image.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
@@ -57,6 +56,7 @@ import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import static java.lang.Math.min;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@@ -1151,7 +1151,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
// At least imageStarted and imageComplete, plus any number of imageProgress
|
||||
InOrder ordered = inOrder(listener);
|
||||
ordered.verify(listener).imageStarted(reader, 0);
|
||||
ordered.verify(listener, atLeastOnce()).imageProgress(eq(reader), anyInt());
|
||||
ordered.verify(listener, atLeastOnce()).imageProgress(eq(reader), anyFloat());
|
||||
ordered.verify(listener).imageComplete(reader);
|
||||
reader.dispose();
|
||||
}
|
||||
@@ -1184,9 +1184,9 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
ordered.verify(listenerToo).imageStarted(reader, 0);
|
||||
ordered.verify(listenerThree).imageStarted(reader, 0);
|
||||
|
||||
ordered.verify(listener, atLeastOnce()).imageProgress(eq(reader), anyInt());
|
||||
ordered.verify(listenerToo, atLeastOnce()).imageProgress(eq(reader), anyInt());
|
||||
ordered.verify(listenerThree, atLeastOnce()).imageProgress(eq(reader), anyInt());
|
||||
ordered.verify(listener, atLeastOnce()).imageProgress(eq(reader), anyFloat());
|
||||
ordered.verify(listenerToo, atLeastOnce()).imageProgress(eq(reader), anyFloat());
|
||||
ordered.verify(listenerThree, atLeastOnce()).imageProgress(eq(reader), anyFloat());
|
||||
|
||||
ordered.verify(listener).imageComplete(reader);
|
||||
ordered.verify(listenerToo).imageComplete(reader);
|
||||
@@ -1226,7 +1226,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
// Should not have called any methods...
|
||||
verifyZeroInteractions(listener);
|
||||
verifyNoInteractions(listener);
|
||||
reader.dispose();
|
||||
}
|
||||
|
||||
@@ -1253,11 +1253,11 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
// Should not have called any methods on listener1...
|
||||
verifyZeroInteractions(listener);
|
||||
verifyNoInteractions(listener);
|
||||
|
||||
InOrder ordered = inOrder(listenerToo);
|
||||
ordered.verify(listenerToo).imageStarted(reader, 0);
|
||||
ordered.verify(listenerToo, atLeastOnce()).imageProgress(eq(reader), anyInt());
|
||||
ordered.verify(listenerToo, atLeastOnce()).imageProgress(eq(reader), anyFloat());
|
||||
ordered.verify(listenerToo).imageComplete(reader);
|
||||
reader.dispose();
|
||||
}
|
||||
@@ -1281,7 +1281,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
// Should not have called any methods...
|
||||
verifyZeroInteractions(listener);
|
||||
verifyNoInteractions(listener);
|
||||
reader.dispose();
|
||||
}
|
||||
|
||||
@@ -1307,8 +1307,8 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
// Should not have called any methods...
|
||||
verifyZeroInteractions(listener);
|
||||
verifyZeroInteractions(listenerToo);
|
||||
verifyNoInteractions(listener);
|
||||
verifyNoInteractions(listenerToo);
|
||||
reader.dispose();
|
||||
}
|
||||
|
||||
@@ -1333,7 +1333,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
};
|
||||
doAnswer(abort).when(abortingListener).imageStarted(any(ImageReader.class), anyInt());
|
||||
doAnswer(abort).when(abortingListener).imageProgress(any(ImageReader.class), anyInt());
|
||||
doAnswer(abort).when(abortingListener).imageProgress(any(ImageReader.class), anyFloat());
|
||||
|
||||
reader.addIIOReadProgressListener(abortingListener);
|
||||
|
||||
@@ -1738,6 +1738,43 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
reader.dispose();
|
||||
}
|
||||
|
||||
protected List<TestData> getTestDataForAffineTransformOpCompatibility() {
|
||||
// Allow subclasses to filter out test data that can't be converted to a compatible image without data loss
|
||||
return getTestData();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAffineTransformOpCompatibility() throws IOException {
|
||||
// Test that the output of normal images are compatible with AffineTransformOp. Is unlikely to work on all test data
|
||||
ImageReader reader = createReader();
|
||||
|
||||
for (TestData testData : getTestDataForAffineTransformOpCompatibility()) {
|
||||
try (ImageInputStream input = testData.getInputStream()) {
|
||||
reader.setInput(input);
|
||||
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
param.setSourceRegion(new Rectangle(min(reader.getWidth(0), 64), min(reader.getHeight(0), 64)));
|
||||
|
||||
BufferedImage originalImage = reader.read(0, param);
|
||||
|
||||
AffineTransform transform = AffineTransform.getTranslateInstance(10, 10);
|
||||
AffineTransformOp op = new AffineTransformOp(transform, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
|
||||
|
||||
try {
|
||||
BufferedImage resultImage = op.filter(originalImage, null); // The exception happens here
|
||||
assertNotNull(resultImage);
|
||||
}
|
||||
catch (ImagingOpException e) {
|
||||
fail(e.getMessage() + ".\n\t"
|
||||
+ originalImage + "\n\t"
|
||||
+ testData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reader.dispose();
|
||||
}
|
||||
|
||||
@Ignore("TODO: Implement")
|
||||
@Test
|
||||
public void testSetDestinationBands() {
|
||||
|
||||
+12
-13
@@ -52,7 +52,6 @@ import java.net.URL;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
@@ -79,7 +78,7 @@ public abstract class ImageWriterAbstractTest<T extends ImageWriter> {
|
||||
protected abstract ImageWriterSpi createProvider();
|
||||
|
||||
protected final T createWriter() throws IOException {
|
||||
return writerClass.cast(provider.createWriterInstance(null));
|
||||
return writerClass.cast(provider.createWriterInstance());
|
||||
}
|
||||
|
||||
protected abstract List<? extends RenderedImage> getTestData();
|
||||
@@ -104,7 +103,7 @@ public abstract class ImageWriterAbstractTest<T extends ImageWriter> {
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
|
||||
protected final RenderedImage getTestData(final int index) {
|
||||
return getTestData().get(index);
|
||||
}
|
||||
@@ -219,7 +218,7 @@ public abstract class ImageWriterAbstractTest<T extends ImageWriter> {
|
||||
// At least imageStarted and imageComplete, plus any number of imageProgress
|
||||
InOrder ordered = inOrder(listener);
|
||||
ordered.verify(listener).imageStarted(writer, 0);
|
||||
ordered.verify(listener, atLeastOnce()).imageProgress(eq(writer), anyInt());
|
||||
ordered.verify(listener, atLeastOnce()).imageProgress(eq(writer), anyFloat());
|
||||
ordered.verify(listener).imageComplete(writer);
|
||||
}
|
||||
|
||||
@@ -251,9 +250,9 @@ public abstract class ImageWriterAbstractTest<T extends ImageWriter> {
|
||||
ordered.verify(listenerToo).imageStarted(writer, 0);
|
||||
ordered.verify(listenerThree).imageStarted(writer, 0);
|
||||
|
||||
ordered.verify(listener, atLeastOnce()).imageProgress(eq(writer), anyInt());
|
||||
ordered.verify(listenerToo, atLeastOnce()).imageProgress(eq(writer), anyInt());
|
||||
ordered.verify(listenerThree, atLeastOnce()).imageProgress(eq(writer), anyInt());
|
||||
ordered.verify(listener, atLeastOnce()).imageProgress(eq(writer), anyFloat());
|
||||
ordered.verify(listenerToo, atLeastOnce()).imageProgress(eq(writer), anyFloat());
|
||||
ordered.verify(listenerThree, atLeastOnce()).imageProgress(eq(writer), anyFloat());
|
||||
|
||||
ordered.verify(listener).imageComplete(writer);
|
||||
ordered.verify(listenerToo).imageComplete(writer);
|
||||
@@ -290,7 +289,7 @@ public abstract class ImageWriterAbstractTest<T extends ImageWriter> {
|
||||
}
|
||||
|
||||
// Should not have called any methods...
|
||||
verifyZeroInteractions(listener);
|
||||
verifyNoInteractions(listener);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -315,12 +314,12 @@ public abstract class ImageWriterAbstractTest<T extends ImageWriter> {
|
||||
}
|
||||
|
||||
// Should not have called any methods...
|
||||
verifyZeroInteractions(listener);
|
||||
verifyNoInteractions(listener);
|
||||
|
||||
// At least imageStarted and imageComplete, plus any number of imageProgress
|
||||
InOrder ordered = inOrder(listenerToo);
|
||||
ordered.verify(listenerToo).imageStarted(writer, 0);
|
||||
ordered.verify(listenerToo, atLeastOnce()).imageProgress(eq(writer), anyInt());
|
||||
ordered.verify(listenerToo, atLeastOnce()).imageProgress(eq(writer), anyFloat());
|
||||
ordered.verify(listenerToo).imageComplete(writer);
|
||||
|
||||
}
|
||||
@@ -345,7 +344,7 @@ public abstract class ImageWriterAbstractTest<T extends ImageWriter> {
|
||||
}
|
||||
|
||||
// Should not have called any methods...
|
||||
verifyZeroInteractions(listener);
|
||||
verifyNoInteractions(listener);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -371,7 +370,7 @@ public abstract class ImageWriterAbstractTest<T extends ImageWriter> {
|
||||
}
|
||||
|
||||
// Should not have called any methods...
|
||||
verifyZeroInteractions(listener);
|
||||
verifyZeroInteractions(listenerToo);
|
||||
verifyNoInteractions(listener);
|
||||
verifyNoInteractions(listenerToo);
|
||||
}
|
||||
}
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
package com.twelvemonkeys.imageio.util;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import java.awt.*;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.*;
|
||||
import java.util.Random;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertSame;
|
||||
|
||||
/**
|
||||
* RasterUtilsTest.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: RasterUtilsTest.java,v 1.0 05/05/2021 haraldk Exp$
|
||||
*/
|
||||
public class RasterUtilsTest {
|
||||
@Test(expected = NullPointerException.class)
|
||||
public void testAsByteRasterFromNull() {
|
||||
RasterUtils.asByteRaster((Raster) null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("RedundantCast")
|
||||
@Test(expected = NullPointerException.class)
|
||||
public void testAsByteRasterWritableFromNull() {
|
||||
RasterUtils.asByteRaster((WritableRaster) null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsByteRasterPassThrough() {
|
||||
WritableRaster[] rasters = new WritableRaster[] {
|
||||
new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR).getRaster(),
|
||||
new BufferedImage(1, 1, BufferedImage.TYPE_4BYTE_ABGR).getRaster(),
|
||||
new BufferedImage(1, 1, BufferedImage.TYPE_4BYTE_ABGR_PRE).getRaster(),
|
||||
new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY).getRaster(),
|
||||
Raster.createBandedRaster(DataBuffer.TYPE_BYTE, 1, 1, 7, null),
|
||||
Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, 1, 1, 2, null),
|
||||
new WritableRaster(new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE, 1, 1, 1, 1, new int[1]), new Point(0, 0)) {}
|
||||
};
|
||||
|
||||
for (Raster raster : rasters) {
|
||||
assertSame(raster, RasterUtils.asByteRaster(raster));
|
||||
}
|
||||
|
||||
for (WritableRaster raster : rasters) {
|
||||
assertSame(raster, RasterUtils.asByteRaster(raster));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsByteRasterWritableFromTYPE_INT_RGB() {
|
||||
BufferedImage image = new BufferedImage(9, 11, BufferedImage.TYPE_INT_RGB);
|
||||
|
||||
WritableRaster raster = RasterUtils.asByteRaster(image.getRaster());
|
||||
|
||||
assertEquals(DataBuffer.TYPE_BYTE, raster.getTransferType());
|
||||
assertEquals(PixelInterleavedSampleModel.class, raster.getSampleModel().getClass());
|
||||
assertEquals(image.getWidth(), raster.getWidth());
|
||||
assertEquals(image.getHeight(), raster.getHeight());
|
||||
|
||||
assertEquals(3, raster.getNumBands());
|
||||
assertEquals(3, raster.getNumDataElements());
|
||||
|
||||
assertImageRasterEquals(image, raster);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsByteRasterWritableFromTYPE_INT_ARGB() {
|
||||
BufferedImage image = new BufferedImage(9, 11, BufferedImage.TYPE_INT_ARGB);
|
||||
|
||||
WritableRaster raster = RasterUtils.asByteRaster(image.getRaster());
|
||||
|
||||
assertEquals(DataBuffer.TYPE_BYTE, raster.getTransferType());
|
||||
assertEquals(PixelInterleavedSampleModel.class, raster.getSampleModel().getClass());
|
||||
assertEquals(image.getWidth(), raster.getWidth());
|
||||
assertEquals(image.getHeight(), raster.getHeight());
|
||||
|
||||
assertEquals(4, raster.getNumBands());
|
||||
assertEquals(4, raster.getNumDataElements());
|
||||
|
||||
assertImageRasterEquals(image, raster);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsByteRasterWritableFromTYPE_INT_ARGB_PRE() {
|
||||
BufferedImage image = new BufferedImage(9, 11, BufferedImage.TYPE_INT_ARGB_PRE);
|
||||
|
||||
WritableRaster raster = RasterUtils.asByteRaster(image.getRaster());
|
||||
|
||||
assertEquals(DataBuffer.TYPE_BYTE, raster.getTransferType());
|
||||
assertEquals(PixelInterleavedSampleModel.class, raster.getSampleModel().getClass());
|
||||
assertEquals(image.getWidth(), raster.getWidth());
|
||||
assertEquals(image.getHeight(), raster.getHeight());
|
||||
|
||||
assertEquals(4, raster.getNumBands());
|
||||
assertEquals(4, raster.getNumDataElements());
|
||||
|
||||
// We don't assert on values here, as the premultiplied values makes it hard...
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsByteRasterWritableFromTYPE_INT_BGR() {
|
||||
BufferedImage image = new BufferedImage(9, 11, BufferedImage.TYPE_INT_BGR);
|
||||
|
||||
WritableRaster raster = RasterUtils.asByteRaster(image.getRaster());
|
||||
|
||||
assertEquals(DataBuffer.TYPE_BYTE, raster.getTransferType());
|
||||
assertEquals(PixelInterleavedSampleModel.class, raster.getSampleModel().getClass());
|
||||
assertEquals(image.getWidth(), raster.getWidth());
|
||||
assertEquals(image.getHeight(), raster.getHeight());
|
||||
|
||||
assertEquals(3, raster.getNumBands());
|
||||
assertEquals(3, raster.getNumDataElements());
|
||||
|
||||
assertImageRasterEquals(image, raster);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsByteRasterWritableFromTYPE_CUSTOM_GRAB() {
|
||||
BufferedImage image = ImageTypeSpecifier.createPacked(ColorSpace.getInstance(ColorSpace.CS_sRGB),
|
||||
0x00FF0000,
|
||||
0xFF000000,
|
||||
0x000000FF,
|
||||
0x0000FF00,
|
||||
DataBuffer.TYPE_INT, false).createBufferedImage(7, 13);
|
||||
|
||||
WritableRaster raster = RasterUtils.asByteRaster(image.getRaster());
|
||||
|
||||
assertEquals(DataBuffer.TYPE_BYTE, raster.getTransferType());
|
||||
assertEquals(PixelInterleavedSampleModel.class, raster.getSampleModel().getClass());
|
||||
assertEquals(image.getWidth(), raster.getWidth());
|
||||
assertEquals(image.getHeight(), raster.getHeight());
|
||||
|
||||
assertEquals(4, raster.getNumBands());
|
||||
assertEquals(4, raster.getNumDataElements());
|
||||
|
||||
assertImageRasterEquals(image, raster);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsByteRasterWritableFromTYPE_CUSTOM_BxRG() {
|
||||
BufferedImage image = ImageTypeSpecifier.createPacked(ColorSpace.getInstance(ColorSpace.CS_sRGB),
|
||||
0x0000FF00,
|
||||
0x000000FF,
|
||||
0xFF000000,
|
||||
0,
|
||||
DataBuffer.TYPE_INT, false).createBufferedImage(7, 13);
|
||||
|
||||
WritableRaster raster = RasterUtils.asByteRaster(image.getRaster());
|
||||
|
||||
assertEquals(DataBuffer.TYPE_BYTE, raster.getTransferType());
|
||||
assertEquals(PixelInterleavedSampleModel.class, raster.getSampleModel().getClass());
|
||||
assertEquals(image.getWidth(), raster.getWidth());
|
||||
assertEquals(image.getHeight(), raster.getHeight());
|
||||
|
||||
assertEquals(3, raster.getNumBands());
|
||||
assertEquals(3, raster.getNumDataElements());
|
||||
|
||||
assertImageRasterEquals(image, raster);
|
||||
}
|
||||
|
||||
private static void assertImageRasterEquals(BufferedImage image, WritableRaster raster) {
|
||||
// NOTE: This is NOT necessarily how the values are stored in the data buffer
|
||||
int[] argbOffs = new int[] {16, 8, 0, 24};
|
||||
|
||||
Raster imageRaster = image.getRaster();
|
||||
|
||||
Random rng = new Random(27365481723L);
|
||||
|
||||
for (int y = 0; y < raster.getHeight(); y++) {
|
||||
for (int x = 0; x < raster.getWidth(); x++) {
|
||||
int argb = 0;
|
||||
|
||||
for (int b = 0; b < raster.getNumBands(); b++) {
|
||||
int s = rng.nextInt(0xFF);
|
||||
raster.setSample(x, y, b, s);
|
||||
|
||||
assertEquals(s, raster.getSample(x, y, b));
|
||||
assertEquals(s, imageRaster.getSample(x, y, b));
|
||||
|
||||
argb |= (s << argbOffs[b]);
|
||||
}
|
||||
|
||||
if (raster.getNumBands() < 4) {
|
||||
argb |= 0xFF000000;
|
||||
}
|
||||
|
||||
int expectedArgb = image.getRGB(x, y);
|
||||
if (argb != expectedArgb) {
|
||||
assertEquals(x + ", " + y + ": ", String.format("#%08x", expectedArgb), String.format("#%08x", argb));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.7.0</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-hdr</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: HDR plugin</name>
|
||||
|
||||
+10
-2
@@ -38,12 +38,14 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
/**
|
||||
* TGAImageReaderTest
|
||||
* HDRImageReaderTest
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: TGAImageReaderTest.java,v 1.0 03.07.14 22:28 haraldk Exp$
|
||||
* @version $Id: HDRImageReaderTest.java,v 1.0 03.07.14 22:28 haraldk Exp$
|
||||
*/
|
||||
public class HDRImageReaderTest extends ImageReaderAbstractTest<HDRImageReader> {
|
||||
@Override
|
||||
@@ -58,6 +60,12 @@ public class HDRImageReaderTest extends ImageReaderAbstractTest<HDRImageReader>
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<TestData> getTestDataForAffineTransformOpCompatibility() {
|
||||
// HDR images uses floating point buffers...
|
||||
return emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getFormatNames() {
|
||||
return Arrays.asList("HDR", "hdr", "RGBE", "rgbe");
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.7.0</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-icns</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: ICNS plugin</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.7.0</version>
|
||||
<version>3.8.4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-iff</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: IFF plugin</name>
|
||||
|
||||
+6
-6
@@ -69,14 +69,14 @@ abstract class AbstractMultiPaletteChunk extends IFFChunk implements MultiPalett
|
||||
protected WeakReference<IndexColorModel> originalPalette;
|
||||
protected MutableIndexColorModel mutablePalette;
|
||||
|
||||
public AbstractMultiPaletteChunk(int pChunkId, int pChunkLength) {
|
||||
super(pChunkId, pChunkLength);
|
||||
public AbstractMultiPaletteChunk(int chunkId, int chunkLength) {
|
||||
super(chunkId, chunkLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
void readChunk(final DataInput pInput) throws IOException {
|
||||
void readChunk(final DataInput input) throws IOException {
|
||||
if (chunkId == IFF.CHUNK_SHAM) {
|
||||
pInput.readUnsignedShort(); // Version, typically 0, skipped
|
||||
input.readUnsignedShort(); // Version, typically 0, skipped
|
||||
}
|
||||
|
||||
int rows = chunkLength / 32; /* sizeof(word) * 16 */
|
||||
@@ -91,7 +91,7 @@ abstract class AbstractMultiPaletteChunk extends IFFChunk implements MultiPalett
|
||||
}
|
||||
|
||||
for (int i = 0; i < 16; i++ ) {
|
||||
int data = pInput.readUnsignedShort();
|
||||
int data = input.readUnsignedShort();
|
||||
|
||||
changes[row][i].index = i;
|
||||
changes[row][i].r = (byte) (((data & 0x0f00) >> 8) * FACTOR_4BIT);
|
||||
@@ -102,7 +102,7 @@ abstract class AbstractMultiPaletteChunk extends IFFChunk implements MultiPalett
|
||||
}
|
||||
|
||||
@Override
|
||||
void writeChunk(DataOutput pOutput) {
|
||||
void writeChunk(DataOutput output) {
|
||||
throw new UnsupportedOperationException("Method writeChunk not implemented");
|
||||
}
|
||||
|
||||
|
||||
+43
-43
@@ -30,12 +30,11 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.iff;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
|
||||
/**
|
||||
* BMHDChunk
|
||||
*
|
||||
@@ -110,64 +109,65 @@ final class BMHDChunk extends IFFChunk {
|
||||
int pageWidth;
|
||||
int pageHeight;
|
||||
|
||||
protected BMHDChunk(int pChunkLength) {
|
||||
super(IFF.CHUNK_BMHD, pChunkLength);
|
||||
BMHDChunk(int chunkLength) {
|
||||
super(IFF.CHUNK_BMHD, chunkLength);
|
||||
}
|
||||
|
||||
protected BMHDChunk(int pWidth, int pHeight, int pBitplanes, int pMaskType, int pCompressionType, int pTransparentIndex) {
|
||||
BMHDChunk(int width, int height, int bitplanes, int maskType, int compressionType, int transparentIndex) {
|
||||
super(IFF.CHUNK_BMHD, 20);
|
||||
width = pWidth;
|
||||
height = pHeight;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
xPos = 0;
|
||||
yPos = 0;
|
||||
bitplanes = pBitplanes;
|
||||
maskType = pMaskType;
|
||||
compressionType = pCompressionType;
|
||||
transparentIndex = pTransparentIndex;
|
||||
this.bitplanes = bitplanes;
|
||||
this.maskType = maskType;
|
||||
this.compressionType = compressionType;
|
||||
this.transparentIndex = transparentIndex;
|
||||
xAspect = 1;
|
||||
yAspect = 1;
|
||||
pageWidth = Math.min(pWidth, Short.MAX_VALUE); // For some reason, these are signed?
|
||||
pageHeight = Math.min(pHeight, Short.MAX_VALUE);
|
||||
pageWidth = Math.min(width, Short.MAX_VALUE); // For some reason, these are signed?
|
||||
pageHeight = Math.min(height, Short.MAX_VALUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
void readChunk(final DataInput pInput) throws IOException {
|
||||
void readChunk(final DataInput input) throws IOException {
|
||||
if (chunkLength != 20) {
|
||||
throw new IIOException("Unknown BMHD chunk length: " + chunkLength);
|
||||
}
|
||||
width = pInput.readUnsignedShort();
|
||||
height = pInput.readUnsignedShort();
|
||||
xPos = pInput.readShort();
|
||||
yPos = pInput.readShort();
|
||||
bitplanes = pInput.readUnsignedByte();
|
||||
maskType = pInput.readUnsignedByte();
|
||||
compressionType = pInput.readUnsignedByte();
|
||||
pInput.readByte(); // PAD
|
||||
transparentIndex = pInput.readUnsignedShort();
|
||||
xAspect = pInput.readUnsignedByte();
|
||||
yAspect = pInput.readUnsignedByte();
|
||||
pageWidth = pInput.readShort();
|
||||
pageHeight = pInput.readShort();
|
||||
|
||||
width = input.readUnsignedShort();
|
||||
height = input.readUnsignedShort();
|
||||
xPos = input.readShort();
|
||||
yPos = input.readShort();
|
||||
bitplanes = input.readUnsignedByte();
|
||||
maskType = input.readUnsignedByte();
|
||||
compressionType = input.readUnsignedByte();
|
||||
input.readByte(); // PAD
|
||||
transparentIndex = input.readUnsignedShort();
|
||||
xAspect = input.readUnsignedByte();
|
||||
yAspect = input.readUnsignedByte();
|
||||
pageWidth = input.readShort();
|
||||
pageHeight = input.readShort();
|
||||
}
|
||||
|
||||
@Override
|
||||
void writeChunk(final DataOutput pOutput) throws IOException {
|
||||
pOutput.writeInt(chunkId);
|
||||
pOutput.writeInt(chunkLength);
|
||||
void writeChunk(final DataOutput output) throws IOException {
|
||||
output.writeInt(chunkId);
|
||||
output.writeInt(chunkLength);
|
||||
|
||||
pOutput.writeShort(width);
|
||||
pOutput.writeShort(height);
|
||||
pOutput.writeShort(xPos);
|
||||
pOutput.writeShort(yPos);
|
||||
pOutput.writeByte(bitplanes);
|
||||
pOutput.writeByte(maskType);
|
||||
pOutput.writeByte(compressionType);
|
||||
pOutput.writeByte(0); // PAD
|
||||
pOutput.writeShort(transparentIndex);
|
||||
pOutput.writeByte(xAspect);
|
||||
pOutput.writeByte(yAspect);
|
||||
pOutput.writeShort(pageWidth);
|
||||
pOutput.writeShort(pageHeight);
|
||||
output.writeShort(width);
|
||||
output.writeShort(height);
|
||||
output.writeShort(xPos);
|
||||
output.writeShort(yPos);
|
||||
output.writeByte(bitplanes);
|
||||
output.writeByte(maskType);
|
||||
output.writeByte(compressionType);
|
||||
output.writeByte(0); // PAD
|
||||
output.writeShort(transparentIndex);
|
||||
output.writeByte(xAspect);
|
||||
output.writeByte(yAspect);
|
||||
output.writeShort(pageWidth);
|
||||
output.writeShort(pageHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+9
-4
@@ -33,6 +33,8 @@ package com.twelvemonkeys.imageio.plugins.iff;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
|
||||
import static com.twelvemonkeys.lang.Validate.isTrue;
|
||||
|
||||
/**
|
||||
* BODYChunk
|
||||
*
|
||||
@@ -40,17 +42,20 @@ import java.io.DataOutput;
|
||||
* @version $Id: BODYChunk.java,v 1.0 28.feb.2006 01:25:49 haku Exp$
|
||||
*/
|
||||
final class BODYChunk extends IFFChunk {
|
||||
protected BODYChunk(int pChunkLength) {
|
||||
super(IFF.CHUNK_BODY, pChunkLength);
|
||||
final long chunkOffset;
|
||||
|
||||
BODYChunk(int chunkId, int chunkLength, long chunkOffset) {
|
||||
super(isTrue(chunkId == IFF.CHUNK_BODY || chunkId == IFF.CHUNK_DBOD, chunkId, "Illegal body chunk: '%s'"), chunkLength);
|
||||
this.chunkOffset = chunkOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
void readChunk(final DataInput pInput) {
|
||||
void readChunk(final DataInput input) {
|
||||
throw new InternalError("BODY chunk should only be read from IFFImageReader");
|
||||
}
|
||||
|
||||
@Override
|
||||
void writeChunk(final DataOutput pOutput) {
|
||||
void writeChunk(final DataOutput output) {
|
||||
throw new InternalError("BODY chunk should only be written from IFFImageWriter");
|
||||
}
|
||||
}
|
||||
|
||||
+6
-7
@@ -30,12 +30,11 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.iff;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
|
||||
/**
|
||||
* CAMGChunk
|
||||
*
|
||||
@@ -49,21 +48,21 @@ final class CAMGChunk extends IFFChunk {
|
||||
|
||||
int camg;
|
||||
|
||||
public CAMGChunk(int pLength) {
|
||||
super(IFF.CHUNK_CAMG, pLength);
|
||||
CAMGChunk(int chunkLength) {
|
||||
super(IFF.CHUNK_CAMG, chunkLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
void readChunk(final DataInput pInput) throws IOException {
|
||||
void readChunk(final DataInput input) throws IOException {
|
||||
if (chunkLength != 4) {
|
||||
throw new IIOException("Unknown CAMG chunk length: " + chunkLength);
|
||||
}
|
||||
|
||||
camg = pInput.readInt();
|
||||
camg = input.readInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
void writeChunk(final DataOutput pOutput) {
|
||||
void writeChunk(final DataOutput output) {
|
||||
throw new InternalError("Not implemented: writeChunk()");
|
||||
}
|
||||
|
||||
|
||||
+20
-35
@@ -30,16 +30,13 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.iff;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import javax.imageio.IIOException;
|
||||
import java.awt.image.IndexColorModel;
|
||||
import java.awt.image.WritableRaster;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
|
||||
/**
|
||||
* CMAPChunk
|
||||
*
|
||||
@@ -60,7 +57,7 @@ final class CMAPChunk extends IFFChunk {
|
||||
|
||||
private IndexColorModel model;
|
||||
|
||||
protected CMAPChunk(final int pChunkLength) {
|
||||
CMAPChunk(final int pChunkLength) {
|
||||
super(IFF.CHUNK_CMAP, pChunkLength);
|
||||
}
|
||||
|
||||
@@ -70,7 +67,7 @@ final class CMAPChunk extends IFFChunk {
|
||||
}
|
||||
|
||||
@Override
|
||||
void readChunk(final DataInput pInput) throws IOException {
|
||||
void readChunk(final DataInput input) throws IOException {
|
||||
int numColors = chunkLength / 3;
|
||||
|
||||
reds = new byte[numColors];
|
||||
@@ -78,9 +75,9 @@ final class CMAPChunk extends IFFChunk {
|
||||
blues = reds.clone();
|
||||
|
||||
for (int i = 0; i < numColors; i++) {
|
||||
reds[i] = pInput.readByte();
|
||||
greens[i] = pInput.readByte();
|
||||
blues[i] = pInput.readByte();
|
||||
reds[i] = input.readByte();
|
||||
greens[i] = input.readByte();
|
||||
blues[i] = input.readByte();
|
||||
}
|
||||
|
||||
// TODO: When reading in a CMAP for 8-bit-per-gun display or
|
||||
@@ -93,25 +90,25 @@ final class CMAPChunk extends IFFChunk {
|
||||
|
||||
// All chunks are WORD aligned (even sized), may need to read pad...
|
||||
if (chunkLength % 2 != 0) {
|
||||
pInput.readByte();
|
||||
input.readByte();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void writeChunk(final DataOutput pOutput) throws IOException {
|
||||
pOutput.writeInt(chunkId);
|
||||
pOutput.writeInt(chunkLength);
|
||||
void writeChunk(final DataOutput output) throws IOException {
|
||||
output.writeInt(chunkId);
|
||||
output.writeInt(chunkLength);
|
||||
|
||||
final int length = model.getMapSize();
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
pOutput.writeByte(model.getRed(i));
|
||||
pOutput.writeByte(model.getGreen(i));
|
||||
pOutput.writeByte(model.getBlue(i));
|
||||
output.writeByte(model.getRed(i));
|
||||
output.writeByte(model.getGreen(i));
|
||||
output.writeByte(model.getBlue(i));
|
||||
}
|
||||
|
||||
if (chunkLength % 2 != 0) {
|
||||
pOutput.writeByte(0); // PAD
|
||||
output.writeByte(0); // PAD
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,25 +117,11 @@ final class CMAPChunk extends IFFChunk {
|
||||
return super.toString() + " {colorMap=" + model + "}";
|
||||
}
|
||||
|
||||
BufferedImage createPaletteImage(final BMHDChunk header, boolean isEHB) throws IIOException {
|
||||
// Create a 1 x colors.length image
|
||||
IndexColorModel cm = getIndexColorModel(header, isEHB);
|
||||
WritableRaster raster = cm.createCompatibleWritableRaster(cm.getMapSize(), 1);
|
||||
byte[] pixel = null;
|
||||
|
||||
for (int x = 0; x < cm.getMapSize(); x++) {
|
||||
pixel = (byte[]) cm.getDataElements(cm.getRGB(x), pixel);
|
||||
raster.setDataElements(x, 0, pixel);
|
||||
}
|
||||
|
||||
return new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
|
||||
}
|
||||
|
||||
public IndexColorModel getIndexColorModel(final BMHDChunk header, boolean isEHB) throws IIOException {
|
||||
public IndexColorModel getIndexColorModel(final Form.ILBMForm header) throws IIOException {
|
||||
if (model == null) {
|
||||
int numColors = reds.length; // All arrays are same size
|
||||
|
||||
if (isEHB) {
|
||||
if (header.isEHB()) {
|
||||
if (numColors == 32) {
|
||||
reds = Arrays.copyOf(reds, numColors * 2);
|
||||
blues = Arrays.copyOf(blues, numColors * 2);
|
||||
@@ -161,8 +144,10 @@ final class CMAPChunk extends IFFChunk {
|
||||
// Would it work to double to numbers of colors, and create an indexcolormodel,
|
||||
// with alpha, where all colors above the original color is all transparent?
|
||||
// This is a waste of time and space, of course...
|
||||
int transparent = header.maskType == BMHDChunk.MASK_TRANSPARENT_COLOR ? header.transparentIndex : -1;
|
||||
model = new IndexColorModel(header.bitplanes, reds.length, reds, greens, blues, transparent); // https://github.com/haraldk/TwelveMonkeys/issues/15
|
||||
int transparent = header.transparentIndex();
|
||||
int bitplanes = header.bitplanes() == 25 ? 8 : header.bitplanes();
|
||||
|
||||
model = new IndexColorModel(bitplanes, reds.length, reds, greens, blues, transparent); // https://github.com/haraldk/TwelveMonkeys/issues/15
|
||||
}
|
||||
|
||||
return model;
|
||||
|
||||
+2
-2
@@ -38,7 +38,7 @@ package com.twelvemonkeys.imageio.plugins.iff;
|
||||
* @version $Id: CTBLChunk.java,v 1.0 30.03.12 14:53 haraldk Exp$
|
||||
*/
|
||||
final class CTBLChunk extends AbstractMultiPaletteChunk {
|
||||
protected CTBLChunk(int pChunkLength) {
|
||||
super(IFF.CHUNK_CTBL, pChunkLength);
|
||||
CTBLChunk(int chunkLength) {
|
||||
super(IFF.CHUNK_CTBL, chunkLength);
|
||||
}
|
||||
}
|
||||
|
||||
+56
-25
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* Copyright (c) 2022, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -28,44 +28,75 @@
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.servlet.cache;
|
||||
package com.twelvemonkeys.imageio.plugins.iff;
|
||||
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
|
||||
import java.net.URI;
|
||||
import javax.imageio.IIOException;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* AbstractCacheRequest
|
||||
* DGBLChunk
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haku $
|
||||
* @version $Id: AbstractCacheRequest.java#1 $
|
||||
* @version $Id: DGBLChunk.java,v 1.0 28.feb.2006 02:10:07 haku Exp$
|
||||
*/
|
||||
public abstract class AbstractCacheRequest implements CacheRequest {
|
||||
private final URI requestURI;
|
||||
private final String method;
|
||||
final class DGBLChunk extends IFFChunk {
|
||||
|
||||
protected AbstractCacheRequest(final URI pRequestURI, final String pMethod) {
|
||||
requestURI = Validate.notNull(pRequestURI, "requestURI");
|
||||
method = Validate.notNull(pMethod, "method");
|
||||
/*
|
||||
//
|
||||
struct DGBL = {
|
||||
//
|
||||
// Size of source display
|
||||
//
|
||||
UWORD DisplayWidth,DisplayHeight;
|
||||
//
|
||||
// Type of compression
|
||||
//
|
||||
UWORD Compression;
|
||||
//
|
||||
// Pixel aspect, a ration w:h
|
||||
//
|
||||
UBYTE xAspect,yAspect;
|
||||
};
|
||||
|
||||
*/
|
||||
int displayWidth;
|
||||
int displayHeight;
|
||||
int compressionType;
|
||||
int xAspect;
|
||||
int yAspect;
|
||||
|
||||
DGBLChunk(int chunkLength) {
|
||||
super(IFF.CHUNK_DGBL, chunkLength);
|
||||
}
|
||||
|
||||
public URI getRequestURI() {
|
||||
return requestURI;
|
||||
@Override
|
||||
void readChunk(final DataInput input) throws IOException {
|
||||
if (chunkLength != 8) {
|
||||
throw new IIOException("Unknown DBGL chunk length: " + chunkLength);
|
||||
}
|
||||
|
||||
displayWidth = input.readUnsignedShort();
|
||||
displayHeight = input.readUnsignedShort();
|
||||
compressionType = input.readUnsignedShort();
|
||||
xAspect = input.readUnsignedByte();
|
||||
yAspect = input.readUnsignedByte();
|
||||
}
|
||||
|
||||
public String getMethod() {
|
||||
return method;
|
||||
@Override
|
||||
void writeChunk(final DataOutput output) {
|
||||
throw new InternalError("Not implemented: writeChunk()");
|
||||
}
|
||||
|
||||
// TODO: Consider overriding equals/hashcode
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new StringBuilder(getClass().getSimpleName())
|
||||
.append("[URI=").append(requestURI)
|
||||
.append(", parameters=").append(getParameters())
|
||||
.append(", headers=").append(getHeaders())
|
||||
.append("]").toString();
|
||||
return super.toString() +
|
||||
"{displayWidth=" + displayWidth +
|
||||
", displayHeight=" + displayHeight +
|
||||
", compression=" + compressionType +
|
||||
", xAspect=" + xAspect +
|
||||
", yAspect=" + yAspect +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
Executable → Regular
+32
-28
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2008, Harald Kuhr
|
||||
* Copyright (c) 2022, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -28,49 +28,53 @@
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.servlet.cache;
|
||||
package com.twelvemonkeys.imageio.plugins.iff;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.imageio.IIOException;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* ServletCacheResponse
|
||||
* DLOCChunk.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haku $
|
||||
* @version $Id: ServletCacheResponse.java#2 $
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: DLOCChunk.java,v 1.0 31/01/2022 haraldk Exp$
|
||||
*/
|
||||
public final class ServletCacheResponse extends AbstractCacheResponse {
|
||||
private HttpServletResponse response;
|
||||
final class DLOCChunk extends IFFChunk {
|
||||
int width;
|
||||
int height;
|
||||
int x;
|
||||
int y;
|
||||
|
||||
public ServletCacheResponse(HttpServletResponse pResponse) {
|
||||
response = pResponse;
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream() throws IOException {
|
||||
return response.getOutputStream();
|
||||
DLOCChunk(final int chunkLength) {
|
||||
super(IFF.CHUNK_DLOC, chunkLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStatus(int pStatusCode) {
|
||||
response.setStatus(pStatusCode);
|
||||
super.setStatus(pStatusCode);
|
||||
void readChunk(final DataInput input) throws IOException {
|
||||
if (chunkLength != 8) {
|
||||
throw new IIOException("Unknown DLOC chunk length: " + chunkLength);
|
||||
}
|
||||
|
||||
width = input.readUnsignedShort();
|
||||
height = input.readUnsignedShort();
|
||||
x = input.readShort();
|
||||
y = input.readShort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addHeader(String pHeaderName, String pHeaderValue) {
|
||||
response.addHeader(pHeaderName, pHeaderValue);
|
||||
super.addHeader(pHeaderName, pHeaderValue);
|
||||
void writeChunk(final DataOutput output) {
|
||||
throw new InternalError("Not implemented: writeChunk()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeader(String pHeaderName, String pHeaderValue) {
|
||||
response.setHeader(pHeaderName, pHeaderValue);
|
||||
super.setHeader(pHeaderName, pHeaderValue);
|
||||
}
|
||||
|
||||
HttpServletResponse getResponse() {
|
||||
return response;
|
||||
public String toString() {
|
||||
return super.toString() +
|
||||
"{width=" + width +
|
||||
", height=" + height +
|
||||
", x=" + x +
|
||||
", y=" + y + '}';
|
||||
}
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
package com.twelvemonkeys.imageio.plugins.iff;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* DPELChunk.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: DPELChunk.java,v 1.0 01/02/2022 haraldk Exp$
|
||||
*/
|
||||
final class DPELChunk extends IFFChunk {
|
||||
/*
|
||||
//
|
||||
// Chunk DPEL
|
||||
// ----------
|
||||
struct DPEL = {
|
||||
//
|
||||
// Number of pixel components
|
||||
//
|
||||
ULONG nElements;
|
||||
//
|
||||
// The TypeDepth structure is repeated nElement times to identify
|
||||
// the content of every pixel. Pixels will always be padded to
|
||||
// byte boundaries. The DBOD chunk will be padded to an even
|
||||
// longword boundary.
|
||||
//
|
||||
struct TypeDepth = {
|
||||
//
|
||||
// Type of data
|
||||
//
|
||||
UWORD cType;
|
||||
//
|
||||
// Bit depth of this type
|
||||
//
|
||||
UWORD cBitDepth;
|
||||
} typedepth[Nelements];
|
||||
};
|
||||
*/
|
||||
TypeDepth[] typeDepths;
|
||||
|
||||
DPELChunk(final int chunkLength) {
|
||||
super(IFF.CHUNK_DPEL, chunkLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
void readChunk(final DataInput input) throws IOException {
|
||||
int components = input.readInt(); // Strictly, it's unsigned, but that many components is unlikely...
|
||||
|
||||
if (chunkLength != 4 + components * 4) {
|
||||
throw new IIOException("Unsupported DPEL chunk length: " + chunkLength);
|
||||
}
|
||||
|
||||
typeDepths = new TypeDepth[components];
|
||||
|
||||
for (int i = 0; i < components; i++) {
|
||||
typeDepths[i] = new TypeDepth(input.readUnsignedShort(), input.readUnsignedShort());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void writeChunk(final DataOutput output) {
|
||||
throw new InternalError("Not implemented: writeChunk()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString()
|
||||
+ "{typeDepths=" + Arrays.toString(typeDepths) + '}';
|
||||
}
|
||||
|
||||
public int bitsPerPixel() {
|
||||
int bitCount = 0;
|
||||
|
||||
for (TypeDepth typeDepth : typeDepths) {
|
||||
bitCount += typeDepth.bitDepth;
|
||||
}
|
||||
|
||||
return bitCount;
|
||||
}
|
||||
|
||||
static class TypeDepth {
|
||||
final int type;
|
||||
final int bitDepth;
|
||||
|
||||
TypeDepth(final int type, final int bitDepth) {
|
||||
this.type = type;
|
||||
this.bitDepth = bitDepth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TypeDepth{" +
|
||||
"type=" + type +
|
||||
", bits=" + bitDepth +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,421 @@
|
||||
package com.twelvemonkeys.imageio.plugins.iff;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ColorModel;
|
||||
import java.awt.image.IndexColorModel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.twelvemonkeys.imageio.plugins.iff.IFFUtil.toChunkStr;
|
||||
|
||||
/**
|
||||
* Form.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: Form.java,v 1.0 31/01/2022 haraldk Exp$
|
||||
*/
|
||||
abstract class Form {
|
||||
|
||||
final int formType;
|
||||
final List<GenericChunk> meta = new ArrayList<>();
|
||||
|
||||
Form(int formType) {
|
||||
this.formType = formType;
|
||||
}
|
||||
|
||||
abstract int width();
|
||||
abstract int height();
|
||||
abstract float aspect();
|
||||
abstract int bitplanes();
|
||||
abstract int compressionType();
|
||||
|
||||
boolean isMultiPalette() {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isHAM() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean premultiplied() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public int sampleSize() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
public int transparentIndex() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public IndexColorModel colorMap() throws IIOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public ColorModel colorMapForRow(IndexColorModel colorModel, int row) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public abstract boolean hasThumbnail();
|
||||
|
||||
public abstract int thumbnailWidth();
|
||||
|
||||
public abstract int thumbnailHeight();
|
||||
|
||||
public abstract BufferedImage thumbnail();
|
||||
|
||||
abstract long bodyOffset();
|
||||
abstract long bodyLength();
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toChunkStr(formType);
|
||||
}
|
||||
|
||||
Form with(final IFFChunk chunk) throws IIOException {
|
||||
if (chunk instanceof GenericChunk) {
|
||||
// TODO: This feels kind of hackish, as it breaks the immutable design, perhaps we should just reconsider...
|
||||
meta.add((GenericChunk) chunk);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(chunk + " not supported in FORM type " + toChunkStr(formType));
|
||||
}
|
||||
|
||||
static Form ofType(int formType) {
|
||||
switch (formType) {
|
||||
case IFF.TYPE_ACBM:
|
||||
case IFF.TYPE_ILBM:
|
||||
case IFF.TYPE_PBM:
|
||||
case IFF.TYPE_RGB8:
|
||||
return new ILBMForm(formType);
|
||||
case IFF.TYPE_DEEP:
|
||||
case IFF.TYPE_TVPP:
|
||||
return new DEEPForm(formType);
|
||||
default:
|
||||
throw new IllegalArgumentException("FORM type " + toChunkStr(formType) + " not supported");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The set of chunks used in the "original" ILBM,
|
||||
* and also ACBM, PBM and RGB8 FORMs.
|
||||
*/
|
||||
static final class ILBMForm extends Form {
|
||||
private final BMHDChunk bitmapHeader;
|
||||
private final CAMGChunk viewMode;
|
||||
private final CMAPChunk colorMap;
|
||||
private final AbstractMultiPaletteChunk multiPalette;
|
||||
private final XS24Chunk thumbnail; // TVPaint puts these into normal IFF ILBM 24 bit files as well as DEEP/TVPP
|
||||
private final BODYChunk body;
|
||||
|
||||
ILBMForm(int formType) {
|
||||
this(formType, null, null, null, null, null, null);
|
||||
}
|
||||
|
||||
private ILBMForm(final int formType, final BMHDChunk bitmapHeader, final CAMGChunk viewMode, final CMAPChunk colorMap, final AbstractMultiPaletteChunk multiPalette, final XS24Chunk thumbnail, final BODYChunk body) {
|
||||
super(formType);
|
||||
this.bitmapHeader = bitmapHeader;
|
||||
this.viewMode = viewMode;
|
||||
this.colorMap = colorMap;
|
||||
this.multiPalette = multiPalette;
|
||||
this.thumbnail = thumbnail;
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
@Override
|
||||
int width() {
|
||||
return bitmapHeader.width;
|
||||
}
|
||||
|
||||
@Override
|
||||
int height() {
|
||||
return bitmapHeader.height;
|
||||
}
|
||||
|
||||
@Override
|
||||
int bitplanes() {
|
||||
return bitmapHeader.bitplanes;
|
||||
}
|
||||
|
||||
@Override
|
||||
int compressionType() {
|
||||
return bitmapHeader.compressionType;
|
||||
}
|
||||
|
||||
@Override
|
||||
float aspect() {
|
||||
return bitmapHeader.yAspect == 0 ? 0 : (bitmapHeader.xAspect / (float) bitmapHeader.yAspect);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isMultiPalette() {
|
||||
return multiPalette != null;
|
||||
}
|
||||
|
||||
boolean isEHB() {
|
||||
return viewMode != null && viewMode.isEHB();
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isHAM() {
|
||||
return viewMode != null && viewMode.isHAM();
|
||||
}
|
||||
|
||||
boolean isLaced() {
|
||||
return viewMode != null && viewMode.isLaced();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int transparentIndex() {
|
||||
return bitmapHeader.maskType == BMHDChunk.MASK_TRANSPARENT_COLOR ? bitmapHeader.transparentIndex : -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IndexColorModel colorMap() throws IIOException {
|
||||
return colorMap != null ? colorMap.getIndexColorModel(this) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColorModel colorMapForRow(final IndexColorModel colorModel, final int row) {
|
||||
return multiPalette != null ? multiPalette.getColorModel(colorModel, row, isLaced()) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasThumbnail() {
|
||||
return thumbnail != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int thumbnailWidth() {
|
||||
return thumbnail != null ? thumbnail.width : -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int thumbnailHeight() {
|
||||
return thumbnail != null ? thumbnail.height : -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedImage thumbnail() {
|
||||
return thumbnail != null ? thumbnail.thumbnail() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
long bodyOffset() {
|
||||
return body.chunkOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
long bodyLength() {
|
||||
return body.chunkLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
ILBMForm with(final IFFChunk chunk) throws IIOException {
|
||||
if (chunk instanceof BMHDChunk) {
|
||||
if (bitmapHeader != null) {
|
||||
throw new IIOException("Multiple BMHD chunks not allowed");
|
||||
}
|
||||
|
||||
return new ILBMForm(formType, (BMHDChunk) chunk, null, colorMap, multiPalette, thumbnail, body);
|
||||
}
|
||||
else if (chunk instanceof CAMGChunk) {
|
||||
if (viewMode != null) {
|
||||
throw new IIOException("Multiple CAMG chunks not allowed");
|
||||
}
|
||||
|
||||
return new ILBMForm(formType, bitmapHeader, (CAMGChunk) chunk, colorMap, multiPalette, thumbnail, body);
|
||||
}
|
||||
else if (chunk instanceof CMAPChunk) {
|
||||
if (colorMap != null) {
|
||||
throw new IIOException("Multiple CMAP chunks not allowed");
|
||||
}
|
||||
|
||||
return new ILBMForm(formType, bitmapHeader, viewMode, (CMAPChunk) chunk, multiPalette, thumbnail, body);
|
||||
}
|
||||
else if (chunk instanceof AbstractMultiPaletteChunk) {
|
||||
// NOTE: We prefer PHCG over SHAM/CTBL style palette changes, if both are present
|
||||
if (multiPalette instanceof PCHGChunk) {
|
||||
if (chunk instanceof PCHGChunk) {
|
||||
throw new IIOException("Multiple PCHG/SHAM/CTBL chunks not allowed");
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
return new ILBMForm(formType, bitmapHeader, viewMode, colorMap, (AbstractMultiPaletteChunk) chunk, thumbnail, body);
|
||||
}
|
||||
else if (chunk instanceof XS24Chunk) {
|
||||
if (thumbnail != null) {
|
||||
throw new IIOException("Multiple XS24 chunks not allowed");
|
||||
}
|
||||
|
||||
return new ILBMForm(formType, bitmapHeader, viewMode, colorMap, multiPalette, (XS24Chunk) chunk, body);
|
||||
}
|
||||
else if (chunk instanceof BODYChunk) {
|
||||
if (body != null) {
|
||||
throw new IIOException("Multiple " + toChunkStr(chunk.chunkId) + " chunks not allowed");
|
||||
}
|
||||
|
||||
return new ILBMForm(formType, bitmapHeader, viewMode, colorMap, multiPalette, thumbnail, (BODYChunk) chunk);
|
||||
}
|
||||
else if (chunk instanceof GRABChunk) {
|
||||
// Ignored for now
|
||||
return this;
|
||||
}
|
||||
|
||||
return (ILBMForm) super.with(chunk);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + '{' + bitmapHeader +
|
||||
(viewMode != null ? ", " + viewMode : "" ) +
|
||||
(colorMap != null ? ", " + colorMap : "" ) +
|
||||
(multiPalette != null ? ", " + multiPalette : "" ) +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The set of chunks used in DEEP and TVPP FORMs.
|
||||
*/
|
||||
private static final class DEEPForm extends Form {
|
||||
private final DGBLChunk deepGlobal;
|
||||
private final DLOCChunk deepLocation;
|
||||
private final DPELChunk deepPixel;
|
||||
private final XS24Chunk thumbnail;
|
||||
private final BODYChunk body;
|
||||
|
||||
DEEPForm(int formType) {
|
||||
this(formType, null, null, null, null, null);
|
||||
}
|
||||
|
||||
private DEEPForm(final int formType, final DGBLChunk deepGlobal, final DLOCChunk deepLocation, final DPELChunk deepPixel, final XS24Chunk thumbnail, final BODYChunk body) {
|
||||
super(formType);
|
||||
this.deepGlobal = deepGlobal;
|
||||
this.deepLocation = deepLocation;
|
||||
this.deepPixel = deepPixel;
|
||||
this.thumbnail = thumbnail;
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
int width() {
|
||||
return deepLocation.width;
|
||||
}
|
||||
|
||||
@Override
|
||||
int height() {
|
||||
return deepLocation.height;
|
||||
}
|
||||
|
||||
@Override
|
||||
int bitplanes() {
|
||||
return deepPixel.bitsPerPixel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int sampleSize() {
|
||||
return bitplanes() / 8;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean premultiplied() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
int compressionType() {
|
||||
return deepGlobal.compressionType;
|
||||
}
|
||||
|
||||
@Override
|
||||
float aspect() {
|
||||
return deepGlobal.yAspect == 0 ? 0 : deepGlobal.xAspect / (float) deepGlobal.yAspect;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasThumbnail() {
|
||||
return thumbnail != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int thumbnailWidth() {
|
||||
return thumbnail != null ? thumbnail.width : -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int thumbnailHeight() {
|
||||
return thumbnail != null ? thumbnail.height : -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedImage thumbnail() {
|
||||
return thumbnail != null ? thumbnail.thumbnail() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
long bodyOffset() {
|
||||
return body.chunkOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
long bodyLength() {
|
||||
return body.chunkLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
DEEPForm with(final IFFChunk chunk) throws IIOException {
|
||||
if (chunk instanceof DGBLChunk) {
|
||||
if (deepGlobal != null) {
|
||||
throw new IIOException("Multiple DGBL chunks not allowed");
|
||||
}
|
||||
|
||||
return new DEEPForm(formType, (DGBLChunk) chunk, null, null, thumbnail, body);
|
||||
}
|
||||
else if (chunk instanceof DLOCChunk) {
|
||||
if (deepLocation != null) {
|
||||
throw new IIOException("Multiple DLOC chunks not allowed");
|
||||
}
|
||||
|
||||
return new DEEPForm(formType, deepGlobal, (DLOCChunk) chunk, deepPixel, thumbnail, body);
|
||||
}
|
||||
else if (chunk instanceof DPELChunk) {
|
||||
if (deepPixel != null) {
|
||||
throw new IIOException("Multiple DPEL chunks not allowed");
|
||||
}
|
||||
|
||||
return new DEEPForm(formType, deepGlobal, deepLocation, (DPELChunk) chunk, thumbnail, body);
|
||||
}
|
||||
else if (chunk instanceof XS24Chunk) {
|
||||
if (thumbnail != null) {
|
||||
throw new IIOException("Multiple XS24 chunks not allowed");
|
||||
}
|
||||
|
||||
return new DEEPForm(formType, deepGlobal, deepLocation, deepPixel, (XS24Chunk) chunk, body);
|
||||
}
|
||||
else if (chunk instanceof BODYChunk) {
|
||||
// TODO: Make a better approach!
|
||||
// if (body != null) {
|
||||
// throw new IIOException("Multiple " + toChunkStr(chunk.chunkId) + " chunks not allowed");
|
||||
// }
|
||||
|
||||
return new DEEPForm(formType, deepGlobal, deepLocation, deepPixel, thumbnail, (BODYChunk) chunk);
|
||||
}
|
||||
|
||||
return (DEEPForm) super.with(chunk);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + '{' + deepGlobal + ", " + deepLocation + ", " + deepPixel + '}';
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
-9
@@ -50,25 +50,25 @@ final class GRABChunk extends IFFChunk {
|
||||
|
||||
Point2D point;
|
||||
|
||||
protected GRABChunk(int pChunkLength) {
|
||||
super(IFF.CHUNK_GRAB, pChunkLength);
|
||||
GRABChunk(int chunkLength) {
|
||||
super(IFF.CHUNK_GRAB, chunkLength);
|
||||
}
|
||||
|
||||
protected GRABChunk(Point2D pPoint) {
|
||||
GRABChunk(Point2D point) {
|
||||
super(IFF.CHUNK_GRAB, 4);
|
||||
point = pPoint;
|
||||
this.point = point;
|
||||
}
|
||||
|
||||
void readChunk(DataInput pInput) throws IOException {
|
||||
void readChunk(DataInput input) throws IOException {
|
||||
if (chunkLength != 4) {
|
||||
throw new IIOException("Unknown GRAB chunk size: " + chunkLength);
|
||||
}
|
||||
point = new Point(pInput.readShort(), pInput.readShort());
|
||||
point = new Point(input.readShort(), input.readShort());
|
||||
}
|
||||
|
||||
void writeChunk(DataOutput pOutput) throws IOException {
|
||||
pOutput.writeShort((int) point.getX());
|
||||
pOutput.writeShort((int) point.getY());
|
||||
void writeChunk(DataOutput output) throws IOException {
|
||||
output.writeShort((int) point.getX());
|
||||
output.writeShort((int) point.getY());
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
|
||||
+14
-14
@@ -44,31 +44,31 @@ final class GenericChunk extends IFFChunk {
|
||||
|
||||
byte[] data;
|
||||
|
||||
protected GenericChunk(int pChunkId, int pChunkLength) {
|
||||
super(pChunkId, pChunkLength);
|
||||
data = new byte[chunkLength];
|
||||
GenericChunk(int chunkId, int chunkLength) {
|
||||
super(chunkId, chunkLength);
|
||||
data = new byte[this.chunkLength];
|
||||
}
|
||||
|
||||
protected GenericChunk(int pChunkId, byte[] pChunkData) {
|
||||
super(pChunkId, pChunkData.length);
|
||||
data = pChunkData;
|
||||
GenericChunk(int chunkId, byte[] chunkData) {
|
||||
super(chunkId, chunkData.length);
|
||||
data = chunkData;
|
||||
}
|
||||
|
||||
@Override
|
||||
void readChunk(final DataInput pInput) throws IOException {
|
||||
pInput.readFully(data, 0, data.length);
|
||||
void readChunk(final DataInput input) throws IOException {
|
||||
input.readFully(data, 0, data.length);
|
||||
|
||||
skipData(pInput, chunkLength, data.length);
|
||||
skipData(input, chunkLength, data.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
void writeChunk(final DataOutput pOutput) throws IOException {
|
||||
pOutput.writeInt(chunkId);
|
||||
pOutput.writeInt(chunkLength);
|
||||
pOutput.write(data, 0, data.length);
|
||||
void writeChunk(final DataOutput output) throws IOException {
|
||||
output.writeInt(chunkId);
|
||||
output.writeInt(chunkLength);
|
||||
output.write(data, 0, data.length);
|
||||
|
||||
if (data.length % 2 != 0) {
|
||||
pOutput.writeByte(0); // PAD
|
||||
output.writeByte(0); // PAD
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,8 @@ interface IFF {
|
||||
// TODO:
|
||||
/** IFF DEEP form type (TVPaint) */
|
||||
int TYPE_DEEP = ('D' << 24) + ('E' << 16) + ('E' << 8) + 'P';
|
||||
/** IFF TVPP form type (TVPaint Project) */
|
||||
int TYPE_TVPP = ('T' << 24) + ('V' << 16) + ('P' << 8) + 'P';
|
||||
/** IFF RGB8 form type (TurboSilver) */
|
||||
int TYPE_RGB8 = ('R' << 24) + ('G' << 16) + ('B' << 8) + '8';
|
||||
/** IFF RGBN form type (TurboSilver) */
|
||||
@@ -92,7 +94,7 @@ interface IFF {
|
||||
int CHUNK_COPY = ('(' << 24) + ('c' << 16) + (')' << 8) + ' ';
|
||||
|
||||
/** EA IFF 85 Generic annotation chunk (usually used for Software) */
|
||||
int CHUNK_ANNO = ('A' << 24) + ('N' << 16) + ('N' << 8) + 'O';;
|
||||
int CHUNK_ANNO = ('A' << 24) + ('N' << 16) + ('N' << 8) + 'O';
|
||||
|
||||
/** Third-party defined UTF-8 text. */
|
||||
int CHUNK_UTF8 = ('U' << 24) + ('T' << 16) + ('F' << 8) + '8';
|
||||
@@ -129,6 +131,18 @@ interface IFF {
|
||||
int CHUNK_SHAM = ('S' << 24) + ('H' << 16) + ('A' << 8) + 'M';
|
||||
/** ACBM body chunk */
|
||||
int CHUNK_ABIT = ('A' << 24) + ('B' << 16) + ('I' << 8) + 'T';
|
||||
/** unofficial direct color */
|
||||
/** Unofficial direct color */
|
||||
int CHUNK_DCOL = ('D' << 24) + ('C' << 16) + ('O' << 8) + 'L';
|
||||
/** TVPaint Deep GloBaL information */
|
||||
int CHUNK_DGBL = ('D' << 24) + ('G' << 16) + ('B' << 8) + 'L';
|
||||
/** TVPaint Deep Pixel ELements */
|
||||
int CHUNK_DPEL = ('D' << 24) + ('P' << 16) + ('E' << 8) + 'L';
|
||||
/** TVPaint Deep LOCation information */
|
||||
int CHUNK_DLOC = ('D' << 24) + ('L' << 16) + ('O' << 8) + 'C';
|
||||
/** TVPaint Deep BODy */
|
||||
int CHUNK_DBOD = ('D' << 24) + ('B' << 16) + ('O' << 8) + 'D';
|
||||
/** TVPaint Deep CHanGe buffer */
|
||||
int CHUNK_DCHG = ('D' << 24) + ('C' << 16) + ('H' << 8) + 'G';
|
||||
/** TVPaint 24 bit thumbnail */
|
||||
int CHUNK_XS24 = ('X' << 24) + ('S' << 16) + ('2' << 8) + '4';
|
||||
}
|
||||
|
||||
+8
-8
@@ -44,25 +44,25 @@ abstract class IFFChunk {
|
||||
int chunkId;
|
||||
int chunkLength;
|
||||
|
||||
protected IFFChunk(int pChunkId, int pChunkLength) {
|
||||
chunkId = pChunkId;
|
||||
chunkLength = pChunkLength;
|
||||
protected IFFChunk(int chunkId, int chunkLength) {
|
||||
this.chunkId = chunkId;
|
||||
this.chunkLength = chunkLength;
|
||||
}
|
||||
|
||||
abstract void readChunk(DataInput pInput) throws IOException;
|
||||
abstract void readChunk(DataInput input) throws IOException;
|
||||
|
||||
abstract void writeChunk(DataOutput pOutput) throws IOException;
|
||||
abstract void writeChunk(DataOutput output) throws IOException;
|
||||
|
||||
protected static void skipData(final DataInput pInput, final int chunkLength, final int dataReadSoFar) throws IOException {
|
||||
protected static void skipData(final DataInput input, final int chunkLength, final int dataReadSoFar) throws IOException {
|
||||
int toSkip = chunkLength - dataReadSoFar;
|
||||
|
||||
while (toSkip > 0) {
|
||||
toSkip -= pInput.skipBytes(toSkip);
|
||||
toSkip -= input.skipBytes(toSkip);
|
||||
}
|
||||
|
||||
// Read pad
|
||||
if (chunkLength % 2 != 0) {
|
||||
pInput.readByte();
|
||||
input.readByte();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+38
-25
@@ -13,31 +13,29 @@ import static com.twelvemonkeys.lang.Validate.isTrue;
|
||||
import static com.twelvemonkeys.lang.Validate.notNull;
|
||||
|
||||
final class IFFImageMetadata extends AbstractMetadata {
|
||||
private final int formType;
|
||||
private final BMHDChunk header;
|
||||
private final Form header;
|
||||
private final IndexColorModel colorMap;
|
||||
private final CAMGChunk viewPort;
|
||||
private final List<GenericChunk> meta;
|
||||
|
||||
IFFImageMetadata(int formType, BMHDChunk header, IndexColorModel colorMap, CAMGChunk viewPort, List<GenericChunk> meta) {
|
||||
this.formType = isTrue(validFormType(formType), formType, "Unknown IFF Form type: %s");
|
||||
IFFImageMetadata(Form header, IndexColorModel colorMap) {
|
||||
this.header = notNull(header, "header");
|
||||
isTrue(validFormType(header.formType), header.formType, "Unknown IFF Form type: %s");
|
||||
this.colorMap = colorMap;
|
||||
this.viewPort = viewPort;
|
||||
this.meta = meta;
|
||||
this.meta = header.meta;
|
||||
}
|
||||
|
||||
private boolean validFormType(int formType) {
|
||||
switch (formType) {
|
||||
default:
|
||||
return false;
|
||||
case TYPE_ACBM:
|
||||
case TYPE_DEEP:
|
||||
case TYPE_ILBM:
|
||||
case TYPE_PBM:
|
||||
case TYPE_RGB8:
|
||||
case TYPE_RGBN:
|
||||
case TYPE_TVPP:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +46,7 @@ final class IFFImageMetadata extends AbstractMetadata {
|
||||
IIOMetadataNode csType = new IIOMetadataNode("ColorSpaceType");
|
||||
chroma.appendChild(csType);
|
||||
|
||||
switch (header.bitplanes) {
|
||||
switch (header.bitplanes()) {
|
||||
case 8:
|
||||
if (colorMap == null) {
|
||||
csType.setAttribute("name", "GRAY");
|
||||
@@ -62,6 +60,7 @@ final class IFFImageMetadata extends AbstractMetadata {
|
||||
case 6:
|
||||
case 7:
|
||||
case 24:
|
||||
case 25:
|
||||
case 32:
|
||||
csType.setAttribute("name", "RGB");
|
||||
break;
|
||||
@@ -72,10 +71,10 @@ final class IFFImageMetadata extends AbstractMetadata {
|
||||
// NOTE: Channels in chroma node reflects channels in color model (see data node, for channels in data)
|
||||
IIOMetadataNode numChannels = new IIOMetadataNode("NumChannels");
|
||||
chroma.appendChild(numChannels);
|
||||
if (colorMap == null && header.bitplanes == 8) {
|
||||
if (colorMap == null && header.bitplanes() == 8) {
|
||||
numChannels.setAttribute("value", Integer.toString(1));
|
||||
}
|
||||
else if (header.bitplanes == 32) {
|
||||
else if (header.bitplanes() == 25 || header.bitplanes() == 32) {
|
||||
numChannels.setAttribute("value", Integer.toString(4));
|
||||
}
|
||||
else {
|
||||
@@ -102,9 +101,16 @@ final class IFFImageMetadata extends AbstractMetadata {
|
||||
paletteEntry.setAttribute("green", Integer.toString(colorMap.getGreen(i)));
|
||||
paletteEntry.setAttribute("blue", Integer.toString(colorMap.getBlue(i)));
|
||||
}
|
||||
|
||||
if (colorMap.getTransparentPixel() != -1) {
|
||||
IIOMetadataNode backgroundIndex = new IIOMetadataNode("BackgroundIndex");
|
||||
chroma.appendChild(backgroundIndex);
|
||||
backgroundIndex.setAttribute("value", Integer.toString(colorMap.getTransparentPixel()));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Background color is the color of the transparent index in the color model?
|
||||
// TODO: TVPP TVPaint Project files have a MIXR chunk with a background color
|
||||
// and also a BGP1 (background pen 1?) and BGP2 chunks
|
||||
// if (extensions != null && extensions.getBackgroundColor() != 0) {
|
||||
// Color background = new Color(extensions.getBackgroundColor(), true);
|
||||
//
|
||||
@@ -121,7 +127,7 @@ final class IFFImageMetadata extends AbstractMetadata {
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardCompressionNode() {
|
||||
if (header.compressionType == BMHDChunk.COMPRESSION_NONE) {
|
||||
if (header.compressionType() == BMHDChunk.COMPRESSION_NONE) {
|
||||
return null; // All defaults
|
||||
}
|
||||
|
||||
@@ -144,7 +150,10 @@ final class IFFImageMetadata extends AbstractMetadata {
|
||||
|
||||
// PlanarConfiguration
|
||||
IIOMetadataNode planarConfiguration = new IIOMetadataNode("PlanarConfiguration");
|
||||
switch (formType) {
|
||||
switch (header.formType) {
|
||||
case TYPE_DEEP:
|
||||
case TYPE_TVPP:
|
||||
case TYPE_RGB8:
|
||||
case TYPE_PBM:
|
||||
planarConfiguration.setAttribute("value", "PixelInterleaved");
|
||||
break;
|
||||
@@ -152,7 +161,7 @@ final class IFFImageMetadata extends AbstractMetadata {
|
||||
planarConfiguration.setAttribute("value", "PlaneInterleaved");
|
||||
break;
|
||||
default:
|
||||
planarConfiguration.setAttribute("value", "Unknown " + IFFUtil.toChunkStr(formType));
|
||||
planarConfiguration.setAttribute("value", "Unknown " + IFFUtil.toChunkStr(header.formType));
|
||||
break;
|
||||
}
|
||||
data.appendChild(planarConfiguration);
|
||||
@@ -163,7 +172,7 @@ final class IFFImageMetadata extends AbstractMetadata {
|
||||
|
||||
// BitsPerSample
|
||||
IIOMetadataNode bitsPerSample = new IIOMetadataNode("BitsPerSample");
|
||||
String value = bitsPerSampleValue(header.bitplanes);
|
||||
String value = bitsPerSampleValue(header.bitplanes());
|
||||
bitsPerSample.setAttribute("value", value);
|
||||
data.appendChild(bitsPerSample);
|
||||
|
||||
@@ -171,7 +180,6 @@ final class IFFImageMetadata extends AbstractMetadata {
|
||||
// SampleMSB not in format
|
||||
|
||||
return data;
|
||||
|
||||
}
|
||||
|
||||
private String bitsPerSampleValue(int bitplanes) {
|
||||
@@ -187,16 +195,22 @@ final class IFFImageMetadata extends AbstractMetadata {
|
||||
return Integer.toString(bitplanes);
|
||||
case 24:
|
||||
return "8 8 8";
|
||||
case 25:
|
||||
if (header.formType != TYPE_RGB8) {
|
||||
throw new IllegalArgumentException(String.format("25 bit depth only supported for FORM type RGB8: %s", IFFUtil.toChunkStr(header.formType)));
|
||||
}
|
||||
|
||||
return "8 8 8 1";
|
||||
case 32:
|
||||
return "8 8 8 8";
|
||||
default:
|
||||
throw new IllegalArgumentException("Ubknown bit count: " + bitplanes);
|
||||
throw new IllegalArgumentException("Unknown bit count: " + bitplanes);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardDimensionNode() {
|
||||
if (viewPort == null) {
|
||||
if (header.aspect() == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -204,7 +218,7 @@ final class IFFImageMetadata extends AbstractMetadata {
|
||||
|
||||
// PixelAspectRatio
|
||||
IIOMetadataNode pixelAspectRatio = new IIOMetadataNode("PixelAspectRatio");
|
||||
pixelAspectRatio.setAttribute("value", String.valueOf((viewPort.isHires() ? 2f : 1f) / (viewPort.isLaced() ? 2f : 1f)));
|
||||
pixelAspectRatio.setAttribute("value", String.valueOf(header.aspect()));
|
||||
dimension.appendChild(pixelAspectRatio);
|
||||
|
||||
// TODO: HorizontalScreenSize?
|
||||
@@ -241,20 +255,19 @@ final class IFFImageMetadata extends AbstractMetadata {
|
||||
}
|
||||
|
||||
return text;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardTransparencyNode() {
|
||||
if ((colorMap == null || !colorMap.hasAlpha()) && header.bitplanes != 32) {
|
||||
if ((colorMap == null || !colorMap.hasAlpha()) && header.bitplanes() != 32 && header.bitplanes() != 25) {
|
||||
return null;
|
||||
}
|
||||
|
||||
IIOMetadataNode transparency = new IIOMetadataNode("Transparency");
|
||||
|
||||
if (header.bitplanes == 32) {
|
||||
if (header.bitplanes() == 25 || header.bitplanes() == 32) {
|
||||
IIOMetadataNode alpha = new IIOMetadataNode("Alpha");
|
||||
alpha.setAttribute("value", "nonpremultiplied");
|
||||
alpha.setAttribute("value", header.premultiplied() ? "premultiplied" : "nonpremultiplied");
|
||||
transparency.appendChild(alpha);
|
||||
}
|
||||
|
||||
|
||||
+378
-295
File diff suppressed because it is too large
Load Diff
+16
-17
@@ -30,13 +30,12 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.iff;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
|
||||
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* IFFImageReaderSpi
|
||||
@@ -54,41 +53,41 @@ public final class IFFImageReaderSpi extends ImageReaderSpiBase {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canDecodeInput(Object pSource) throws IOException {
|
||||
return pSource instanceof ImageInputStream && canDecode((ImageInputStream) pSource);
|
||||
public boolean canDecodeInput(final Object source) throws IOException {
|
||||
return source instanceof ImageInputStream && canDecode((ImageInputStream) source);
|
||||
}
|
||||
|
||||
private static boolean canDecode(ImageInputStream pInput) throws IOException {
|
||||
pInput.mark();
|
||||
private static boolean canDecode(final ImageInputStream input) throws IOException {
|
||||
input.mark();
|
||||
|
||||
try {
|
||||
// Is it IFF
|
||||
if (pInput.readInt() == IFF.CHUNK_FORM) {
|
||||
pInput.readInt();// Skip length field
|
||||
if (input.readInt() == IFF.CHUNK_FORM) {
|
||||
input.readInt();// Skip length field
|
||||
|
||||
int type = pInput.readInt();
|
||||
|
||||
// Is it ILBM or PBM
|
||||
if (type == IFF.TYPE_ILBM || type == IFF.TYPE_PBM) {
|
||||
int type = input.readInt();
|
||||
if (type == IFF.TYPE_ILBM || type == IFF.TYPE_PBM
|
||||
|| type == IFF.TYPE_RGB8 // Impulse RGB8 format
|
||||
|| type == IFF.TYPE_DEEP || type == IFF.TYPE_TVPP) { // TVPaint DEEP format
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
finally {
|
||||
pInput.reset();
|
||||
input.reset();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageReader createReaderInstance(Object pExtension) throws IOException {
|
||||
public ImageReader createReaderInstance(final Object extension) {
|
||||
return new IFFImageReader(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription(Locale pLocale) {
|
||||
public String getDescription(Locale locale) {
|
||||
return "Commodore Amiga/Electronic Arts Image Interchange Format (IFF) image reader";
|
||||
}
|
||||
}
|
||||
|
||||
+57
-60
@@ -30,31 +30,22 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.iff;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ColorModel;
|
||||
import java.awt.image.IndexColorModel;
|
||||
import java.awt.image.Raster;
|
||||
import java.awt.image.RenderedImage;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import javax.imageio.IIOImage;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.ImageWriteParam;
|
||||
import javax.imageio.ImageWriter;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.spi.ImageWriterSpi;
|
||||
|
||||
import com.twelvemonkeys.imageio.ImageWriterBase;
|
||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||
import com.twelvemonkeys.io.FastByteArrayOutputStream;
|
||||
import com.twelvemonkeys.io.enc.EncoderStream;
|
||||
import com.twelvemonkeys.io.enc.PackBitsEncoder;
|
||||
|
||||
import javax.imageio.*;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.spi.ImageWriterSpi;
|
||||
import java.awt.*;
|
||||
import java.awt.image.*;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Writer for Commodore Amiga (Electronic Arts) IFF ILBM (InterLeaved BitMap) format.
|
||||
* The IFF format (Interchange File Format) is the standard file format
|
||||
@@ -68,8 +59,8 @@ import com.twelvemonkeys.io.enc.PackBitsEncoder;
|
||||
*/
|
||||
public final class IFFImageWriter extends ImageWriterBase {
|
||||
|
||||
IFFImageWriter(ImageWriterSpi pProvider) {
|
||||
super(pProvider);
|
||||
IFFImageWriter(ImageWriterSpi provider) {
|
||||
super(provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -83,23 +74,29 @@ public final class IFFImageWriter extends ImageWriterBase {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(IIOMetadata pStreamMetadata, IIOImage pImage, ImageWriteParam pParam) throws IOException {
|
||||
public ImageWriteParam getDefaultWriteParam() {
|
||||
return new IFFWriteParam(getLocale());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(IIOMetadata streamMetadata, IIOImage image, ImageWriteParam param) throws IOException {
|
||||
assertOutput();
|
||||
|
||||
if (pImage.hasRaster()) {
|
||||
if (image.hasRaster()) {
|
||||
throw new UnsupportedOperationException("Cannot write raster");
|
||||
}
|
||||
|
||||
processImageStarted(0);
|
||||
|
||||
RenderedImage renderedImage = image.getRenderedImage();
|
||||
boolean compress = shouldCompress(renderedImage, param);
|
||||
|
||||
// Prepare image data to be written
|
||||
ByteArrayOutputStream imageData = new FastByteArrayOutputStream(1024);
|
||||
packImageData(imageData, pImage.getRenderedImage(), pParam);
|
||||
|
||||
//System.out.println("Image data: " + imageData.size());
|
||||
packImageData(imageData, renderedImage, compress);
|
||||
|
||||
// Write metadata
|
||||
writeMeta(pImage.getRenderedImage(), imageData.size());
|
||||
writeMeta(renderedImage, imageData.size(), compress);
|
||||
|
||||
// Write image data
|
||||
writeBody(imageData);
|
||||
@@ -107,34 +104,31 @@ public final class IFFImageWriter extends ImageWriterBase {
|
||||
processImageComplete();
|
||||
}
|
||||
|
||||
private void writeBody(ByteArrayOutputStream pImageData) throws IOException {
|
||||
private void writeBody(ByteArrayOutputStream imageData) throws IOException {
|
||||
imageOutput.writeInt(IFF.CHUNK_BODY);
|
||||
imageOutput.writeInt(pImageData.size());
|
||||
imageOutput.writeInt(imageData.size());
|
||||
|
||||
// NOTE: This is much faster than imageOutput.write(pImageData.toByteArray())
|
||||
// NOTE: This is much faster than imageOutput.write(imageData.toByteArray())
|
||||
// as the data array is not duplicated
|
||||
try (OutputStream adapter = IIOUtil.createStreamAdapter(imageOutput)) {
|
||||
pImageData.writeTo(adapter);
|
||||
imageData.writeTo(adapter);
|
||||
}
|
||||
|
||||
if (pImageData.size() % 2 == 0) {
|
||||
if (imageData.size() % 2 == 0) {
|
||||
imageOutput.writeByte(0); // PAD
|
||||
}
|
||||
|
||||
imageOutput.flush();
|
||||
}
|
||||
|
||||
private void packImageData(OutputStream pOutput, RenderedImage pImage, ImageWriteParam pParam) throws IOException {
|
||||
// TODO: Allow param to dictate uncompressed
|
||||
// TODO: Allow param to dictate type PBM?
|
||||
private void packImageData(OutputStream outputStream, RenderedImage image, final boolean compress) throws IOException {
|
||||
// TODO: Subsample/AOI
|
||||
final boolean compress = shouldCompress(pImage);
|
||||
final OutputStream output = compress ? new EncoderStream(pOutput, new PackBitsEncoder(), true) : pOutput;
|
||||
final ColorModel model = pImage.getColorModel();
|
||||
final Raster raster = pImage.getData();
|
||||
final OutputStream output = compress ? new EncoderStream(outputStream, new PackBitsEncoder(), true) : outputStream;
|
||||
final ColorModel model = image.getColorModel();
|
||||
final Raster raster = image.getData();
|
||||
|
||||
final int width = pImage.getWidth();
|
||||
final int height = pImage.getHeight();
|
||||
final int width = image.getWidth();
|
||||
final int height = image.getHeight();
|
||||
|
||||
// Store each row of pixels
|
||||
// 0. Loop pr channel
|
||||
@@ -142,7 +136,6 @@ public final class IFFImageWriter extends ImageWriterBase {
|
||||
// 2. Perform byteRun1 compression for each plane separately
|
||||
// 3. Write the plane data for each plane
|
||||
|
||||
//final int planeWidth = (width + 7) / 8;
|
||||
final int planeWidth = 2 * ((width + 15) / 16);
|
||||
final byte[] planeData = new byte[8 * planeWidth];
|
||||
final int channels = (model.getPixelSize() + 7) / 8;
|
||||
@@ -167,10 +160,6 @@ public final class IFFImageWriter extends ImageWriterBase {
|
||||
|
||||
for (int p = 0; p < planesPerChannel; p++) {
|
||||
output.write(planeData, p * planeWidth, planeWidth);
|
||||
|
||||
if (!compress && planeWidth % 2 != 0) {
|
||||
output.write(0); // PAD
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,17 +171,16 @@ public final class IFFImageWriter extends ImageWriterBase {
|
||||
output.flush();
|
||||
}
|
||||
|
||||
private void writeMeta(RenderedImage pImage, int pBodyLength) throws IOException {
|
||||
private void writeMeta(RenderedImage image, int bodyLength, boolean compress) throws IOException {
|
||||
// Annotation ANNO chunk, 8 + annoData.length bytes
|
||||
String annotation = String.format("Written by %s IFFImageWriter %s", getOriginatingProvider().getVendorName(), getOriginatingProvider().getVersion());
|
||||
GenericChunk anno = new GenericChunk(IFFUtil.toInt("ANNO".getBytes()), annotation.getBytes());
|
||||
|
||||
ColorModel cm = pImage.getColorModel();
|
||||
ColorModel cm = image.getColorModel();
|
||||
IndexColorModel icm = null;
|
||||
|
||||
// Bitmap header BMHD chunk, 8 + 20 bytes
|
||||
// By default, don't compress narrow images
|
||||
int compression = shouldCompress(pImage) ? BMHDChunk.COMPRESSION_BYTE_RUN : BMHDChunk.COMPRESSION_NONE;
|
||||
int compression = compress ? BMHDChunk.COMPRESSION_BYTE_RUN : BMHDChunk.COMPRESSION_NONE;
|
||||
|
||||
BMHDChunk header;
|
||||
if (cm instanceof IndexColorModel) {
|
||||
@@ -200,12 +188,12 @@ public final class IFFImageWriter extends ImageWriterBase {
|
||||
icm = (IndexColorModel) cm;
|
||||
int trans = icm.getTransparency() == Transparency.BITMASK ? BMHDChunk.MASK_TRANSPARENT_COLOR : BMHDChunk.MASK_NONE;
|
||||
int transPixel = icm.getTransparency() == Transparency.BITMASK ? icm.getTransparentPixel() : 0;
|
||||
header = new BMHDChunk(pImage.getWidth(), pImage.getHeight(), icm.getPixelSize(),
|
||||
header = new BMHDChunk(image.getWidth(), image.getHeight(), icm.getPixelSize(),
|
||||
trans, compression, transPixel);
|
||||
}
|
||||
else {
|
||||
//System.out.println(cm.getClass().getName());
|
||||
header = new BMHDChunk(pImage.getWidth(), pImage.getHeight(), cm.getPixelSize(),
|
||||
header = new BMHDChunk(image.getWidth(), image.getHeight(), cm.getPixelSize(),
|
||||
BMHDChunk.MASK_NONE, compression, 0);
|
||||
}
|
||||
|
||||
@@ -217,7 +205,7 @@ public final class IFFImageWriter extends ImageWriterBase {
|
||||
}
|
||||
|
||||
// ILBM(4) + anno(8+len) + header(8+20) + cmap(8+len)? + body(8+len);
|
||||
int size = 4 + 8 + anno.chunkLength + 28 + 8 + pBodyLength;
|
||||
int size = 4 + 8 + anno.chunkLength + 28 + 8 + bodyLength;
|
||||
if (cmap != null) {
|
||||
size += 8 + cmap.chunkLength;
|
||||
}
|
||||
@@ -231,21 +219,30 @@ public final class IFFImageWriter extends ImageWriterBase {
|
||||
header.writeChunk(imageOutput);
|
||||
|
||||
if (cmap != null) {
|
||||
//System.out.println("CMAP written");
|
||||
cmap.writeChunk(imageOutput);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private boolean shouldCompress(RenderedImage pImage) {
|
||||
return pImage.getWidth() >= 32;
|
||||
private boolean shouldCompress(final RenderedImage image, final ImageWriteParam param) {
|
||||
if (param != null && param.canWriteCompressed()) {
|
||||
switch (param.getCompressionMode()) {
|
||||
case ImageWriteParam.MODE_DISABLED:
|
||||
return false;
|
||||
case ImageWriteParam.MODE_EXPLICIT:
|
||||
return IFFWriteParam.COMPRESSION_TYPES[1].equals(param.getCompressionType());
|
||||
default:
|
||||
// Fall through
|
||||
}
|
||||
}
|
||||
|
||||
return image.getWidth() >= 32;
|
||||
}
|
||||
|
||||
public static void main(String[] pArgs) throws IOException {
|
||||
BufferedImage image = ImageIO.read(new File(pArgs[0]));
|
||||
public static void main(String[] args) throws IOException {
|
||||
BufferedImage image = ImageIO.read(new File(args[0]));
|
||||
|
||||
ImageWriter writer = new IFFImageWriter(new IFFImageWriterSpi());
|
||||
writer.setOutput(ImageIO.createImageOutputStream(new File(pArgs[1])));
|
||||
writer.setOutput(ImageIO.createImageOutputStream(new File(args[1])));
|
||||
//writer.addIIOWriteProgressListener(new ProgressListenerBase() {
|
||||
// int mCurrPct = 0;
|
||||
//
|
||||
|
||||
+5
-7
@@ -30,13 +30,11 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.iff;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
import com.twelvemonkeys.imageio.spi.ImageWriterSpiBase;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.ImageWriter;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ImageWriterSpiBase;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* IFFImageWriterSpi
|
||||
@@ -53,19 +51,19 @@ public class IFFImageWriterSpi extends ImageWriterSpiBase {
|
||||
super(new IFFProviderInfo());
|
||||
}
|
||||
|
||||
public boolean canEncodeImage(final ImageTypeSpecifier pType) {
|
||||
public boolean canEncodeImage(final ImageTypeSpecifier type) {
|
||||
// TODO: Probably can't store 16 bit types etc...
|
||||
// TODO: Can't store CMYK (well.. it does, but they can't be read back)
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageWriter createWriterInstance(Object pExtension) throws IOException {
|
||||
public ImageWriter createWriterInstance(Object extension) {
|
||||
return new IFFImageWriter(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription(Locale pLocale) {
|
||||
public String getDescription(Locale locale) {
|
||||
return "Commodore Amiga/Electronic Arts Image Interchange Format (IFF) image writer";
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -40,11 +40,11 @@ import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
||||
* @version $Id: IFFProviderInfo.java,v 1.0 20/03/15 harald.kuhr Exp$
|
||||
*/
|
||||
final class IFFProviderInfo extends ReaderWriterProviderInfo {
|
||||
protected IFFProviderInfo() {
|
||||
IFFProviderInfo() {
|
||||
super(
|
||||
IFFProviderInfo.class,
|
||||
new String[] {"iff", "IFF"},
|
||||
new String[] {"iff", "lbm", "ham", "ham8", "ilbm"},
|
||||
new String[] {"iff", "lbm", "ham", "ham8", "ilbm", "rgb8", "deep"},
|
||||
new String[] {"image/iff", "image/x-iff"},
|
||||
"com.twelvemonkeys.imageio.plugins.iff.IFFImageReader",
|
||||
new String[]{"com.twelvemonkeys.imageio.plugins.iff.IFFImageReaderSpi"},
|
||||
|
||||
+107
-106
@@ -56,11 +56,11 @@ final class IFFUtil {
|
||||
* @return the rotation table
|
||||
*/
|
||||
static private long[] rtable(int n) {
|
||||
return new long[]{
|
||||
0x00000000l << n, 0x00000001l << n, 0x00000100l << n, 0x00000101l << n,
|
||||
0x00010000l << n, 0x00010001l << n, 0x00010100l << n, 0x00010101l << n,
|
||||
0x01000000l << n, 0x01000001l << n, 0x01000100l << n, 0x01000101l << n,
|
||||
0x01010000l << n, 0x01010001l << n, 0x01010100l << n, 0x01010101l << n
|
||||
return new long[] {
|
||||
0x00000000L , 0x00000001L << n, 0x00000100L << n, 0x00000101L << n,
|
||||
0x00010000L << n, 0x00010001L << n, 0x00010100L << n, 0x00010101L << n,
|
||||
0x01000000L << n, 0x01000001L << n, 0x01000100L << n, 0x01000101L << n,
|
||||
0x01010000L << n, 0x01010001L << n, 0x01010100L << n, 0x01010101L << n
|
||||
};
|
||||
}
|
||||
|
||||
@@ -75,16 +75,16 @@ final class IFFUtil {
|
||||
* Bits from the source are rotated 90 degrees clockwise written to the
|
||||
* destination.
|
||||
*
|
||||
* @param pSrc source pixel data
|
||||
* @param pSrcPos starting index of 8 x 8 bit source tile
|
||||
* @param pSrcStep byte offset between adjacent rows in source
|
||||
* @param pDst destination pixel data
|
||||
* @param pDstPos starting index of 8 x 8 bit destination tile
|
||||
* @param pDstStep byte offset between adjacent rows in destination
|
||||
* @param src source pixel data
|
||||
* @param srcPos starting index of 8 x 8 bit source tile
|
||||
* @param srcStep byte offset between adjacent rows in source
|
||||
* @param dst destination pixel data
|
||||
* @param dstPos starting index of 8 x 8 bit destination tile
|
||||
* @param dstStep byte offset between adjacent rows in destination
|
||||
*/
|
||||
static void bitRotateCW(final byte[] pSrc, int pSrcPos, int pSrcStep,
|
||||
final byte[] pDst, int pDstPos, int pDstStep) {
|
||||
int idx = pSrcPos;
|
||||
static void bitRotateCW(final byte[] src, int srcPos, int srcStep,
|
||||
final byte[] dst, int dstPos, int dstStep) {
|
||||
int idx = srcPos;
|
||||
|
||||
int lonyb;
|
||||
int hinyb;
|
||||
@@ -92,41 +92,41 @@ final class IFFUtil {
|
||||
long hi = 0;
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
lonyb = pSrc[idx] & 0xF;
|
||||
hinyb = (pSrc[idx] >> 4) & 0xF;
|
||||
lonyb = src[idx] & 0xF;
|
||||
hinyb = (src[idx] >> 4) & 0xF;
|
||||
lo |= RTABLE[i][lonyb];
|
||||
hi |= RTABLE[i][hinyb];
|
||||
idx += pSrcStep;
|
||||
idx += srcStep;
|
||||
}
|
||||
|
||||
idx = pDstPos;
|
||||
idx = dstPos;
|
||||
|
||||
pDst[idx] = (byte)((hi >> 24) & 0xFF);
|
||||
idx += pDstStep;
|
||||
if (idx < pDst.length) {
|
||||
pDst[idx] = (byte)((hi >> 16) & 0xFF);
|
||||
idx += pDstStep;
|
||||
if (idx < pDst.length) {
|
||||
pDst[idx] = (byte)((hi >> 8) & 0xFF);
|
||||
idx += pDstStep;
|
||||
if (idx < pDst.length) {
|
||||
pDst[idx] = (byte)(hi & 0xFF);
|
||||
idx += pDstStep;
|
||||
dst[idx] = (byte)((hi >> 24) & 0xFF);
|
||||
idx += dstStep;
|
||||
if (idx < dst.length) {
|
||||
dst[idx] = (byte)((hi >> 16) & 0xFF);
|
||||
idx += dstStep;
|
||||
if (idx < dst.length) {
|
||||
dst[idx] = (byte)((hi >> 8) & 0xFF);
|
||||
idx += dstStep;
|
||||
if (idx < dst.length) {
|
||||
dst[idx] = (byte)(hi & 0xFF);
|
||||
idx += dstStep;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (idx < pDst.length) {
|
||||
pDst[idx] = (byte)((lo >> 24) & 0xFF);
|
||||
idx += pDstStep;
|
||||
if (idx < pDst.length) {
|
||||
pDst[idx] = (byte)((lo >> 16) & 0xFF);
|
||||
idx += pDstStep;
|
||||
if (idx < pDst.length) {
|
||||
pDst[idx] = (byte)((lo >> 8) & 0xFF);
|
||||
idx += pDstStep;
|
||||
if (idx < pDst.length) {
|
||||
pDst[idx] = (byte)(lo & 0xFF);
|
||||
if (idx < dst.length) {
|
||||
dst[idx] = (byte)((lo >> 24) & 0xFF);
|
||||
idx += dstStep;
|
||||
if (idx < dst.length) {
|
||||
dst[idx] = (byte)((lo >> 16) & 0xFF);
|
||||
idx += dstStep;
|
||||
if (idx < dst.length) {
|
||||
dst[idx] = (byte)((lo >> 8) & 0xFF);
|
||||
idx += dstStep;
|
||||
if (idx < dst.length) {
|
||||
dst[idx] = (byte)(lo & 0xFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -137,16 +137,16 @@ final class IFFUtil {
|
||||
* Rotate bits counterclockwise.
|
||||
* The IFFImageWriter uses this to convert pixel bits from chunky to planar.
|
||||
*
|
||||
* @param pSrc source pixel data (only lower 8 bits used)
|
||||
* @param pSrcPos starting index of 8 x 8 bit source tile
|
||||
* @param pSrcStep byte offset between adjacent rows in source
|
||||
* @param pDst destination pixel data
|
||||
* @param pDstPos starting index of 8 x 8 bit destination tile
|
||||
* @param pDstStep byte offset between adjacent rows in destination
|
||||
* @param src source pixel data (only lower 8 bits used)
|
||||
* @param srcPos starting index of 8 x 8 bit source tile
|
||||
* @param srcStep byte offset between adjacent rows in source
|
||||
* @param dst destination pixel data
|
||||
* @param dstPos starting index of 8 x 8 bit destination tile
|
||||
* @param dstStep byte offset between adjacent rows in destination
|
||||
*/
|
||||
static void bitRotateCCW(final int[] pSrc, int pSrcPos, int pSrcStep,
|
||||
final byte[] pDst, int pDstPos, int pDstStep) {
|
||||
int idx = pSrcPos;
|
||||
static void bitRotateCCW(final int[] src, int srcPos, @SuppressWarnings("SameParameterValue") int srcStep,
|
||||
final byte[] dst, int dstPos, int dstStep) {
|
||||
int idx = srcPos;
|
||||
|
||||
int lonyb;
|
||||
int hinyb;
|
||||
@@ -154,48 +154,49 @@ final class IFFUtil {
|
||||
long hi = 0;
|
||||
|
||||
for (int i = 7; i >= 0; i--) {
|
||||
lonyb = pSrc[idx] & 0xF;
|
||||
hinyb = (pSrc[idx] >> 4) & 0xF;
|
||||
lonyb = src[idx] & 0xF;
|
||||
hinyb = (src[idx] >> 4) & 0xF;
|
||||
lo |= RTABLE[i][lonyb];
|
||||
hi |= RTABLE[i][hinyb];
|
||||
idx += pSrcStep;
|
||||
idx += srcStep;
|
||||
}
|
||||
|
||||
idx = pDstPos;
|
||||
idx = dstPos;
|
||||
|
||||
pDst[idx] = (byte)(lo & 0xFF);
|
||||
idx += pDstStep;
|
||||
pDst[idx] = (byte)((lo >> 8) & 0xFF);
|
||||
idx += pDstStep;
|
||||
pDst[idx] = (byte)((lo >> 16) & 0xFF);
|
||||
idx += pDstStep;
|
||||
pDst[idx] = (byte)((lo >> 24) & 0xFF);
|
||||
dst[idx] = (byte)(lo & 0xFF);
|
||||
idx += dstStep;
|
||||
dst[idx] = (byte)((lo >> 8) & 0xFF);
|
||||
idx += dstStep;
|
||||
dst[idx] = (byte)((lo >> 16) & 0xFF);
|
||||
idx += dstStep;
|
||||
dst[idx] = (byte)((lo >> 24) & 0xFF);
|
||||
|
||||
idx += pDstStep;
|
||||
idx += dstStep;
|
||||
|
||||
pDst[idx] = (byte)(hi & 0xFF);
|
||||
idx += pDstStep;
|
||||
pDst[idx] = (byte)((hi >> 8) & 0xFF);
|
||||
idx += pDstStep;
|
||||
pDst[idx] = (byte)((hi >> 16) & 0xFF);
|
||||
idx += pDstStep;
|
||||
pDst[idx] = (byte)((hi >> 24) & 0xFF);
|
||||
dst[idx] = (byte)(hi & 0xFF);
|
||||
idx += dstStep;
|
||||
dst[idx] = (byte)((hi >> 8) & 0xFF);
|
||||
idx += dstStep;
|
||||
dst[idx] = (byte)((hi >> 16) & 0xFF);
|
||||
idx += dstStep;
|
||||
dst[idx] = (byte)((hi >> 24) & 0xFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotate bits counterclockwise.
|
||||
* The IFFImageWriter uses this to convert pixel bits from chunky to planar.
|
||||
*
|
||||
* @param pSrc source pixel data
|
||||
* @param pSrcPos starting index of 8 x 8 bit source tile
|
||||
* @param pSrcStep byte offset between adjacent rows in source
|
||||
* @param pDst destination pixel data
|
||||
* @param pDstPos starting index of 8 x 8 bit destination tile
|
||||
* @param pDstStep byte offset between adjacent rows in destination
|
||||
* @param src source pixel data
|
||||
* @param srcPos starting index of 8 x 8 bit source tile
|
||||
* @param srcStep byte offset between adjacent rows in source
|
||||
* @param dst destination pixel data
|
||||
* @param dstPos starting index of 8 x 8 bit destination tile
|
||||
* @param dstStep byte offset between adjacent rows in destination
|
||||
*/
|
||||
static void bitRotateCCW(final byte[] pSrc, int pSrcPos, int pSrcStep,
|
||||
final byte[] pDst, int pDstPos, int pDstStep) {
|
||||
int idx = pSrcPos;
|
||||
@SuppressWarnings("unused")
|
||||
static void bitRotateCCW(final byte[] src, int srcPos, int srcStep,
|
||||
final byte[] dst, int dstPos, int dstStep) {
|
||||
int idx = srcPos;
|
||||
|
||||
int lonyb;
|
||||
int hinyb;
|
||||
@@ -203,57 +204,57 @@ final class IFFUtil {
|
||||
long hi = 0;
|
||||
|
||||
for (int i = 7; i >= 0; i--) {
|
||||
lonyb = pSrc[idx] & 0xF;
|
||||
hinyb = (pSrc[idx] >> 4) & 0xF;
|
||||
lonyb = src[idx] & 0xF;
|
||||
hinyb = (src[idx] >> 4) & 0xF;
|
||||
lo |= RTABLE[i][lonyb];
|
||||
hi |= IFFUtil.RTABLE[i][hinyb];
|
||||
idx += pSrcStep;
|
||||
idx += srcStep;
|
||||
}
|
||||
|
||||
idx = pDstPos;
|
||||
idx = dstPos;
|
||||
|
||||
pDst[idx] = (byte)(lo & 0xFF);
|
||||
idx += pDstStep;
|
||||
pDst[idx] = (byte)((lo >> 8) & 0xFF);
|
||||
idx += pDstStep;
|
||||
pDst[idx] = (byte)((lo >> 16) & 0xFF);
|
||||
idx += pDstStep;
|
||||
pDst[idx] = (byte)((lo >> 24) & 0xFF);
|
||||
dst[idx] = (byte)(lo & 0xFF);
|
||||
idx += dstStep;
|
||||
dst[idx] = (byte)((lo >> 8) & 0xFF);
|
||||
idx += dstStep;
|
||||
dst[idx] = (byte)((lo >> 16) & 0xFF);
|
||||
idx += dstStep;
|
||||
dst[idx] = (byte)((lo >> 24) & 0xFF);
|
||||
|
||||
idx += pDstStep;
|
||||
idx += dstStep;
|
||||
|
||||
pDst[idx] = (byte)(hi & 0xFF);
|
||||
idx += pDstStep;
|
||||
pDst[idx] = (byte)((hi >> 8) & 0xFF);
|
||||
idx += pDstStep;
|
||||
pDst[idx] = (byte)((hi >> 16) & 0xFF);
|
||||
idx += pDstStep;
|
||||
pDst[idx] = (byte)((hi >> 24) & 0xFF);
|
||||
dst[idx] = (byte)(hi & 0xFF);
|
||||
idx += dstStep;
|
||||
dst[idx] = (byte)((hi >> 8) & 0xFF);
|
||||
idx += dstStep;
|
||||
dst[idx] = (byte)((hi >> 16) & 0xFF);
|
||||
idx += dstStep;
|
||||
dst[idx] = (byte)((hi >> 24) & 0xFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a byte array to an int.
|
||||
*
|
||||
* @param pBytes a byte array of length 4
|
||||
* @param bytes a byte array of length 4
|
||||
* @return the bytes converted to an int
|
||||
*
|
||||
* @throws ArrayIndexOutOfBoundsException if length is < 4
|
||||
*/
|
||||
static int toInt(final byte[] pBytes) {
|
||||
return (pBytes[0] & 0xff) << 24 | (pBytes[1] & 0xff) << 16
|
||||
| (pBytes[2] & 0xff) << 8 | (pBytes[3] & 0xff);
|
||||
static int toInt(final byte[] bytes) {
|
||||
return (bytes[0] & 0xff) << 24 | (bytes[1] & 0xff) << 16
|
||||
| (bytes[2] & 0xff) << 8 | (bytes[3] & 0xff);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an int to a four letter String.
|
||||
*
|
||||
* @param pChunkId the chunk identifier
|
||||
* @param chunkId the chunk identifier
|
||||
* @return a String
|
||||
*/
|
||||
static String toChunkStr(int pChunkId) {
|
||||
return new String(new byte[] {(byte) ((pChunkId & 0xff000000) >> 24),
|
||||
(byte) ((pChunkId & 0x00ff0000) >> 16),
|
||||
(byte) ((pChunkId & 0x0000ff00) >> 8),
|
||||
(byte) ((pChunkId & 0x000000ff))});
|
||||
static String toChunkStr(int chunkId) {
|
||||
return new String(new byte[] {(byte) ((chunkId & 0xff000000) >> 24),
|
||||
(byte) ((chunkId & 0x00ff0000) >> 16),
|
||||
(byte) ((chunkId & 0x0000ff00) >> 8),
|
||||
(byte) ((chunkId & 0x000000ff))});
|
||||
}
|
||||
}
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package com.twelvemonkeys.imageio.plugins.iff;
|
||||
|
||||
import javax.imageio.ImageWriteParam;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* IFFWriteParam.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: IFFWriteParam.java,v 1.0 03/02/2022 haraldk Exp$
|
||||
*/
|
||||
public final class IFFWriteParam extends ImageWriteParam {
|
||||
|
||||
static final String[] COMPRESSION_TYPES = {"NONE", "RLE"};
|
||||
|
||||
public IFFWriteParam(final Locale locale) {
|
||||
super(locale);
|
||||
|
||||
compressionTypes = COMPRESSION_TYPES;
|
||||
compressionType = compressionTypes[1];
|
||||
|
||||
canWriteCompressed = true;
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -80,9 +80,9 @@ final class MutableIndexColorModel extends ColorModel {
|
||||
// TODO: Move validation to chunk (when reading)
|
||||
if (index >= rgbs.length) {
|
||||
// TODO: Issue IIO warning
|
||||
System.err.printf("warning - palette change register out of range\n");
|
||||
System.err.println("warning - palette change register out of range");
|
||||
System.err.printf(" change structure %d index=%d (max %d)\n", i, index, getMapSize() - 1);
|
||||
System.err.printf(" ignoring it... colors might get messed up from here\n");
|
||||
System.err.println(" ignoring it... colors might get messed up from here");
|
||||
}
|
||||
else if (index != MP_REG_IGNORE) {
|
||||
updateRGB(index, ((changes[i].r & 0xff) << 16) | ((changes[i].g & 0xff) << 8) | (changes[i].b & 0xff));
|
||||
|
||||
+18
-17
@@ -72,42 +72,43 @@ final class PCHGChunk extends AbstractMultiPaletteChunk {
|
||||
private int totalChanges;
|
||||
private int minReg;
|
||||
|
||||
public PCHGChunk(int pChunkLength) {
|
||||
super(IFF.CHUNK_PCHG, pChunkLength);
|
||||
PCHGChunk(int chunkLength) {
|
||||
super(IFF.CHUNK_PCHG, chunkLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
void readChunk(final DataInput pInput) throws IOException {
|
||||
int compression = pInput.readUnsignedShort();
|
||||
int flags = pInput.readUnsignedShort();
|
||||
startLine = pInput.readShort();
|
||||
lineCount = pInput.readUnsignedShort();
|
||||
changedLines = pInput.readUnsignedShort();
|
||||
minReg = pInput.readUnsignedShort();
|
||||
int maxReg = pInput.readUnsignedShort();
|
||||
/*int maxChangesPerLine = */pInput.readUnsignedShort(); // We don't really care, as we're not limited by the Amiga display hardware
|
||||
totalChanges = pInput.readInt();
|
||||
void readChunk(final DataInput input) throws IOException {
|
||||
int compression = input.readUnsignedShort();
|
||||
int flags = input.readUnsignedShort();
|
||||
startLine = input.readShort();
|
||||
lineCount = input.readUnsignedShort();
|
||||
changedLines = input.readUnsignedShort();
|
||||
minReg = input.readUnsignedShort();
|
||||
int maxReg = input.readUnsignedShort();
|
||||
/*int maxChangesPerLine = */
|
||||
input.readUnsignedShort(); // We don't really care, as we're not limited by the Amiga display hardware
|
||||
totalChanges = input.readInt();
|
||||
|
||||
byte[] data;
|
||||
|
||||
switch (compression) {
|
||||
case PCHG_COMP_NONE:
|
||||
data = new byte[chunkLength - 20];
|
||||
pInput.readFully(data);
|
||||
input.readFully(data);
|
||||
|
||||
break;
|
||||
case PCHG_COMP_HUFFMAN:
|
||||
// NOTE: Huffman decompression is completely untested, due to lack of source data (read: Probably broken).
|
||||
int compInfoSize = pInput.readInt();
|
||||
int originalDataSize = pInput.readInt();
|
||||
int compInfoSize = input.readInt();
|
||||
int originalDataSize = input.readInt();
|
||||
|
||||
short[] compTree = new short[compInfoSize / 2];
|
||||
for (int i = 0; i < compTree.length; i++) {
|
||||
compTree[i] = pInput.readShort();
|
||||
compTree[i] = input.readShort();
|
||||
}
|
||||
|
||||
byte[] compData = new byte[chunkLength - 20 - 8 - compInfoSize];
|
||||
pInput.readFully(compData);
|
||||
input.readFully(compData);
|
||||
|
||||
data = new byte[originalDataSize];
|
||||
|
||||
|
||||
Executable → Regular
+87
-93
@@ -1,93 +1,87 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 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.servlet.fileupload;
|
||||
|
||||
import org.apache.commons.fileupload.FileItem;
|
||||
import org.apache.commons.fileupload.FileUploadException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* An {@code UploadedFile} implementation, based on
|
||||
* <a href="http://jakarta.apache.org/commons/fileupload/">Jakarta Commons FileUpload</a>.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @version $Id: UploadedFileImpl.java#1 $
|
||||
*/
|
||||
class UploadedFileImpl implements UploadedFile {
|
||||
private final FileItem item;
|
||||
|
||||
public UploadedFileImpl(FileItem pItem) {
|
||||
if (pItem == null) {
|
||||
throw new IllegalArgumentException("fileitem == null");
|
||||
}
|
||||
|
||||
item = pItem;
|
||||
}
|
||||
|
||||
public String getContentType() {
|
||||
return item.getContentType();
|
||||
}
|
||||
|
||||
public InputStream getInputStream() throws IOException {
|
||||
return item.getInputStream();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return item.getName();
|
||||
}
|
||||
|
||||
public long length() {
|
||||
return item.getSize();
|
||||
}
|
||||
|
||||
public void writeTo(File pFile) throws IOException {
|
||||
try {
|
||||
item.write(pFile);
|
||||
}
|
||||
catch(RuntimeException e) {
|
||||
throw e;
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw e;
|
||||
}
|
||||
catch (FileUploadException e) {
|
||||
// We deliberately change this exception to an IOException, as it really is
|
||||
throw (IOException) new IOException(e.getMessage()).initCause(e);
|
||||
}
|
||||
catch (Exception e) {
|
||||
// Should not really happen, ever
|
||||
throw new RuntimeException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) 2022, 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.iff;
|
||||
|
||||
import com.twelvemonkeys.io.enc.DecodeException;
|
||||
import com.twelvemonkeys.io.enc.Decoder;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Decoder implementation for Impulse FORM RGB8 RLE compression (type 4).
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: RGB8RLEDecoder.java,v 1.0 28/01/2022 haraldk Exp$
|
||||
*
|
||||
* @see <a href="https://wiki.amigaos.net/wiki/RGBN_and_RGB8_IFF_Image_Data">RGBN and RGB8 IFF Image Data</a>
|
||||
*/
|
||||
final class RGB8RLEDecoder implements Decoder {
|
||||
public int decode(final InputStream stream, final ByteBuffer buffer) throws IOException {
|
||||
while (buffer.remaining() >= 127 * 4) {
|
||||
int r = stream.read();
|
||||
int g = stream.read();
|
||||
int b = stream.read();
|
||||
int a = stream.read();
|
||||
|
||||
if (a < 0) {
|
||||
// Normal EOF
|
||||
if (r == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Partial pixel read...
|
||||
throw new EOFException();
|
||||
}
|
||||
|
||||
// Get "genlock" (transparency) bit + count
|
||||
boolean alpha = (a & 0x80) != 0;
|
||||
int count = a & 0x7f;
|
||||
a = alpha ? 0 : (byte) 0xff; // convert to full transparent/opaque;
|
||||
|
||||
if (count == 0) {
|
||||
throw new DecodeException("Multi-byte counts not supported");
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
buffer.put((byte) r);
|
||||
buffer.put((byte) g);
|
||||
buffer.put((byte) b);
|
||||
buffer.put((byte) a);
|
||||
}
|
||||
}
|
||||
|
||||
return buffer.position();
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -38,8 +38,8 @@ package com.twelvemonkeys.imageio.plugins.iff;
|
||||
* @version $Id: SHAMChunk.java,v 1.0 30.03.12 14:53 haraldk Exp$
|
||||
*/
|
||||
final class SHAMChunk extends AbstractMultiPaletteChunk {
|
||||
protected SHAMChunk(int pChunkLength) {
|
||||
super(IFF.CHUNK_SHAM, pChunkLength);
|
||||
SHAMChunk(int chunkLength) {
|
||||
super(IFF.CHUNK_SHAM, chunkLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
package com.twelvemonkeys.imageio.plugins.iff;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* XS24Chunk.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: XS24Chunk.java,v 1.0 01/02/2022 haraldk Exp$
|
||||
*/
|
||||
final class XS24Chunk extends IFFChunk {
|
||||
private byte[] data;
|
||||
int width;
|
||||
int height;
|
||||
|
||||
XS24Chunk(final int chunkLength) {
|
||||
super(IFF.CHUNK_XS24, chunkLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
void readChunk(final DataInput input) throws IOException {
|
||||
width = input.readUnsignedShort();
|
||||
height = input.readUnsignedShort();
|
||||
input.readShort(); // Not sure what this is?
|
||||
|
||||
int dataLength = width * height * 3;
|
||||
if (dataLength > chunkLength - 6) {
|
||||
throw new IIOException("Bad XS24 chunk: " + width + " * " + height + " * 3 > chunk length (" + chunkLength + ")");
|
||||
}
|
||||
|
||||
data = new byte[dataLength];
|
||||
|
||||
input.readFully(data);
|
||||
|
||||
// Skip pad
|
||||
for (int i = 0; i < chunkLength - dataLength - 6; i++) {
|
||||
input.readByte();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void writeChunk(final DataOutput output) {
|
||||
throw new InternalError("Not implemented: writeChunk()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString()
|
||||
+ "{thumbnail=" + data.length + '}';
|
||||
}
|
||||
|
||||
public BufferedImage thumbnail() {
|
||||
BufferedImage thumbnail = ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR)
|
||||
.createBufferedImage(width, height);
|
||||
thumbnail.getRaster().setDataElements(0, 0, width, height, data);
|
||||
return thumbnail;
|
||||
}
|
||||
}
|
||||
+263
-91
@@ -1,26 +1,24 @@
|
||||
package com.twelvemonkeys.imageio.plugins.iff;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.awt.image.IndexColorModel;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.imageio.metadata.IIOMetadataFormatImpl;
|
||||
import javax.imageio.metadata.IIOMetadataNode;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.function.ThrowingRunnable;
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.metadata.IIOMetadataFormatImpl;
|
||||
import javax.imageio.metadata.IIOMetadataNode;
|
||||
import java.awt.image.IndexColorModel;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class IFFImageMetadataTest {
|
||||
@Test
|
||||
public void testStandardFeatures() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
public void testStandardFeatures() throws IIOException {
|
||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||
.with(new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
||||
|
||||
final IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, null, Collections.<GenericChunk>emptyList());
|
||||
final IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
// Standard metadata format
|
||||
assertTrue(metadata.isStandardMetadataFormatSupported());
|
||||
@@ -49,10 +47,11 @@ public class IFFImageMetadataTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardChromaGray() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
public void testStandardChromaGray() throws IIOException {
|
||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||
.with(new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, null, Collections.<GenericChunk>emptyList());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
IIOMetadataNode chroma = metadata.getStandardChromaNode();
|
||||
assertNotNull(chroma);
|
||||
@@ -75,10 +74,11 @@ public class IFFImageMetadataTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardChromaRGB() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
public void testStandardChromaRGB() throws IIOException {
|
||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||
.with(new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, null, Collections.<GenericChunk>emptyList());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
IIOMetadataNode chroma = metadata.getStandardChromaNode();
|
||||
assertNotNull(chroma);
|
||||
@@ -101,16 +101,17 @@ public class IFFImageMetadataTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardChromaPalette() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 1, BMHDChunk.MASK_TRANSPARENT_COLOR, BMHDChunk.COMPRESSION_BYTE_RUN, 1);
|
||||
public void testStandardChromaPalette() throws IIOException {
|
||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||
.with(new BMHDChunk(300, 200, 1, BMHDChunk.MASK_TRANSPARENT_COLOR, BMHDChunk.COMPRESSION_BYTE_RUN, 1));
|
||||
|
||||
byte[] bw = {0, (byte) 0xff};
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, new IndexColorModel(header.bitplanes, bw.length, bw, bw, bw, header.transparentIndex), null, Collections.<GenericChunk>emptyList());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, new IndexColorModel(header.bitplanes(), bw.length, bw, bw, bw, header.transparentIndex()));
|
||||
|
||||
IIOMetadataNode chroma = metadata.getStandardChromaNode();
|
||||
assertNotNull(chroma);
|
||||
assertEquals("Chroma", chroma.getNodeName());
|
||||
assertEquals(4, chroma.getLength());
|
||||
assertEquals(5, chroma.getLength());
|
||||
|
||||
IIOMetadataNode colorSpaceType = (IIOMetadataNode) chroma.getFirstChild();
|
||||
assertEquals("ColorSpaceType", colorSpaceType.getNodeName());
|
||||
@@ -138,14 +139,21 @@ public class IFFImageMetadataTest {
|
||||
assertEquals(rgb, item0.getAttribute("blue"));
|
||||
}
|
||||
|
||||
// TODO: BackgroundIndex == 1??
|
||||
// BackgroundIndex == 1
|
||||
IIOMetadataNode backgroundIndex = (IIOMetadataNode) palette.getNextSibling();
|
||||
assertEquals("BackgroundIndex", backgroundIndex.getNodeName());
|
||||
assertEquals("1", backgroundIndex.getAttribute("value"));
|
||||
|
||||
// No more elements
|
||||
assertNull(backgroundIndex.getNextSibling());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardCompressionRLE() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
public void testStandardCompressionRLE() throws IIOException {
|
||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||
.with(new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, null, Collections.<GenericChunk>emptyList());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
IIOMetadataNode compression = metadata.getStandardCompressionNode();
|
||||
assertNotNull(compression);
|
||||
@@ -164,19 +172,21 @@ public class IFFImageMetadataTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardCompressionNone() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_NONE, 0);
|
||||
public void testStandardCompressionNone() throws IIOException {
|
||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||
.with(new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_NONE, 0));
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, null, Collections.<GenericChunk>emptyList());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
assertNull(metadata.getStandardCompressionNode()); // No compression, all default...
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardDataILBM_Gray() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
public void testStandardDataILBM_Gray() throws IIOException {
|
||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||
.with(new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, null, Collections.<GenericChunk>emptyList());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
IIOMetadataNode data = metadata.getStandardDataNode();
|
||||
assertNotNull(data);
|
||||
@@ -199,10 +209,11 @@ public class IFFImageMetadataTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardDataILBM_RGB() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
public void testStandardDataILBM_RGB() throws IIOException {
|
||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||
.with(new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, null, Collections.<GenericChunk>emptyList());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
IIOMetadataNode data = metadata.getStandardDataNode();
|
||||
assertNotNull(data);
|
||||
@@ -225,10 +236,11 @@ public class IFFImageMetadataTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardDataILBM_RGBA() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 32, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
public void testStandardDataILBM_RGBA() throws IIOException {
|
||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||
.with(new BMHDChunk(300, 200, 32, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, null, Collections.<GenericChunk>emptyList());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
IIOMetadataNode data = metadata.getStandardDataNode();
|
||||
assertNotNull(data);
|
||||
@@ -251,12 +263,13 @@ public class IFFImageMetadataTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardDataILBM_Palette() {
|
||||
public void testStandardDataILBM_Palette() throws IIOException {
|
||||
for (int i = 1; i <= 8; i++) {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, i, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||
.with(new BMHDChunk(300, 200, i, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
||||
|
||||
byte[] rgb = new byte[2 << i]; // Colors doesn't really matter here
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, new IndexColorModel(header.bitplanes, rgb.length, rgb, rgb, rgb, 0), null, Collections.<GenericChunk>emptyList());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, new IndexColorModel(header.bitplanes(), rgb.length, rgb, rgb, rgb, 0));
|
||||
|
||||
IIOMetadataNode data = metadata.getStandardDataNode();
|
||||
assertNotNull(data);
|
||||
@@ -280,10 +293,11 @@ public class IFFImageMetadataTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardDataPBM_Gray() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
public void testStandardDataPBM_Gray() throws IIOException {
|
||||
Form header = Form.ofType(IFF.TYPE_PBM)
|
||||
.with(new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_PBM, header, null, null, Collections.<GenericChunk>emptyList());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
IIOMetadataNode data = metadata.getStandardDataNode();
|
||||
assertNotNull(data);
|
||||
@@ -306,10 +320,11 @@ public class IFFImageMetadataTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardDataPBM_RGB() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
public void testStandardDataPBM_RGB() throws IIOException {
|
||||
Form header = Form.ofType(IFF.TYPE_PBM)
|
||||
.with(new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_PBM, header, null, null, Collections.<GenericChunk>emptyList());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
IIOMetadataNode data = metadata.getStandardDataNode();
|
||||
assertNotNull(data);
|
||||
@@ -333,40 +348,57 @@ public class IFFImageMetadataTest {
|
||||
|
||||
|
||||
@Test
|
||||
public void testStandardDimensionNoViewport() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
public void testStandardDimensionNoViewport() throws IIOException {
|
||||
BMHDChunk bitmapHeader = new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
bitmapHeader.xAspect = 0;
|
||||
bitmapHeader.yAspect = 0;
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, null, Collections.<GenericChunk>emptyList());
|
||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||
.with(bitmapHeader);
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
IIOMetadataNode dimension = metadata.getStandardDimensionNode();
|
||||
assertNull(dimension);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardDimensionNormal() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
public void testStandardDimensionNormal() throws IIOException {
|
||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||
.with(new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0))
|
||||
.with(new CAMGChunk(4));
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, new CAMGChunk(4), Collections.<GenericChunk>emptyList());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
IIOMetadataNode dimension = metadata.getStandardDimensionNode();
|
||||
assertNotNull(dimension);
|
||||
assertEquals("Dimension", dimension.getNodeName());
|
||||
assertEquals(1, dimension.getLength());
|
||||
|
||||
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) dimension.getFirstChild();
|
||||
assertEquals("PixelAspectRatio", pixelAspectRatio.getNodeName());
|
||||
assertEquals("1.0", pixelAspectRatio.getAttribute("value"));
|
||||
// No Dimension node is okay, or one with an aspect ratio of 1.0
|
||||
if (dimension != null) {
|
||||
assertEquals("Dimension", dimension.getNodeName());
|
||||
assertEquals(1, dimension.getLength());
|
||||
|
||||
assertNull(pixelAspectRatio.getNextSibling()); // No more children
|
||||
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) dimension.getFirstChild();
|
||||
assertEquals("PixelAspectRatio", pixelAspectRatio.getNodeName());
|
||||
assertEquals("1.0", pixelAspectRatio.getAttribute("value"));
|
||||
|
||||
assertNull(pixelAspectRatio.getNextSibling()); // No more children
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardDimensionHires() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
public void testStandardDimensionHires() throws IIOException {
|
||||
BMHDChunk bitmapHeader = new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
bitmapHeader.xAspect = 2;
|
||||
bitmapHeader.yAspect = 1;
|
||||
|
||||
CAMGChunk viewPort = new CAMGChunk(4);
|
||||
viewPort.camg = 0x8000;
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, viewPort, Collections.<GenericChunk>emptyList());
|
||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||
.with(bitmapHeader)
|
||||
.with(viewPort);
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
IIOMetadataNode dimension = metadata.getStandardDimensionNode();
|
||||
assertNotNull(dimension);
|
||||
@@ -381,13 +413,19 @@ public class IFFImageMetadataTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardDimensionInterlaced() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
public void testStandardDimensionInterlaced() throws IIOException {
|
||||
BMHDChunk bitmapHeader = new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
bitmapHeader.xAspect = 1;
|
||||
bitmapHeader.yAspect = 2;
|
||||
|
||||
CAMGChunk viewPort = new CAMGChunk(4);
|
||||
viewPort.camg = 0x4;
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, viewPort, Collections.<GenericChunk>emptyList());
|
||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||
.with(bitmapHeader)
|
||||
.with(viewPort);
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
IIOMetadataNode dimension = metadata.getStandardDimensionNode();
|
||||
assertNotNull(dimension);
|
||||
@@ -402,13 +440,14 @@ public class IFFImageMetadataTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardDimensionHiresInterlaced() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
|
||||
public void testStandardDimensionHiresInterlaced() throws IIOException {
|
||||
CAMGChunk viewPort = new CAMGChunk(4);
|
||||
viewPort.camg = 0x8004;
|
||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||
.with(new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0))
|
||||
.with(viewPort);
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, viewPort, Collections.<GenericChunk>emptyList());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
IIOMetadataNode dimension = metadata.getStandardDimensionNode();
|
||||
assertNotNull(dimension);
|
||||
@@ -423,10 +462,11 @@ public class IFFImageMetadataTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardDocument() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
public void testStandardDocument() throws IIOException {
|
||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||
.with(new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, null, Collections.<GenericChunk>emptyList());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
IIOMetadataNode document = metadata.getStandardDocumentNode();
|
||||
assertNotNull(document);
|
||||
@@ -441,13 +481,15 @@ public class IFFImageMetadataTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardText() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
|
||||
public void testStandardText() throws IIOException {
|
||||
int[] chunks = {IFF.CHUNK_ANNO, IFF.CHUNK_UTF8};
|
||||
String[] texts = {"annotation", "äñnótâtïøñ"};
|
||||
List<GenericChunk> meta = Arrays.asList(new GenericChunk(IFF.CHUNK_ANNO, texts[0].getBytes(StandardCharsets.US_ASCII)),
|
||||
new GenericChunk(IFF.CHUNK_UTF8, texts[1].getBytes(StandardCharsets.UTF_8)));
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, null, meta);
|
||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||
.with(new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0))
|
||||
.with(new GenericChunk(chunks[0], texts[0].getBytes(StandardCharsets.US_ASCII)))
|
||||
.with(new GenericChunk(chunks[1], texts[1].getBytes(StandardCharsets.UTF_8)));
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
IIOMetadataNode text = metadata.getStandardTextNode();
|
||||
assertNotNull(text);
|
||||
@@ -457,26 +499,28 @@ public class IFFImageMetadataTest {
|
||||
for (int i = 0; i < texts.length; i++) {
|
||||
IIOMetadataNode textEntry = (IIOMetadataNode) text.item(i);
|
||||
assertEquals("TextEntry", textEntry.getNodeName());
|
||||
assertEquals(IFFUtil.toChunkStr(meta.get(i).chunkId), textEntry.getAttribute("keyword"));
|
||||
assertEquals(IFFUtil.toChunkStr(chunks[i]), textEntry.getAttribute("keyword"));
|
||||
assertEquals(texts[i], textEntry.getAttribute("value"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardTransparencyRGB() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
public void testStandardTransparencyRGB() throws IIOException {
|
||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||
.with(new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, null, Collections.<GenericChunk>emptyList());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
|
||||
assertNull(transparency); // No transparency, just defaults
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardTransparencyRGBA() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 32, BMHDChunk.MASK_HAS_MASK, BMHDChunk.COMPRESSION_BYTE_RUN, 0);
|
||||
public void testStandardTransparencyRGBA() throws IIOException {
|
||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||
.with(new BMHDChunk(300, 200, 32, BMHDChunk.MASK_HAS_MASK, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
||||
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, null, null, Collections.<GenericChunk>emptyList());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
|
||||
assertNotNull(transparency);
|
||||
@@ -491,11 +535,12 @@ public class IFFImageMetadataTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardTransparencyPalette() {
|
||||
BMHDChunk header = new BMHDChunk(300, 200, 1, BMHDChunk.MASK_TRANSPARENT_COLOR, BMHDChunk.COMPRESSION_BYTE_RUN, 1);
|
||||
public void testStandardTransparencyPalette() throws IIOException {
|
||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||
.with(new BMHDChunk(300, 200, 1, BMHDChunk.MASK_TRANSPARENT_COLOR, BMHDChunk.COMPRESSION_BYTE_RUN, 1));
|
||||
|
||||
byte[] bw = {0, (byte) 0xff};
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(IFF.TYPE_ILBM, header, new IndexColorModel(header.bitplanes, bw.length, bw, bw, bw, header.transparentIndex), null, Collections.<GenericChunk>emptyList());
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, new IndexColorModel(header.bitplanes(), bw.length, bw, bw, bw, header.transparentIndex()));
|
||||
|
||||
IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
|
||||
assertNotNull(transparency);
|
||||
@@ -508,4 +553,131 @@ public class IFFImageMetadataTest {
|
||||
|
||||
assertNull(pixelAspectRatio.getNextSibling()); // No more children
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardRGB8() throws IIOException {
|
||||
Form header = Form.ofType(IFF.TYPE_RGB8)
|
||||
.with(new BMHDChunk(300, 200, 25, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
// Chroma
|
||||
IIOMetadataNode chroma = metadata.getStandardChromaNode();
|
||||
assertNotNull(chroma);
|
||||
assertEquals("Chroma", chroma.getNodeName());
|
||||
assertEquals(3, chroma.getLength());
|
||||
|
||||
IIOMetadataNode colorSpaceType = (IIOMetadataNode) chroma.getFirstChild();
|
||||
assertEquals("ColorSpaceType", colorSpaceType.getNodeName());
|
||||
assertEquals("RGB", colorSpaceType.getAttribute("name"));
|
||||
|
||||
IIOMetadataNode numChannels = (IIOMetadataNode) colorSpaceType.getNextSibling();
|
||||
assertEquals("NumChannels", numChannels.getNodeName());
|
||||
assertEquals("4", numChannels.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode blackIsZero = (IIOMetadataNode) numChannels.getNextSibling();
|
||||
assertEquals("BlackIsZero", blackIsZero.getNodeName());
|
||||
assertEquals("TRUE", blackIsZero.getAttribute("value"));
|
||||
|
||||
assertNull(blackIsZero.getNextSibling()); // No more children
|
||||
|
||||
// Data
|
||||
IIOMetadataNode data = metadata.getStandardDataNode();
|
||||
assertNotNull(data);
|
||||
assertEquals("Data", data.getNodeName());
|
||||
assertEquals(3, data.getLength());
|
||||
|
||||
IIOMetadataNode planarConfiguration = (IIOMetadataNode) data.getFirstChild();
|
||||
assertEquals("PlanarConfiguration", planarConfiguration.getNodeName());
|
||||
assertEquals("PixelInterleaved", planarConfiguration.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode sampleFormat = (IIOMetadataNode) planarConfiguration.getNextSibling();
|
||||
assertEquals("SampleFormat", sampleFormat.getNodeName());
|
||||
assertEquals("UnsignedIntegral", sampleFormat.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode bitsPerSample = (IIOMetadataNode) sampleFormat.getNextSibling();
|
||||
assertEquals("BitsPerSample", bitsPerSample.getNodeName());
|
||||
assertEquals("8 8 8 1", bitsPerSample.getAttribute("value"));
|
||||
|
||||
assertNull(bitsPerSample.getNextSibling()); // No more children
|
||||
|
||||
// Transparency
|
||||
IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
|
||||
assertNotNull(transparency);
|
||||
assertEquals("Transparency", transparency.getNodeName());
|
||||
assertEquals(1, transparency.getLength());
|
||||
|
||||
IIOMetadataNode alpha = (IIOMetadataNode) transparency.getFirstChild();
|
||||
assertEquals("Alpha", alpha.getNodeName());
|
||||
assertEquals("nonpremultiplied", alpha.getAttribute("value"));
|
||||
|
||||
assertNull(alpha.getNextSibling()); // No more children
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardDEEP() throws IIOException {
|
||||
DPELChunk dpel = new DPELChunk(20);
|
||||
dpel.typeDepths = new DPELChunk.TypeDepth[4];
|
||||
for (int i = 0; i < dpel.typeDepths.length; i++) {
|
||||
dpel.typeDepths[i] = new DPELChunk.TypeDepth(i == 0 ? 11 : i, 8);
|
||||
}
|
||||
|
||||
Form header = Form.ofType(IFF.TYPE_DEEP)
|
||||
.with(new DGBLChunk(8))
|
||||
.with(dpel);
|
||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
||||
|
||||
// Chroma
|
||||
IIOMetadataNode chroma = metadata.getStandardChromaNode();
|
||||
assertNotNull(chroma);
|
||||
assertEquals("Chroma", chroma.getNodeName());
|
||||
assertEquals(3, chroma.getLength());
|
||||
|
||||
IIOMetadataNode colorSpaceType = (IIOMetadataNode) chroma.getFirstChild();
|
||||
assertEquals("ColorSpaceType", colorSpaceType.getNodeName());
|
||||
assertEquals("RGB", colorSpaceType.getAttribute("name"));
|
||||
|
||||
IIOMetadataNode numChannels = (IIOMetadataNode) colorSpaceType.getNextSibling();
|
||||
assertEquals("NumChannels", numChannels.getNodeName());
|
||||
assertEquals("4", numChannels.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode blackIsZero = (IIOMetadataNode) numChannels.getNextSibling();
|
||||
assertEquals("BlackIsZero", blackIsZero.getNodeName());
|
||||
assertEquals("TRUE", blackIsZero.getAttribute("value"));
|
||||
|
||||
// TODO: BackgroundColor = 0x666666
|
||||
|
||||
assertNull(blackIsZero.getNextSibling()); // No more children
|
||||
|
||||
// Data
|
||||
IIOMetadataNode data = metadata.getStandardDataNode();
|
||||
assertNotNull(data);
|
||||
assertEquals("Data", data.getNodeName());
|
||||
assertEquals(3, data.getLength());
|
||||
|
||||
IIOMetadataNode planarConfiguration = (IIOMetadataNode) data.getFirstChild();
|
||||
assertEquals("PlanarConfiguration", planarConfiguration.getNodeName());
|
||||
assertEquals("PixelInterleaved", planarConfiguration.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode sampleFormat = (IIOMetadataNode) planarConfiguration.getNextSibling();
|
||||
assertEquals("SampleFormat", sampleFormat.getNodeName());
|
||||
assertEquals("UnsignedIntegral", sampleFormat.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode bitsPerSample = (IIOMetadataNode) sampleFormat.getNextSibling();
|
||||
assertEquals("BitsPerSample", bitsPerSample.getNodeName());
|
||||
assertEquals("8 8 8 8", bitsPerSample.getAttribute("value"));
|
||||
|
||||
assertNull(bitsPerSample.getNextSibling()); // No more children
|
||||
|
||||
// Transparency
|
||||
IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
|
||||
assertNotNull(transparency);
|
||||
assertEquals("Transparency", transparency.getNodeName());
|
||||
assertEquals(1, transparency.getLength());
|
||||
|
||||
IIOMetadataNode alpha = (IIOMetadataNode) transparency.getFirstChild();
|
||||
assertEquals("Alpha", alpha.getNodeName());
|
||||
assertEquals("premultiplied", alpha.getAttribute("value"));
|
||||
|
||||
assertNull(alpha.getNextSibling()); // No more children
|
||||
}
|
||||
}
|
||||
+19
-5
@@ -88,7 +88,16 @@ public class IFFImageReaderTest extends ImageReaderAbstractTest<IFFImageReader>
|
||||
// 16 color indexed, multi palette (PCHG) - Ok
|
||||
new TestData(getClassLoaderResource("/iff/Manhattan.PCHG"), new Dimension(704, 440)),
|
||||
// 16 color indexed, multi palette (PCHG + SHAM) - Ok
|
||||
new TestData(getClassLoaderResource("/iff/Somnambulist-2.SHAM"), new Dimension(704, 440))
|
||||
new TestData(getClassLoaderResource("/iff/Somnambulist-2.SHAM"), new Dimension(704, 440)),
|
||||
// Impulse RGB8 format straight from Imagine 2.0
|
||||
new TestData(getClassLoaderResource("/iff/glowsphere2.rgb8"), new Dimension(640, 480)),
|
||||
// Impulse RGB8 format written by ASDG ADPro, with cross boundary runs, which is probably not as per spec...
|
||||
new TestData(getClassLoaderResource("/iff/tunnel04-adpro-cross-boundary-runs.rgb8"), new Dimension(640, 480)),
|
||||
// TVPaint (TecSoft) DEEP format
|
||||
new TestData(getClassLoaderResource("/iff/arch.deep"), new Dimension(800, 600)),
|
||||
// TVPaint Project (TVPP is effectively same as the DEEP format, but multiple layers, background color etc.)
|
||||
// TODO: This file contains one more image/layer, second DBOD chunk @1868908, len: 1199144!
|
||||
new TestData(getClassLoaderResource("/iff/warm-and-bright.pro"), new Dimension(800, 600))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -99,7 +108,7 @@ public class IFFImageReaderTest extends ImageReaderAbstractTest<IFFImageReader>
|
||||
|
||||
@Override
|
||||
protected List<String> getSuffixes() {
|
||||
return Arrays.asList("iff", "ilbm", "ham", "ham8", "lbm");
|
||||
return Arrays.asList("iff", "ilbm", "ham", "ham8", "lbm", "rgb8", "deep");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -134,9 +143,14 @@ public class IFFImageReaderTest extends ImageReaderAbstractTest<IFFImageReader>
|
||||
|
||||
for (int i = 0; i < 32; i++) {
|
||||
// Make sure the color model is really EHB
|
||||
assertEquals("red", (reds[i] & 0xff) / 2, reds[i + 32] & 0xff);
|
||||
assertEquals("blue", (blues[i] & 0xff) / 2, blues[i + 32] & 0xff);
|
||||
assertEquals("green", (greens[i] & 0xff) / 2, greens[i + 32] & 0xff);
|
||||
try {
|
||||
assertEquals("red", (reds[i] & 0xff) / 2, reds[i + 32] & 0xff);
|
||||
assertEquals("blue", (blues[i] & 0xff) / 2, blues[i + 32] & 0xff);
|
||||
assertEquals("green", (greens[i] & 0xff) / 2, greens[i + 32] & 0xff);
|
||||
}
|
||||
catch (AssertionError err) {
|
||||
throw new AssertionError("Color " + i + " " + err.getMessage(), err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user