mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2026-05-19 00:00:03 -04:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4b2ebec429 | |||
| 0498a1a095 | |||
| 4fd20f7fa0 | |||
| c9bd68623a | |||
| 7ff74465d3 | |||
| 1f5bf80f7e | |||
| 5c35c6e997 | |||
| 7692eeef5e | |||
| cdadbf7051 | |||
| 7c2423bc94 | |||
| f9837d4540 | |||
| 7728a912b1 | |||
| 923598636e | |||
| 6ab0e64107 | |||
| f69f706f60 | |||
| 53dd493cfa | |||
| 607502fbbd |
@@ -7,14 +7,14 @@ 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.
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem or use case is.
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've already considered, and why they won't work.
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here, like links to specifications or sample files.
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
**What is fixed** Add link to the issue this PR fixes.
|
||||
**What is fixed**
|
||||
|
||||
Example: Fixes #42.
|
||||
Add link to the issue this PR fixes.
|
||||
|
||||
**Why is this change proposed** If this change does *not* fix an open issue, briefly describe the rationale for this PR.
|
||||
Fixes #42.
|
||||
|
||||
**What is changed** Briefly describe the changes proposed in this pull request:
|
||||
**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
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
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
@@ -0,0 +1,14 @@
|
||||
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,52 +1,54 @@
|
||||
[](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://travis-ci.org/haraldk/TwelveMonkeys)
|
||||
[](https://maven-badges.herokuapp.com/maven-central/com.twelvemonkeys.imageio/imageio)
|
||||
[](https://stackoverflow.com/questions/tagged/twelvemonkeys)
|
||||
[](https://paypal.me/haraldk76/100)
|
||||
|
||||
## About
|
||||
|
||||
TwelveMonkeys ImageIO provides extended image file format support for the Java platform, through plugins for the `javax.imageio.*` package.
|
||||
TwelveMonkeys ImageIO is a collection of plugins and extensions for Java's ImageIO.
|
||||
|
||||
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
|
||||
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
|
||||
"in the wild", as well as to maintain access to data in legacy formats.
|
||||
As there is lots of legacy data out there, we see the need for open implementations of readers for popular 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.
|
||||
|
||||
----
|
||||
|
||||
## File formats supported
|
||||
|
||||
| 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) |
|
||||
| Plugin | Format | Description | Read | Write | Metadata | Notes |
|
||||
| ------ | -------- | ----------- |:----:|:-----:| -------- | ----- |
|
||||
| Batik | **SVG** | Scalable Vector Graphics | âś” | - | - | Requires [Batik](https://xmlgraphics.apache.org/batik/)
|
||||
| | WMF | MS Windows Metafile | âś” | - | - | Requires [Batik](https://xmlgraphics.apache.org/batik/)
|
||||
| [BMP](https://github.com/haraldk/TwelveMonkeys/wiki/BMP-Plugin) | **BMP** | MS Windows and IBM OS/2 Device Independent Bitmap | âś” | âś” | [Native](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/bmp_metadata.html) & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| | CUR | MS Windows Cursor Format | âś” | - | - |
|
||||
| | ICO | MS Windows Icon Format | âś” | âś” | - |
|
||||
| [HDR](https://github.com/haraldk/TwelveMonkeys/wiki/HDR-Plugin) | HDR | Radiance High Dynamic Range RGBE Format | âś” | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| [ICNS](https://github.com/haraldk/TwelveMonkeys/wiki/ICNS-Plugin) | ICNS | Apple Icon Image | âś” | âś” | - |
|
||||
| [IFF](https://github.com/haraldk/TwelveMonkeys/wiki/IFF-Plugin) | IFF | Commodore Amiga/Electronic Arts Interchange File Format | âś” | âś” | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| [JPEG](https://github.com/haraldk/TwelveMonkeys/wiki/JPEG-Plugin) | **JPEG** | Joint Photographers Expert Group | âś” | âś” | [Native](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/jpeg_metadata.html#image) & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| | JPEG Lossless | | âś” | - | [Native](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/jpeg_metadata.html#image) & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| [PCX](https://github.com/haraldk/TwelveMonkeys/wiki/PCX-Plugin) | PCX | ZSoft Paintbrush Format | âś” | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| | DCX | Multi-page PCX fax document | âś” | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| [PICT](https://github.com/haraldk/TwelveMonkeys/wiki/PICT-Plugin) | PICT | Apple QuickTime Picture Format | âś” | âś” | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| | PNTG | Apple MacPaint Picture Format | âś” | | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| [PNM](https://github.com/haraldk/TwelveMonkeys/wiki/PNM-Plugin) | PAM | NetPBM Portable Any Map | âś” | âś” | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| | PBM | NetPBM Portable Bit Map | âś” | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| | PGM | NetPBM Portable Grey Map | âś” | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| | PPM | NetPBM Portable Pix Map | âś” | âś” | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| | PFM | Portable Float Map | âś” | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| [PSD](https://github.com/haraldk/TwelveMonkeys/wiki/PSD-Plugin) | **PSD** | Adobe Photoshop Document | âś” | - | Native & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| | PSB | Adobe Photoshop Large Document | âś” | - | Native & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| [SGI](https://github.com/haraldk/TwelveMonkeys/wiki/SGI-Plugin) | SGI | Silicon Graphics Image Format | âś” | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| [TGA](https://github.com/haraldk/TwelveMonkeys/wiki/TGA-Plugin) | TGA | Truevision TGA Image Format | âś” | âś” | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
|ThumbsDB| Thumbs.db| MS Windows Thumbs DB | âś” | - | - | OLE2 Compound Document based format only
|
||||
| [TIFF](https://github.com/haraldk/TwelveMonkeys/wiki/TIFF-Plugin) | **TIFF** | Aldus/Adobe Tagged Image File Format | âś” | âś” | [Native](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/tiff_metadata.html#ImageMetadata) & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| | BigTIFF | | âś” | - | [Native](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/tiff_metadata.html#ImageMetadata) & [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| [WebP](https://github.com/haraldk/TwelveMonkeys/wiki/WebP-Plugin) | **WebP** | Google WebP Format | âś” | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
| XWD | XWD | X11 Window Dump Format | âś” | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
||||
|
||||
|
||||
**Important note on using Batik:** *Please read [The Apache™ XML Graphics Project - Security](https://xmlgraphics.apache.org/security.html),
|
||||
@@ -272,12 +274,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.8.1</version>
|
||||
<version>3.7.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-tiff</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<version>3.7.0</version>
|
||||
</dependency>
|
||||
|
||||
<!--
|
||||
@@ -287,17 +289,7 @@ 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.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>
|
||||
<version>3.7.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
```
|
||||
@@ -306,13 +298,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.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
|
||||
twelvemonkeys-common-lang-3.7.0.jar
|
||||
twelvemonkeys-common-io-3.7.0.jar
|
||||
twelvemonkeys-common-image-3.7.0.jar
|
||||
twelvemonkeys-imageio-core-3.7.0.jar
|
||||
twelvemonkeys-imageio-metadata-3.7.0.jar
|
||||
twelvemonkeys-imageio-jpeg-3.7.0.jar
|
||||
twelvemonkeys-imageio-tiff-3.7.0.jar
|
||||
|
||||
#### Deploying the plugins in a web app
|
||||
|
||||
@@ -378,44 +370,44 @@ Other "fat" JAR bundlers will probably have similar mechanisms to merge entries
|
||||
|
||||
### Links to prebuilt binaries
|
||||
|
||||
##### Latest version (3.8.1)
|
||||
##### Latest version (3.7.0)
|
||||
|
||||
Requires Java 7 or later.
|
||||
|
||||
Common dependencies
|
||||
* [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)
|
||||
* [common-lang-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-lang/3.7.0/common-lang-3.7.0.jar)
|
||||
* [common-io-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-io/3.7.0/common-io-3.7.0.jar)
|
||||
* [common-image-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-image/3.7.0/common-image-3.7.0.jar)
|
||||
|
||||
ImageIO dependencies
|
||||
* [imageio-core-3.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-core-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-core/3.7.0/imageio-core-3.7.0.jar)
|
||||
* [imageio-metadata-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-metadata/3.7.0/imageio-metadata-3.7.0.jar)
|
||||
|
||||
ImageIO plugins
|
||||
* [imageio-bmp-3.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-bmp-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-bmp/3.7.0/imageio-bmp-3.7.0.jar)
|
||||
* [imageio-hdr-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-hdr/3.7.0/imageio-hdr-3.7.0.jar)
|
||||
* [imageio-icns-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-icns/3.7.0/imageio-icns-3.7.0.jar)
|
||||
* [imageio-iff-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-iff/3.7.0/imageio-iff-3.7.0.jar)
|
||||
* [imageio-jpeg-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jpeg/3.7.0/imageio-jpeg-3.7.0.jar)
|
||||
* [imageio-pcx-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pcx/3.7.0/imageio-pcx-3.7.0.jar)
|
||||
* [imageio-pict-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pict/3.7.0/imageio-pict-3.7.0.jar)
|
||||
* [imageio-pnm-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pnm/3.7.0/imageio-pnm-3.7.0.jar)
|
||||
* [imageio-psd-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-psd/3.7.0/imageio-psd-3.7.0.jar)
|
||||
* [imageio-sgi-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-sgi/3.7.0/imageio-sgi-3.7.0.jar)
|
||||
* [imageio-tga-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tga/3.7.0/imageio-tga-3.7.0.jar)
|
||||
* [imageio-thumbsdb-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-thumbsdb/3.7.0/imageio-thumbsdb-3.7.0.jar)
|
||||
* [imageio-tiff-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tiff/3.7.0/imageio-tiff-3.7.0.jar)
|
||||
* [imageio-webp-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-webp/3.7.0/imageio-webp-3.7.0.jar)
|
||||
* [imageio-xwd-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-xwd/3.7.0/imageio-xwd-3.7.0.jar)
|
||||
|
||||
ImageIO plugins requiring 3rd party libs
|
||||
* [imageio-batik-3.8.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-batik/3.8.1/imageio-batik-3.8.1.jar)
|
||||
* [imageio-batik-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-batik/3.7.0/imageio-batik-3.7.0.jar)
|
||||
|
||||
Photoshop Path support for ImageIO
|
||||
* [imageio-clippath-3.8.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-clippath/3.8.1/imageio-clippath-3.8.1.jar)
|
||||
* [imageio-clippath-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-clippath/3.7.0/imageio-clippath-3.7.0.jar)
|
||||
|
||||
Servlet support
|
||||
* [servlet-3.8.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/servlet/servlet/3.8.1/servlet-3.8.1.jar)
|
||||
* [servlet-3.7.0.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/servlet/servlet/3.7.0/servlet-3.7.0.jar)
|
||||
|
||||
##### Old version (3.0.x)
|
||||
|
||||
|
||||
+16
-6
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys</groupId>
|
||||
<artifactId>twelvemonkeys</artifactId>
|
||||
<version>3.8.3</version>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>com.twelvemonkeys.bom</groupId>
|
||||
@@ -58,6 +58,16 @@
|
||||
<artifactId>imageio-bmp</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-cr2</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-dng</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-hdr</artifactId>
|
||||
@@ -78,6 +88,11 @@
|
||||
<artifactId>imageio-jpeg</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-nef</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-pcx</artifactId>
|
||||
@@ -123,11 +138,6 @@
|
||||
<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.8.3</version>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>common-image</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
@@ -31,7 +31,7 @@
|
||||
<dependency>
|
||||
<groupId>jmagick</groupId>
|
||||
<artifactId>jmagick</artifactId>
|
||||
<version>6.6.9</version>
|
||||
<version>6.2.4</version>
|
||||
<optional>true</optional>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -34,13 +34,7 @@ import java.awt.*;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
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;
|
||||
import java.awt.image.*;
|
||||
|
||||
/**
|
||||
* This is a drop-in replacement for {@link java.awt.image.AffineTransformOp}.
|
||||
@@ -76,7 +70,6 @@ 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 {
|
||||
@@ -87,9 +80,10 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp {
|
||||
dst = createCompatibleDestImage(src, src.getColorModel());
|
||||
}
|
||||
|
||||
Graphics2D g2d = dst.createGraphics();
|
||||
Graphics2D g2d = null;
|
||||
|
||||
try {
|
||||
g2d = dst.createGraphics();
|
||||
int interpolationType = delegate.getInterpolationType();
|
||||
|
||||
if (interpolationType > 0) {
|
||||
@@ -115,7 +109,9 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp {
|
||||
return dst;
|
||||
}
|
||||
finally {
|
||||
g2d.dispose();
|
||||
if (g2d != null) {
|
||||
g2d.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,8 +45,8 @@ import java.awt.image.BufferedImage;
|
||||
*/
|
||||
public class BufferedImageIcon implements Icon {
|
||||
private final BufferedImage image;
|
||||
private final int width;
|
||||
private final int height;
|
||||
private int width;
|
||||
private int height;
|
||||
private final boolean fast;
|
||||
|
||||
public BufferedImageIcon(BufferedImage pImage) {
|
||||
@@ -81,10 +81,11 @@ public class BufferedImageIcon implements Icon {
|
||||
else {
|
||||
//System.out.println("Scaling using interpolation");
|
||||
Graphics2D g2 = (Graphics2D) g;
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -587,7 +587,6 @@ 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);
|
||||
}
|
||||
|
||||
+16
-22
@@ -30,26 +30,17 @@
|
||||
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
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 org.junit.Test;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Point2D;
|
||||
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.awt.image.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* AffineTransformOpTest.
|
||||
@@ -110,7 +101,6 @@ 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() {
|
||||
@@ -138,8 +128,8 @@ public class AffineTransformOpTest {
|
||||
|
||||
@Test
|
||||
public void testFilterRotateBIStandard() {
|
||||
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);
|
||||
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);
|
||||
|
||||
for (Integer type : TYPES) {
|
||||
BufferedImage image = new BufferedImage(width, height, type);
|
||||
@@ -157,8 +147,8 @@ public class AffineTransformOpTest {
|
||||
|
||||
@Test
|
||||
public void testFilterRotateBICustom() {
|
||||
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);
|
||||
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);
|
||||
|
||||
for (ImageTypeSpecifier spec : SPECS) {
|
||||
BufferedImage image = spec.createBufferedImage(width, height);
|
||||
@@ -207,8 +197,8 @@ public class AffineTransformOpTest {
|
||||
|
||||
@Test
|
||||
public void testFilterRotateRasterStandard() {
|
||||
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);
|
||||
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);
|
||||
|
||||
for (Integer type : TYPES) {
|
||||
Raster raster = new BufferedImage(width, height, type).getRaster();
|
||||
@@ -231,6 +221,8 @@ public class AffineTransformOpTest {
|
||||
fail("No result!");
|
||||
}
|
||||
else {
|
||||
System.err.println("AffineTransformOpTest.testFilterRotateRasterStandard");
|
||||
System.err.println("type: " + type);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -248,8 +240,8 @@ public class AffineTransformOpTest {
|
||||
|
||||
@Test
|
||||
public void testFilterRotateRasterCustom() {
|
||||
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);
|
||||
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);
|
||||
|
||||
for (ImageTypeSpecifier spec : SPECS) {
|
||||
Raster raster = spec.createBufferedImage(width, height).getRaster();
|
||||
@@ -272,6 +264,8 @@ 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.8.3</version>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>common-io</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
@@ -34,7 +34,6 @@ 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
|
||||
@@ -43,8 +42,11 @@ import java.util.Arrays;
|
||||
* @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.
|
||||
@@ -95,8 +97,10 @@ public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
|
||||
|
||||
private void growIfNeeded(int pNewCount) {
|
||||
if (pNewCount > buf.length) {
|
||||
int newSize = Math.max(buf.length << 1, pNewCount);
|
||||
buf = Arrays.copyOf(buf, newSize);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,7 +113,10 @@ public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
|
||||
// Non-synchronized version of toByteArray
|
||||
@Override
|
||||
public byte[] toByteArray() {
|
||||
return Arrays.copyOf(buf, count);
|
||||
byte[] newBuf = new byte[count];
|
||||
System.arraycopy(buf, 0, newBuf, 0, count);
|
||||
|
||||
return newBuf;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -65,7 +65,6 @@ import java.io.FilenameFilter;
|
||||
* @see WildcardStringParser
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated
|
||||
public class FilenameMaskFilter implements FilenameFilter {
|
||||
|
||||
// TODO: Rewrite to use regexp, or create new class
|
||||
|
||||
@@ -442,7 +442,6 @@ 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 {
|
||||
private final ByteBuffer buffer;
|
||||
private final Decoder decoder;
|
||||
protected final ByteBuffer buffer;
|
||||
protected final Decoder decoder;
|
||||
|
||||
/**
|
||||
* Creates a new decoder stream and chains it to the
|
||||
* input stream specified by the {@code stream} argument.
|
||||
* input stream specified by the {@code pStream} argument.
|
||||
* The stream will use a default decode buffer size.
|
||||
*
|
||||
* @param stream the underlying input stream.
|
||||
* @param decoder the decoder that will be used to decode the underlying stream
|
||||
* @param pStream the underlying input stream.
|
||||
* @param pDecoder the decoder that will be used to decode the underlying stream
|
||||
*
|
||||
* @see java.io.FilterInputStream#in
|
||||
*/
|
||||
public DecoderStream(final InputStream stream, final Decoder decoder) {
|
||||
public DecoderStream(final InputStream pStream, final Decoder pDecoder) {
|
||||
// TODO: Let the decoder decide preferred buffer size
|
||||
this(stream, decoder, 1024);
|
||||
this(pStream, pDecoder, 1024);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new decoder stream and chains it to the
|
||||
* input stream specified by the {@code stream} argument.
|
||||
* input stream specified by the {@code pStream} argument.
|
||||
*
|
||||
* @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
|
||||
* @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
|
||||
*
|
||||
* @see java.io.FilterInputStream#in
|
||||
*/
|
||||
public DecoderStream(final InputStream stream, final Decoder decoder, final int bufferSize) {
|
||||
super(stream);
|
||||
public DecoderStream(final InputStream pStream, final Decoder pDecoder, final int pBufferSize) {
|
||||
super(pStream);
|
||||
|
||||
this.decoder = decoder;
|
||||
buffer = ByteBuffer.allocate(bufferSize);
|
||||
decoder = pDecoder;
|
||||
buffer = ByteBuffer.allocate(pBufferSize);
|
||||
buffer.flip();
|
||||
}
|
||||
|
||||
@@ -95,15 +95,15 @@ public final class DecoderStream extends FilterInputStream {
|
||||
return buffer.get() & 0xff;
|
||||
}
|
||||
|
||||
public int read(final byte[] bytes, final int offset, final int length) throws IOException {
|
||||
if (bytes == null) {
|
||||
public int read(final byte pBytes[], final int pOffset, final int pLength) throws IOException {
|
||||
if (pBytes == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
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 ((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 (length == 0) {
|
||||
else if (pLength == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -114,11 +114,11 @@ public final class DecoderStream extends FilterInputStream {
|
||||
}
|
||||
}
|
||||
|
||||
// Read until we have read length bytes, or have reached EOF
|
||||
// Read until we have read pLength bytes, or have reached EOF
|
||||
int count = 0;
|
||||
int off = offset;
|
||||
int off = pOffset;
|
||||
|
||||
while (length > count) {
|
||||
while (pLength > 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(length - count, buffer.remaining());
|
||||
buffer.get(bytes, off, dstLen);
|
||||
int dstLen = Math.min(pLength - count, buffer.remaining());
|
||||
buffer.get(pBytes, off, dstLen);
|
||||
|
||||
// Update offset (rest)
|
||||
off += dstLen;
|
||||
@@ -139,7 +139,7 @@ public final class DecoderStream extends FilterInputStream {
|
||||
return count;
|
||||
}
|
||||
|
||||
public long skip(final long length) throws IOException {
|
||||
public long skip(final long pLength) 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 length bytes, or have reached EOF
|
||||
// Skip until we have skipped pLength bytes, or have reached EOF
|
||||
long total = 0;
|
||||
|
||||
while (total < length) {
|
||||
while (total < pLength) {
|
||||
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(length - total, buffer.remaining());
|
||||
int skipped = (int) Math.min(pLength - 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
|
||||
*/
|
||||
private int fill() throws IOException {
|
||||
protected int fill() throws IOException {
|
||||
buffer.clear();
|
||||
int read = decoder.decode(in, buffer);
|
||||
|
||||
|
||||
@@ -45,39 +45,41 @@ 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!!!
|
||||
|
||||
private final Encoder encoder;
|
||||
protected final Encoder encoder;
|
||||
private final boolean flushOnWrite;
|
||||
|
||||
private final ByteBuffer buffer;
|
||||
protected final ByteBuffer buffer;
|
||||
|
||||
/**
|
||||
* Creates an output stream filter built on top of the specified
|
||||
* underlying output stream.
|
||||
*
|
||||
* @param stream the underlying output stream
|
||||
* @param encoder the encoder to use
|
||||
* @param pStream the underlying output stream
|
||||
* @param pEncoder the encoder to use
|
||||
*/
|
||||
public EncoderStream(final OutputStream stream, final Encoder encoder) {
|
||||
this(stream, encoder, false);
|
||||
public EncoderStream(final OutputStream pStream, final Encoder pEncoder) {
|
||||
this(pStream, pEncoder, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an output stream filter built on top of the specified
|
||||
* underlying output stream.
|
||||
*
|
||||
* @param stream the underlying output stream
|
||||
* @param encoder the encoder to use
|
||||
* @param flushOnWrite if {@code true}, calls to the byte-array
|
||||
* @param pStream the underlying output stream
|
||||
* @param pEncoder the encoder to use
|
||||
* @param pFlushOnWrite if {@code true}, calls to the byte-array
|
||||
* {@code write} methods will automatically flush the buffer.
|
||||
*/
|
||||
public EncoderStream(final OutputStream stream, final Encoder encoder, final boolean flushOnWrite) {
|
||||
super(stream);
|
||||
public EncoderStream(final OutputStream pStream, final Encoder pEncoder, final boolean pFlushOnWrite) {
|
||||
super(pStream);
|
||||
|
||||
this.encoder = encoder;
|
||||
this.flushOnWrite = flushOnWrite;
|
||||
encoder = pEncoder;
|
||||
flushOnWrite = pFlushOnWrite;
|
||||
|
||||
buffer = ByteBuffer.allocate(1024);
|
||||
buffer.flip();
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
@@ -102,33 +104,33 @@ public final class EncoderStream extends FilterOutputStream {
|
||||
}
|
||||
}
|
||||
|
||||
public void write(final byte[] bytes) throws IOException {
|
||||
write(bytes, 0, bytes.length);
|
||||
public final void write(final byte[] pBytes) throws IOException {
|
||||
write(pBytes, 0, pBytes.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[] values, final int offset, final int length) throws IOException {
|
||||
if (!flushOnWrite && length < buffer.remaining()) {
|
||||
// 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()) {
|
||||
// Buffer data
|
||||
buffer.put(values, offset, length);
|
||||
buffer.put(pBytes, pOffset, pLength);
|
||||
}
|
||||
else {
|
||||
// Encode data already in the buffer
|
||||
encodeBuffer();
|
||||
|
||||
// Encode rest without buffering
|
||||
encoder.encode(out, ByteBuffer.wrap(values, offset, length));
|
||||
encoder.encode(out, ByteBuffer.wrap(pBytes, pOffset, pLength));
|
||||
}
|
||||
}
|
||||
|
||||
public void write(final int value) throws IOException {
|
||||
public void write(final int pByte) throws IOException {
|
||||
if (!buffer.hasRemaining()) {
|
||||
encodeBuffer(); // Resets bufferPos to 0
|
||||
}
|
||||
|
||||
buffer.put((byte) value);
|
||||
buffer.put((byte) pByte);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,9 +29,6 @@
|
||||
|
||||
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;
|
||||
@@ -41,6 +38,9 @@ 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,6 +88,17 @@ 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.
|
||||
@@ -158,7 +169,13 @@ public final class DOMSerializer {
|
||||
try {
|
||||
return DOMImplementationRegistry.newInstance();
|
||||
}
|
||||
catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
|
||||
catch (ClassNotFoundException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
catch (InstantiationException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
catch (IllegalAccessException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,23 +30,16 @@
|
||||
|
||||
package com.twelvemonkeys.xml;
|
||||
|
||||
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 com.twelvemonkeys.lang.StringUtil;
|
||||
import org.w3c.dom.*;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
import org.w3c.dom.*;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import com.twelvemonkeys.lang.StringUtil;
|
||||
import java.io.*;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* XMLSerializer
|
||||
@@ -297,7 +290,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, pStart, pEnd);
|
||||
pBuilder.append(pString.substring(pStart, pEnd));
|
||||
pBuilder.append(pEntity);
|
||||
return pEnd + 1;
|
||||
}
|
||||
@@ -534,7 +527,8 @@ public class XMLSerializer {
|
||||
builder = factory.newDocumentBuilder();
|
||||
}
|
||||
catch (ParserConfigurationException e) {
|
||||
throw new IOException(e);
|
||||
//noinspection ThrowableInstanceNeverThrown BOGUS
|
||||
throw (IOException) new IOException(e.getMessage()).initCause(e);
|
||||
}
|
||||
|
||||
DOMImplementation dom = builder.getDOMImplementation();
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* 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.8.3</version>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>common-lang</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
@@ -770,7 +770,6 @@ public final class StringUtil {
|
||||
*/
|
||||
|
||||
/*public*/
|
||||
@Deprecated
|
||||
static String formatNumber(long pNum, int pLen) throws IllegalArgumentException {
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
@@ -1465,7 +1464,6 @@ 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,7 +157,6 @@ public class Time {
|
||||
* @see #parseTime(String)
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated
|
||||
public String toString(String pFormatStr) {
|
||||
TimeFormat tf = new TimeFormat(pFormatStr);
|
||||
|
||||
@@ -175,7 +174,6 @@ public class Time {
|
||||
* @see #toString(String)
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated
|
||||
public static Time parseTime(String pStr) {
|
||||
TimeFormat tf = TimeFormat.getInstance();
|
||||
|
||||
|
||||
-1
@@ -111,7 +111,6 @@ 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.8.3</version>
|
||||
<version>3.8.0-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.8.3</version>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>com.twelvemonkeys.contrib</groupId>
|
||||
<artifactId>contrib</artifactId>
|
||||
|
||||
@@ -30,14 +30,9 @@ 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, or
|
||||
* {@code null}.
|
||||
* @return an {@code IIOImage} containing the correctly oriented image and metadata including rotation info.
|
||||
* @throws IOException if an error occurs during reading.
|
||||
*/
|
||||
public static IIOImage readWithOrientation(final URL input) throws IOException {
|
||||
@@ -48,14 +43,9 @@ 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, or
|
||||
* {@code null}.
|
||||
* @return an {@code IIOImage} containing the correctly oriented image and metadata including rotation info.
|
||||
* @throws IOException if an error occurs during reading.
|
||||
*/
|
||||
public static IIOImage readWithOrientation(final InputStream input) throws IOException {
|
||||
@@ -66,14 +56,9 @@ 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 or
|
||||
* {@code null}.
|
||||
* @return an {@code IIOImage} containing the correctly oriented image and metadata including rotation info.
|
||||
* @throws IOException if an error occurs during reading.
|
||||
*/
|
||||
public static IIOImage readWithOrientation(final File input) throws IOException {
|
||||
@@ -84,14 +69,9 @@ 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, or
|
||||
* {@code null}.
|
||||
* @return an {@code IIOImage} containing the correctly oriented image and metadata including rotation info.
|
||||
* @throws IOException if an error occurs during reading.
|
||||
*/
|
||||
public static IIOImage readWithOrientation(final ImageInputStream input) throws IOException {
|
||||
|
||||
@@ -31,9 +31,8 @@
|
||||
package com.twelvemonkeys.contrib.tiff;
|
||||
|
||||
import com.twelvemonkeys.contrib.tiff.TIFFUtilities.TIFFExtension;
|
||||
import com.twelvemonkeys.imageio.plugins.tiff.TIFFImageMetadataFormat;
|
||||
import com.twelvemonkeys.imageio.plugins.tiff.TIFFMedataFormat;
|
||||
import com.twelvemonkeys.io.FileUtil;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.w3c.dom.Node;
|
||||
@@ -155,7 +154,7 @@ public class TIFFUtilitiesTest {
|
||||
reader.setInput(checkTest1);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Node metaData = reader.getImageMetadata(i)
|
||||
.getAsTree(TIFFImageMetadataFormat.SUN_NATIVE_IMAGE_METADATA_FORMAT_NAME);
|
||||
.getAsTree(TIFFMedataFormat.SUN_NATIVE_IMAGE_METADATA_FORMAT_NAME);
|
||||
short orientation = ((Number) expression.evaluate(metaData, XPathConstants.NUMBER)).shortValue();
|
||||
Assert.assertEquals(orientation, TIFFExtension.ORIENTATION_RIGHTTOP);
|
||||
}
|
||||
@@ -172,7 +171,7 @@ public class TIFFUtilitiesTest {
|
||||
reader.setInput(checkTest2);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Node metaData = reader.getImageMetadata(i)
|
||||
.getAsTree(TIFFImageMetadataFormat.SUN_NATIVE_IMAGE_METADATA_FORMAT_NAME);
|
||||
.getAsTree(TIFFMedataFormat.SUN_NATIVE_IMAGE_METADATA_FORMAT_NAME);
|
||||
short orientation = ((Number) expression.evaluate(metaData, XPathConstants.NUMBER)).shortValue();
|
||||
Assert.assertEquals(orientation, i == 1
|
||||
? TIFFExtension.ORIENTATION_BOTRIGHT
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.8.3</version>
|
||||
<version>3.8.0-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,13 +48,6 @@
|
||||
<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>
|
||||
@@ -75,6 +68,13 @@
|
||||
<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 the specified version
|
||||
making everything end up depending on Batik 1.5, not 1.6
|
||||
-->
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
|
||||
+95
-103
@@ -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,14 +45,11 @@ 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.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.*;
|
||||
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;
|
||||
@@ -62,8 +59,10 @@ import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import java.awt.*;
|
||||
import java.awt.geom.*;
|
||||
import java.awt.image.*;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Dimension2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
@@ -77,13 +76,12 @@ 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",
|
||||
System.getProperty("com.twelvemonkeys.imageio.plugins.svg.allowexternalresources")));
|
||||
"true".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.imageio.plugins.svg.allowexternalresources"));
|
||||
|
||||
private Rasterizer rasterizer;
|
||||
private boolean allowExternalResources = DEFAULT_ALLOW_EXTERNAL_RESOURCES;
|
||||
@@ -134,7 +132,6 @@ 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));
|
||||
}
|
||||
|
||||
@@ -149,23 +146,29 @@ 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 {
|
||||
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();
|
||||
}
|
||||
processImageStarted(pIndex);
|
||||
|
||||
processImageComplete();
|
||||
BufferedImage image = rasterizer.getImage();
|
||||
|
||||
return destination;
|
||||
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;
|
||||
}
|
||||
catch (TranscoderException e) {
|
||||
Throwable cause = unwrapException(e);
|
||||
throw new IIOException(cause.getMessage(), cause);
|
||||
}
|
||||
}
|
||||
|
||||
private static Throwable unwrapException(TranscoderException ex) {
|
||||
@@ -180,11 +183,11 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
|
||||
// Set dimensions
|
||||
Dimension size = pParam.getSourceRenderSize();
|
||||
Rectangle viewBox = rasterizer.getViewBox();
|
||||
Dimension origSize = new Dimension(getWidth(0), getHeight(0));
|
||||
if (size == null) {
|
||||
// SVG is not a pixel based format, but we'll scale it, according to
|
||||
// the subsampling for compatibility
|
||||
size = getSourceRenderSizeFromSubsamping(pParam, viewBox.getSize());
|
||||
size = getSourceRenderSizeFromSubsamping(pParam, origSize);
|
||||
}
|
||||
|
||||
if (size != null) {
|
||||
@@ -204,8 +207,8 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
}
|
||||
else {
|
||||
// Need to resize here...
|
||||
double xScale = size.getWidth() / viewBox.getWidth();
|
||||
double yScale = size.getHeight() / viewBox.getHeight();
|
||||
double xScale = size.getWidth() / origSize.getWidth();
|
||||
double yScale = size.getHeight() / origSize.getHeight();
|
||||
|
||||
hints.put(ImageTranscoder.KEY_WIDTH, (float) (region.getWidth() * xScale));
|
||||
hints.put(ImageTranscoder.KEY_HEIGHT, (float) (region.getHeight() * yScale));
|
||||
@@ -213,7 +216,7 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
}
|
||||
else if (size != null) {
|
||||
// Allow non-uniform scaling
|
||||
hints.put(ImageTranscoder.KEY_AOI, viewBox);
|
||||
hints.put(ImageTranscoder.KEY_AOI, new Rectangle(origSize));
|
||||
}
|
||||
|
||||
// Background color
|
||||
@@ -228,7 +231,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;
|
||||
}
|
||||
@@ -239,16 +242,25 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
|
||||
public int getWidth(int pIndex) throws IOException {
|
||||
checkBounds(pIndex);
|
||||
|
||||
return rasterizer.getDefaultWidth();
|
||||
try {
|
||||
return rasterizer.getDefaultWidth();
|
||||
}
|
||||
catch (TranscoderException e) {
|
||||
throw new IIOException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public int getHeight(int pIndex) throws IOException {
|
||||
checkBounds(pIndex);
|
||||
return rasterizer.getDefaultHeight();
|
||||
try {
|
||||
return rasterizer.getDefaultHeight();
|
||||
}
|
||||
catch (TranscoderException e) {
|
||||
throw new IIOException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) {
|
||||
public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IOException {
|
||||
return Collections.singleton(ImageTypeSpecifier.createFromRenderedImage(rasterizer.createImage(1, 1))).iterator();
|
||||
}
|
||||
|
||||
@@ -259,11 +271,12 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
* and needs major refactoring!
|
||||
* </p>
|
||||
*/
|
||||
private class Rasterizer extends SVGAbstractTranscoder {
|
||||
private class Rasterizer extends SVGAbstractTranscoder /*ImageTranscoder*/ {
|
||||
|
||||
private BufferedImage image;
|
||||
private TranscoderInput transcoderInput;
|
||||
private final Rectangle2D viewBox = new Rectangle2D.Float();
|
||||
private final Dimension defaultSize = new Dimension();
|
||||
private float defaultWidth;
|
||||
private float defaultHeight;
|
||||
private boolean initialized = false;
|
||||
private SVGOMDocument document;
|
||||
private String uri;
|
||||
@@ -276,7 +289,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) {
|
||||
protected void transcode(Document document, final String uri, final TranscoderOutput output) throws TranscoderException {
|
||||
// Sets up root, curTxf & curAoi
|
||||
// ----
|
||||
if (document != null) {
|
||||
@@ -324,66 +337,54 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
// ----
|
||||
SVGSVGElement rootElement = svgDoc.getRootElement();
|
||||
|
||||
// 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);
|
||||
// get the 'width' and 'height' attributes of the SVG document
|
||||
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)) {
|
||||
width = UnitProcessor.svgToUserSpace(widthStr, SVGConstants.SVG_WIDTH_ATTRIBUTE, UnitProcessor.HORIZONTAL_LENGTH, uctx);
|
||||
defaultWidth = UnitProcessor.svgToUserSpace(widthStr, SVGConstants.SVG_WIDTH_ATTRIBUTE, UnitProcessor.HORIZONTAL_LENGTH, uctx);
|
||||
}
|
||||
if (!StringUtil.isEmpty(heightStr)) {
|
||||
height = UnitProcessor.svgToUserSpace(heightStr, SVGConstants.SVG_HEIGHT_ATTRIBUTE, UnitProcessor.VERTICAL_LENGTH, uctx);
|
||||
if(!StringUtil.isEmpty(heightStr)){
|
||||
defaultHeight = UnitProcessor.svgToUserSpace(heightStr, SVGConstants.SVG_HEIGHT_ATTRIBUTE, UnitProcessor.VERTICAL_LENGTH, uctx);
|
||||
}
|
||||
|
||||
boolean hasWidth = width > 0.0;
|
||||
boolean hasHeight = height > 0.0;
|
||||
boolean hasWidth = defaultWidth > 0.0;
|
||||
boolean hasHeight = defaultHeight > 0.0;
|
||||
|
||||
if (!hasWidth || !hasHeight) {
|
||||
if (!viewBox.isEmpty()) {
|
||||
// If one dimension is given, calculate other by aspect ratio in viewBox
|
||||
String viewBoxStr = rootElement.getAttributeNS
|
||||
(null, SVGConstants.SVG_VIEW_BOX_ATTRIBUTE);
|
||||
if (viewBoxStr.length() != 0) {
|
||||
float[] rect = ViewBox.parseViewBoxAttribute(rootElement, viewBoxStr, null);
|
||||
// if one dimension is given, calculate other by aspect ratio in viewBox
|
||||
// or use viewBox if no dimension is given
|
||||
if (hasWidth) {
|
||||
height = width * viewBox.getHeight() / viewBox.getWidth();
|
||||
defaultHeight = defaultWidth * rect[3] / rect[2];
|
||||
}
|
||||
else if (hasHeight) {
|
||||
width = height * viewBox.getWidth() / viewBox.getHeight();
|
||||
defaultWidth = defaultHeight * rect[2] / rect[3];
|
||||
}
|
||||
else {
|
||||
// ...or use viewBox if no dimension is given
|
||||
width = viewBox.getWidth();
|
||||
height = viewBox.getHeight();
|
||||
defaultWidth = rect[2];
|
||||
defaultHeight = rect[3];
|
||||
}
|
||||
}
|
||||
else {
|
||||
// No viewBox, just assume square size
|
||||
if (hasHeight) {
|
||||
width = height;
|
||||
defaultWidth = defaultHeight;
|
||||
}
|
||||
else if (hasWidth) {
|
||||
height = width;
|
||||
defaultHeight = defaultWidth;
|
||||
}
|
||||
else {
|
||||
// ...or finally fall back to Batik default sizes
|
||||
width = 400;
|
||||
height = 400;
|
||||
// fallback to batik default sizes
|
||||
defaultWidth = 400;
|
||||
defaultHeight = 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;
|
||||
@@ -396,7 +397,7 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
ctx = null;
|
||||
}
|
||||
|
||||
private BufferedImage readImage() throws IOException {
|
||||
private BufferedImage readImage() throws TranscoderException {
|
||||
init();
|
||||
|
||||
if (abortRequested()) {
|
||||
@@ -421,8 +422,7 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
}
|
||||
|
||||
if (gvtRoot == null) {
|
||||
Throwable cause = unwrapException(exception);
|
||||
throw new IIOException(cause.getMessage(), cause);
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
ctx = context;
|
||||
@@ -440,7 +440,7 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
|
||||
|
||||
// ----
|
||||
setImageSize(defaultSize.width, defaultSize.height);
|
||||
setImageSize(defaultWidth, defaultHeight);
|
||||
|
||||
if (abortRequested()) {
|
||||
processReadAborted();
|
||||
@@ -454,17 +454,18 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
|
||||
try {
|
||||
Px = ViewBox.getViewTransform(ref, root, width, height, null);
|
||||
|
||||
}
|
||||
catch (BridgeException ex) {
|
||||
throw new IIOException(ex.getMessage(), ex);
|
||||
throw new TranscoderException(ex);
|
||||
}
|
||||
|
||||
if (Px.isIdentity() && (width != defaultSize.width || height != defaultSize.height)) {
|
||||
if (Px.isIdentity() && (width != defaultWidth || height != defaultHeight)) {
|
||||
// The document has no viewBox, we need to resize it by hand.
|
||||
// we want to keep the document size ratio
|
||||
float xscale, yscale;
|
||||
xscale = width / defaultSize.width;
|
||||
yscale = height / defaultSize.height;
|
||||
xscale = width / defaultWidth;
|
||||
yscale = height / defaultHeight;
|
||||
float scale = Math.min(xscale, yscale);
|
||||
Px = AffineTransform.getScaleInstance(scale, scale);
|
||||
}
|
||||
@@ -514,7 +515,7 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
catch (BridgeException ex) {
|
||||
throw new IIOException(ex.getMessage(), ex);
|
||||
throw new TranscoderException(ex);
|
||||
}
|
||||
|
||||
this.root = gvtRoot;
|
||||
@@ -583,7 +584,9 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
return dest;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IIOException(ex.getMessage(), ex);
|
||||
TranscoderException exception = new TranscoderException(ex.getMessage());
|
||||
exception.initCause(ex);
|
||||
throw exception;
|
||||
}
|
||||
finally {
|
||||
if (context != null) {
|
||||
@@ -592,7 +595,7 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void init() throws IIOException {
|
||||
private synchronized void init() throws TranscoderException {
|
||||
if (!initialized) {
|
||||
if (transcoderInput == null) {
|
||||
throw new IllegalStateException("input == null");
|
||||
@@ -600,17 +603,11 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
|
||||
initialized = true;
|
||||
|
||||
try {
|
||||
super.transcode(transcoderInput, null);
|
||||
}
|
||||
catch (TranscoderException e) {
|
||||
Throwable cause = unwrapException(e);
|
||||
throw new IIOException(cause.getMessage(), cause);
|
||||
}
|
||||
super.transcode(transcoderInput, null);
|
||||
}
|
||||
}
|
||||
|
||||
private BufferedImage getImage() throws IOException {
|
||||
private BufferedImage getImage() throws TranscoderException {
|
||||
if (image == null) {
|
||||
image = readImage();
|
||||
}
|
||||
@@ -618,19 +615,14 @@ public class SVGImageReader extends ImageReaderBase {
|
||||
return image;
|
||||
}
|
||||
|
||||
int getDefaultWidth() throws IOException {
|
||||
int getDefaultWidth() throws TranscoderException {
|
||||
init();
|
||||
return defaultSize.width;
|
||||
return (int) Math.ceil(defaultWidth);
|
||||
}
|
||||
|
||||
int getDefaultHeight() throws IOException {
|
||||
int getDefaultHeight() throws TranscoderException {
|
||||
init();
|
||||
return defaultSize.height;
|
||||
}
|
||||
|
||||
Rectangle getViewBox() throws IOException {
|
||||
init();
|
||||
return viewBox.getBounds();
|
||||
return (int) Math.ceil(defaultHeight);
|
||||
}
|
||||
|
||||
void setInput(final TranscoderInput pInput) {
|
||||
|
||||
+141
-143
@@ -1,143 +1,141 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+9
-74
@@ -43,7 +43,8 @@ import javax.imageio.event.IIOReadWarningListener;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.image.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ImagingOpException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URISyntaxException;
|
||||
@@ -52,10 +53,9 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
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);
|
||||
assertRGBEquals("Expected all red", 0xFF0000, imageRed.getRGB(50, 50) & 0xFFFFFF, 0);
|
||||
assertEquals(0xFF0000, imageRed.getRGB(50, 50) & 0xFFFFFF);
|
||||
|
||||
TestData blueSquare = new TestData(getClassLoaderResource("/svg/blue-square.svg"), dim);
|
||||
reader.setInput(blueSquare.getInputStream());
|
||||
BufferedImage imageBlue = reader.read(0, param);
|
||||
assertRGBEquals("Expected all blue", 0x0000FF, imageBlue.getRGB(50, 50) & 0xFFFFFF, 0);
|
||||
assertEquals(0x0000FF, imageBlue.getRGB(50, 50) & 0xFFFFFF);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -225,7 +225,7 @@ public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader>
|
||||
assertEquals(500, image.getHeight());
|
||||
|
||||
// CSS and embedded resources all go!
|
||||
verifyNoInteractions(listener);
|
||||
verifyZeroInteractions(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
|
||||
verifyNoInteractions(listener);
|
||||
verifyZeroInteractions(listener);
|
||||
}
|
||||
finally {
|
||||
reader.dispose();
|
||||
@@ -339,69 +339,4 @@ 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
<?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>
|
||||
|
Before Width: | Height: | Size: 436 B |
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.8.3</version>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-bmp</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: BMP plugin</name>
|
||||
|
||||
+49
-45
@@ -30,14 +30,15 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.bmp;
|
||||
|
||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||
import com.twelvemonkeys.imageio.stream.SubImageInputStream;
|
||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
||||
import com.twelvemonkeys.imageio.util.ProgressListenerBase;
|
||||
import com.twelvemonkeys.io.LittleEndianDataInputStream;
|
||||
import com.twelvemonkeys.io.enc.DecoderStream;
|
||||
import com.twelvemonkeys.xml.XMLSerializer;
|
||||
import java.awt.*;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.*;
|
||||
import java.io.DataInput;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.ImageIO;
|
||||
@@ -50,15 +51,15 @@ import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.metadata.IIOMetadataFormatImpl;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.color.*;
|
||||
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;
|
||||
|
||||
/**
|
||||
* ImageReader for Microsoft Windows Bitmap (BMP) format.
|
||||
@@ -81,7 +82,7 @@ public final class BMPImageReader extends ImageReaderBase {
|
||||
super(new BMPImageReaderSpi());
|
||||
}
|
||||
|
||||
BMPImageReader(final ImageReaderSpi pProvider) {
|
||||
protected BMPImageReader(final ImageReaderSpi pProvider) {
|
||||
super(pProvider);
|
||||
}
|
||||
|
||||
@@ -362,18 +363,14 @@ public final class BMPImageReader extends ImageReaderBase {
|
||||
|
||||
processImageStarted(imageIndex);
|
||||
for (int y = 0; y < height; y++) {
|
||||
int bitCount = header.getBitCount();
|
||||
switch (bitCount) {
|
||||
switch (header.getBitCount()) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 4:
|
||||
case 8:
|
||||
case 24:
|
||||
byte[] rowDataByte = ((DataBufferByte) rowRaster.getDataBuffer()).getData();
|
||||
int bitsPerSample = bitCount == 24 ? 8 : bitCount;
|
||||
int samplesPerPixel = bitCount == 24 ? 3 : 1;
|
||||
|
||||
readRowByte(input, height, srcRegion, xSub, ySub, bitsPerSample, samplesPerPixel, rowDataByte, destRaster, clippedRow, y);
|
||||
readRowByte(input, height, srcRegion, xSub, ySub, rowDataByte, destRaster, clippedRow, y);
|
||||
break;
|
||||
|
||||
case 16:
|
||||
@@ -387,7 +384,7 @@ public final class BMPImageReader extends ImageReaderBase {
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new AssertionError("Unsupported pixel depth: " + bitCount);
|
||||
throw new AssertionError("Unsupported pixel depth: " + header.getBitCount());
|
||||
}
|
||||
|
||||
processImageProgress(100f * y / height);
|
||||
@@ -484,14 +481,9 @@ 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 (srcY % ySub != 0 || srcY < srcRegion.y || srcY >= srcRegion.y + srcRegion.height) {
|
||||
if (y % ySub != 0 || height - 1 - y < srcRegion.y || height - 1 - y >= srcRegion.y + srcRegion.height) {
|
||||
input.skipBytes(rowDataByte.length);
|
||||
|
||||
return;
|
||||
@@ -501,20 +493,24 @@ public final class BMPImageReader extends ImageReaderBase {
|
||||
|
||||
// Subsample horizontal
|
||||
if (xSub != 1) {
|
||||
IIOUtil.subsampleRow(rowDataByte, srcRegion.x, srcRegion.width, rowDataByte, 0, samplesPerPixel, bitsPerSample, xSub);
|
||||
for (int x = 0; x < srcRegion.width / xSub; x++) {
|
||||
rowDataByte[srcRegion.x + x] = rowDataByte[srcRegion.x + x * xSub];
|
||||
}
|
||||
}
|
||||
|
||||
destChannel.setDataElements(0, dstY, srcChannel);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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 (srcY % ySub != 0 || srcY < srcRegion.y || srcY >= srcRegion.y + srcRegion.height) {
|
||||
if (y % ySub != 0 || height - 1 - y < srcRegion.y || height - 1 - y >= srcRegion.y + srcRegion.height) {
|
||||
input.skipBytes(rowDataUShort.length * 2 + (rowDataUShort.length % 2) * 2);
|
||||
|
||||
return;
|
||||
@@ -534,17 +530,19 @@ public final class BMPImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
destChannel.setDataElements(0, dstY, srcChannel);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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 (srcY % ySub != 0 || srcY < srcRegion.y || srcY >= srcRegion.y + srcRegion.height) {
|
||||
if (y % ySub != 0 || height - 1 - y < srcRegion.y || height - 1 - y >= srcRegion.y + srcRegion.height) {
|
||||
input.skipBytes(rowDataInt.length * 4);
|
||||
|
||||
return;
|
||||
@@ -559,7 +557,13 @@ public final class BMPImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
destChannel.setDataElements(0, dstY, srcChannel);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Candidate util method
|
||||
|
||||
+1
-1
@@ -116,7 +116,7 @@ public final class BMPImageReaderSpi extends ImageReaderSpiBase {
|
||||
}
|
||||
}
|
||||
|
||||
public ImageReader createReaderInstance(final Object pExtension) {
|
||||
public ImageReader createReaderInstance(final Object pExtension) throws IOException {
|
||||
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.ArgumentMatchers.anyFloat;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@@ -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), anyFloat());
|
||||
ordered.verify(listener, atLeastOnce()).imageProgress(eq(reader), anyInt());
|
||||
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), anyFloat());
|
||||
ordered.verify(listener, atLeastOnce()).imageProgress(eq(reader), anyInt());
|
||||
ordered.verify(listener).imageComplete(reader);
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 257 KiB |
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.8.3</version>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-clippath</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: Photoshop Path Support</name>
|
||||
|
||||
-1
@@ -39,7 +39,6 @@ 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.8.3</version>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: Core</name>
|
||||
|
||||
-563
@@ -1,563 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+243
-40
@@ -30,17 +30,23 @@
|
||||
|
||||
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 static com.twelvemonkeys.imageio.color.ColorProfiles.*;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* A helper class for working with ICC color profiles and color spaces.
|
||||
@@ -77,6 +83,9 @@ 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. */
|
||||
@@ -87,17 +96,24 @@ 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<>(16);
|
||||
private static final Map<Key, ICC_ColorSpace> cache = new LRUHashMap<>(10);
|
||||
|
||||
static {
|
||||
// In case we didn't activate through SPI already
|
||||
ProfileDeferralActivator.activateProfiles();
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
private ColorSpaces() {}
|
||||
@@ -118,7 +134,7 @@ public final class ColorSpaces {
|
||||
Validate.notNull(profile, "profile");
|
||||
|
||||
// Fix profile before lookup/create
|
||||
fixProfile(profile);
|
||||
profileCleaner.fixProfile(profile);
|
||||
|
||||
byte[] profileHeader = getProfileHeaderWithProfileId(profile);
|
||||
|
||||
@@ -130,20 +146,53 @@ public final class ColorSpaces {
|
||||
return getCachedOrCreateCS(profile, profileHeader);
|
||||
}
|
||||
|
||||
static ICC_ColorSpace getInternalCS(final int profileCSType, final byte[] profileHeader) {
|
||||
if (profileCSType == ColorSpace.TYPE_RGB && Arrays.equals(profileHeader, ColorProfiles.sRGB.header)) {
|
||||
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)) {
|
||||
return (ICC_ColorSpace) ColorSpace.getInstance(ColorSpace.CS_sRGB);
|
||||
}
|
||||
else if (profileCSType == ColorSpace.TYPE_GRAY && Arrays.equals(profileHeader, ColorProfiles.GRAY.header)) {
|
||||
else if (profileCSType == ColorSpace.TYPE_GRAY && Arrays.equals(profileHeader, GRAY.header)) {
|
||||
return (ICC_ColorSpace) ColorSpace.getInstance(ColorSpace.CS_GRAY);
|
||||
}
|
||||
else if (profileCSType == ColorSpace.TYPE_3CLR && Arrays.equals(profileHeader, ColorProfiles.PYCC.header)) {
|
||||
else if (profileCSType == ColorSpace.TYPE_3CLR && Arrays.equals(profileHeader, PYCC.header)) {
|
||||
return (ICC_ColorSpace) ColorSpace.getInstance(ColorSpace.CS_PYCC);
|
||||
}
|
||||
else if (profileCSType == ColorSpace.TYPE_RGB && Arrays.equals(profileHeader, ColorProfiles.LINEAR_RGB.header)) {
|
||||
else if (profileCSType == ColorSpace.TYPE_RGB && Arrays.equals(profileHeader, LINEAR_RGB.header)) {
|
||||
return (ICC_ColorSpace) ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB);
|
||||
}
|
||||
else if (profileCSType == ColorSpace.TYPE_XYZ && Arrays.equals(profileHeader, ColorProfiles.CIEXYZ.header)) {
|
||||
else if (profileCSType == ColorSpace.TYPE_XYZ && Arrays.equals(profileHeader, CIEXYZ.header)) {
|
||||
return (ICC_ColorSpace) ColorSpace.getInstance(ColorSpace.CS_CIEXYZ);
|
||||
}
|
||||
|
||||
@@ -154,36 +203,26 @@ public final class ColorSpaces {
|
||||
Key key = new Key(profileHeader);
|
||||
|
||||
synchronized (cache) {
|
||||
ICC_ColorSpace cs = getCachedCS(key);
|
||||
ICC_ColorSpace cs = cache.get(key);
|
||||
|
||||
if (cs == null) {
|
||||
cs = new ICC_ColorSpace(profile);
|
||||
|
||||
validateColorSpace(cs);
|
||||
|
||||
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);
|
||||
}
|
||||
key = profileCleaner.validationAltersProfileHeader()
|
||||
? new Key(getProfileHeaderWithProfileId(cs.getProfile()))
|
||||
: key;
|
||||
|
||||
cache.put(key, cs);
|
||||
}
|
||||
|
||||
return 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) {
|
||||
private 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});
|
||||
@@ -194,27 +233,73 @@ public final class ColorSpaces {
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link ColorProfiles#isCS_sRGB(ICC_Profile)} instead.
|
||||
* 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
|
||||
public static boolean isCS_sRGB(final ICC_Profile profile) {
|
||||
return ColorProfiles.isCS_sRGB(profile);
|
||||
Validate.notNull(profile, "profile");
|
||||
|
||||
return profile.getColorSpaceType() == ColorSpace.TYPE_RGB && Arrays.equals(getProfileHeaderWithProfileId(profile), sRGB.header);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link ColorProfiles#isCS_GRAY(ICC_Profile)} instead.
|
||||
* 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
|
||||
public static boolean isCS_GRAY(final ICC_Profile profile) {
|
||||
return ColorProfiles.isCS_GRAY(profile);
|
||||
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 Use {@link ColorProfiles#validateProfile(ICC_Profile)} instead.
|
||||
* 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
|
||||
public static ICC_Profile validateProfile(final ICC_Profile profile) {
|
||||
return ColorProfiles.validateProfile(profile);
|
||||
// Fix profile before validation
|
||||
profileCleaner.fixProfile(profile);
|
||||
validateColorSpace(new ICC_ColorSpace(profile));
|
||||
|
||||
return profile;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -297,6 +382,50 @@ 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;
|
||||
|
||||
@@ -319,4 +448,78 @@ 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 getNumColorComponents() + extraSamples;
|
||||
return samples;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRed(final int pixel) {
|
||||
public final int getRed(final int pixel) {
|
||||
return icm.getRed(pixel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGreen(final int pixel) {
|
||||
public final int getGreen(final int pixel) {
|
||||
return icm.getGreen(pixel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlue(final int pixel) {
|
||||
public final int getBlue(final int pixel) {
|
||||
return icm.getBlue(pixel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAlpha(final int pixel) {
|
||||
return hasAlpha() ? (int) ((((float) pixel) / ((1 << getComponentSize(3)) - 1)) * 255.0f + 0.5f) : 0xff;
|
||||
public final 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 int getRed(final Object inData) {
|
||||
public final int getRed(final Object inData) {
|
||||
return getRed(getSample(inData, 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGreen(final Object inData) {
|
||||
public final int getGreen(final Object inData) {
|
||||
return getGreen(getSample(inData, 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlue(final Object inData) {
|
||||
public final int getBlue(final Object inData) {
|
||||
return getBlue(getSample(inData, 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAlpha(final Object inData) {
|
||||
return hasAlpha() ? getAlpha(getSample(inData, 1)) : 0xff;
|
||||
public final int getAlpha(final Object inData) {
|
||||
return hasAlpha ? getAlpha(getSample(inData, 1)) : 0xff;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SampleModel createCompatibleSampleModel(final int w, final int h) {
|
||||
public final 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 boolean isCompatibleSampleModel(final SampleModel sm) {
|
||||
public final boolean isCompatibleSampleModel(final SampleModel sm) {
|
||||
return sm instanceof PixelInterleavedSampleModel && sm.getNumBands() == samples;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WritableRaster createCompatibleWritableRaster(final int w, final int h) {
|
||||
public final WritableRaster createCompatibleWritableRaster(final int w, final int h) {
|
||||
return Raster.createWritableRaster(createCompatibleSampleModel(w, h), new Point(0, 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCompatibleRaster(final Raster raster) {
|
||||
public final boolean isCompatibleRaster(final Raster raster) {
|
||||
int size = raster.getSampleModel().getSampleSize(0);
|
||||
return ((raster.getTransferType() == transferType) &&
|
||||
(raster.getNumBands() == samples) && ((1 << size) >= icm.getMapSize()));
|
||||
|
||||
-97
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
+31
-87
@@ -45,82 +45,36 @@ public final class YCbCrConverter {
|
||||
private final static int CENTERJSAMPLE = 128;
|
||||
private final static int ONE_HALF = 1 << (SCALEBITS - 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];
|
||||
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 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;
|
||||
}
|
||||
/**
|
||||
* Initializes tables for YCC->RGB color space conversion.
|
||||
*/
|
||||
private static void buildYCCtoRGBtable() {
|
||||
if (ColorSpaces.DEBUG) {
|
||||
System.err.println("Building YCC conversion table");
|
||||
}
|
||||
|
||||
static {
|
||||
buildYCCtoRGBtable();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
static {
|
||||
buildYCCtoRGBtable();
|
||||
}
|
||||
|
||||
public static void convertYCbCr2RGB(final byte[] yCbCr, final byte[] rgb, final double[] coefficients, double[] referenceBW, final int offset) {
|
||||
@@ -154,27 +108,17 @@ public final class YCbCrConverter {
|
||||
rgb[offset + 1] = clamp(green);
|
||||
}
|
||||
|
||||
public static void convertJPEGYCbCr2RGB(final byte[] yCbCr, final byte[] rgb, final int offset) {
|
||||
int y = yCbCr[offset ] & 0xff;
|
||||
int cb = yCbCr[offset + 1] & 0xff;
|
||||
public static void convertYCbCr2RGB(final byte[] yCbCr, final byte[] rgb, final int offset) {
|
||||
int y = yCbCr[offset] & 0xff;
|
||||
int cr = yCbCr[offset + 2] & 0xff;
|
||||
int cb = yCbCr[offset + 1] & 0xff;
|
||||
|
||||
rgb[offset ] = clamp(y + 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]);
|
||||
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]);
|
||||
}
|
||||
|
||||
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) {
|
||||
private static byte clamp(int val) {
|
||||
return (byte) Math.max(0, Math.min(255, val));
|
||||
}
|
||||
}
|
||||
|
||||
-133
@@ -1,133 +0,0 @@
|
||||
/*
|
||||
* 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: %d");
|
||||
Validate.isTrue(!pHasLength || pLength >= 0, pLength, "length < 0: %f");
|
||||
|
||||
input = pInput;
|
||||
left = pLength;
|
||||
|
||||
@@ -45,7 +45,6 @@ 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;
|
||||
|
||||
@@ -159,39 +158,33 @@ public final class IIOUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* THIS METHOD WILL BE MOVED/RENAMED, DO NOT USE.
|
||||
* THIS METHOD WILL ME 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 BE MOVED/RENAMED, DO NOT USE.
|
||||
* THIS METHOD WILL ME 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} if not found
|
||||
* @return the provider instance, or {@code null}.
|
||||
*/
|
||||
public static <T> T lookupProviderByName(final ServiceRegistry registry, final String providerClassName, Class<T> category) {
|
||||
// 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;
|
||||
}
|
||||
try {
|
||||
return category.cast(registry.getServiceProviderByClass(Class.forName(providerClassName)));
|
||||
}
|
||||
catch (ClassNotFoundException ignore) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
-2
@@ -1,4 +1,2 @@
|
||||
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
@@ -1,247 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
+29
-25
@@ -90,6 +90,34 @@ 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);
|
||||
@@ -138,13 +166,11 @@ 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)));
|
||||
@@ -153,36 +179,14 @@ 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 (but are different)...
|
||||
// These profiles are extracted from various JPEGs, and have the exact same profile header...
|
||||
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"));
|
||||
|
||||
|
||||
+1
-22
@@ -37,9 +37,7 @@ import java.awt.image.*;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class DiscreteAlphaIndexColorModelTest {
|
||||
|
||||
@@ -206,25 +204,6 @@ 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,6 +39,8 @@ 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
@@ -1,28 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
+1
-2
@@ -53,7 +53,6 @@ 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);
|
||||
|
||||
@@ -434,7 +433,7 @@ public class BufferedImageInputStreamTest {
|
||||
* and {@code pFirstOffset == pSecondOffset}.
|
||||
* Otherwise {@code false}.
|
||||
*/
|
||||
public static boolean rangeEquals(byte[] pFirst, int pFirstOffset, byte[] pSecond, int pSecondOffset, int pLength) {
|
||||
static boolean rangeEquals(byte[] pFirst, int pFirstOffset, byte[] pSecond, int pSecondOffset, int pLength) {
|
||||
if (pFirst == pSecond && pFirstOffset == pSecondOffset) {
|
||||
return true;
|
||||
}
|
||||
|
||||
-380
@@ -1,380 +0,0 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
+14
-51
@@ -45,8 +45,9 @@ import javax.imageio.spi.IIORegistry;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.image.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.RenderedImage;
|
||||
import java.awt.image.SampleModel;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
@@ -56,7 +57,6 @@ 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), anyFloat());
|
||||
ordered.verify(listener, atLeastOnce()).imageProgress(eq(reader), anyInt());
|
||||
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), anyFloat());
|
||||
ordered.verify(listenerToo, atLeastOnce()).imageProgress(eq(reader), anyFloat());
|
||||
ordered.verify(listenerThree, atLeastOnce()).imageProgress(eq(reader), anyFloat());
|
||||
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).imageComplete(reader);
|
||||
ordered.verify(listenerToo).imageComplete(reader);
|
||||
@@ -1226,7 +1226,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
// Should not have called any methods...
|
||||
verifyNoInteractions(listener);
|
||||
verifyZeroInteractions(listener);
|
||||
reader.dispose();
|
||||
}
|
||||
|
||||
@@ -1253,11 +1253,11 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
// Should not have called any methods on listener1...
|
||||
verifyNoInteractions(listener);
|
||||
verifyZeroInteractions(listener);
|
||||
|
||||
InOrder ordered = inOrder(listenerToo);
|
||||
ordered.verify(listenerToo).imageStarted(reader, 0);
|
||||
ordered.verify(listenerToo, atLeastOnce()).imageProgress(eq(reader), anyFloat());
|
||||
ordered.verify(listenerToo, atLeastOnce()).imageProgress(eq(reader), anyInt());
|
||||
ordered.verify(listenerToo).imageComplete(reader);
|
||||
reader.dispose();
|
||||
}
|
||||
@@ -1281,7 +1281,7 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
// Should not have called any methods...
|
||||
verifyNoInteractions(listener);
|
||||
verifyZeroInteractions(listener);
|
||||
reader.dispose();
|
||||
}
|
||||
|
||||
@@ -1307,8 +1307,8 @@ public abstract class ImageReaderAbstractTest<T extends ImageReader> {
|
||||
}
|
||||
|
||||
// Should not have called any methods...
|
||||
verifyNoInteractions(listener);
|
||||
verifyNoInteractions(listenerToo);
|
||||
verifyZeroInteractions(listener);
|
||||
verifyZeroInteractions(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), anyFloat());
|
||||
doAnswer(abort).when(abortingListener).imageProgress(any(ImageReader.class), anyInt());
|
||||
|
||||
reader.addIIOReadProgressListener(abortingListener);
|
||||
|
||||
@@ -1738,43 +1738,6 @@ 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() {
|
||||
|
||||
+13
-12
@@ -52,6 +52,7 @@ import java.net.URL;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
@@ -78,7 +79,7 @@ public abstract class ImageWriterAbstractTest<T extends ImageWriter> {
|
||||
protected abstract ImageWriterSpi createProvider();
|
||||
|
||||
protected final T createWriter() throws IOException {
|
||||
return writerClass.cast(provider.createWriterInstance());
|
||||
return writerClass.cast(provider.createWriterInstance(null));
|
||||
}
|
||||
|
||||
protected abstract List<? extends RenderedImage> getTestData();
|
||||
@@ -103,7 +104,7 @@ public abstract class ImageWriterAbstractTest<T extends ImageWriter> {
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
|
||||
protected final RenderedImage getTestData(final int index) {
|
||||
return getTestData().get(index);
|
||||
}
|
||||
@@ -218,7 +219,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), anyFloat());
|
||||
ordered.verify(listener, atLeastOnce()).imageProgress(eq(writer), anyInt());
|
||||
ordered.verify(listener).imageComplete(writer);
|
||||
}
|
||||
|
||||
@@ -250,9 +251,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), anyFloat());
|
||||
ordered.verify(listenerToo, atLeastOnce()).imageProgress(eq(writer), anyFloat());
|
||||
ordered.verify(listenerThree, atLeastOnce()).imageProgress(eq(writer), anyFloat());
|
||||
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).imageComplete(writer);
|
||||
ordered.verify(listenerToo).imageComplete(writer);
|
||||
@@ -289,7 +290,7 @@ public abstract class ImageWriterAbstractTest<T extends ImageWriter> {
|
||||
}
|
||||
|
||||
// Should not have called any methods...
|
||||
verifyNoInteractions(listener);
|
||||
verifyZeroInteractions(listener);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -314,12 +315,12 @@ public abstract class ImageWriterAbstractTest<T extends ImageWriter> {
|
||||
}
|
||||
|
||||
// Should not have called any methods...
|
||||
verifyNoInteractions(listener);
|
||||
verifyZeroInteractions(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), anyFloat());
|
||||
ordered.verify(listenerToo, atLeastOnce()).imageProgress(eq(writer), anyInt());
|
||||
ordered.verify(listenerToo).imageComplete(writer);
|
||||
|
||||
}
|
||||
@@ -344,7 +345,7 @@ public abstract class ImageWriterAbstractTest<T extends ImageWriter> {
|
||||
}
|
||||
|
||||
// Should not have called any methods...
|
||||
verifyNoInteractions(listener);
|
||||
verifyZeroInteractions(listener);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -370,7 +371,7 @@ public abstract class ImageWriterAbstractTest<T extends ImageWriter> {
|
||||
}
|
||||
|
||||
// Should not have called any methods...
|
||||
verifyNoInteractions(listener);
|
||||
verifyNoInteractions(listenerToo);
|
||||
verifyZeroInteractions(listener);
|
||||
verifyZeroInteractions(listenerToo);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2014, Harald Kuhr
|
||||
~ All rights reserved.
|
||||
~
|
||||
~ Redistribution and use in source and binary forms, with or without
|
||||
~ modification, are permitted provided that the following conditions are met:
|
||||
~ * Redistributions of source code must retain the above copyright
|
||||
~ notice, this list of conditions and the following disclaimer.
|
||||
~ * Redistributions in binary form must reproduce the above copyright
|
||||
~ notice, this list of conditions and the following disclaimer in the
|
||||
~ documentation and/or other materials provided with the distribution.
|
||||
~ * Neither the name "TwelveMonkeys" nor the
|
||||
~ names of its contributors may be used to endorse or promote products
|
||||
~ derived from this software without specific prior written permission.
|
||||
~
|
||||
~ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
~ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
~ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
~ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
~ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
~ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
~ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
~ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
~ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
~ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
~ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>imageio</artifactId>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>imageio-cr2</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: CR2 plugin</name>
|
||||
<description>ImageIO plugin for Canon RAW (CR2) format.</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
<classifier>tests</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-metadata</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<version>${project.version}</version>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-jpeg</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
+492
@@ -0,0 +1,492 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name "TwelveMonkeys" nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.cr2;
|
||||
|
||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||
import com.twelvemonkeys.imageio.metadata.CompoundDirectory;
|
||||
import com.twelvemonkeys.imageio.metadata.Directory;
|
||||
import com.twelvemonkeys.imageio.metadata.Entry;
|
||||
import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegment;
|
||||
import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegmentUtil;
|
||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
|
||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFFReader;
|
||||
import com.twelvemonkeys.imageio.plugins.jpeg.Slice;
|
||||
import com.twelvemonkeys.imageio.plugins.jpeg.SliceContext;
|
||||
import com.twelvemonkeys.imageio.stream.SubImageInputStream;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
//import com.twelvemonkeys.imageio.plugins.jpeg.LosslessJPEGDecoder;
|
||||
|
||||
/**
|
||||
* Canon CR2 RAW ImageReader.
|
||||
* <p/>
|
||||
* Acknowledgement:
|
||||
* This ImageReader is based on the excellent work of Laurent Clevy, and would probably not exist without it.
|
||||
*
|
||||
* @see <a href="http://lclevy.free.fr/dng/">Understanding What is stored in a Canon RAW .CR2 file, How and Why</a>
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: CR2ImageReader.java,v 1.0 07.04.14 21:31 haraldk Exp$
|
||||
*/
|
||||
public final class CR2ImageReader extends ImageReaderBase {
|
||||
// See http://lclevy.free.fr/dng/
|
||||
// TODO: Avoid duped code from TIFFImageReader
|
||||
// TODO: Probably a good idea to move some of the getAsShort/Int/Long/Array to TIFF/EXIF metadata module
|
||||
// TODO: Automatic EXIF rotation, if we find a good way to do that for JPEG/EXIF/TIFF and keeping the metadata sane...
|
||||
|
||||
final static boolean DEBUG = true; //"true".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.imageio.plugins.dng.debug"));
|
||||
|
||||
// Thumbnail is in IFD1 (2nd entry)
|
||||
private final static int THUMBNAIL_IFD = 1;
|
||||
|
||||
private CompoundDirectory IFDs;
|
||||
private Directory currentIFD;
|
||||
|
||||
CR2ImageReader(final ImageReaderSpi provider) {
|
||||
super(provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resetMembers() {
|
||||
IFDs = null;
|
||||
currentIFD = null;
|
||||
}
|
||||
|
||||
private void readMetadata() throws IOException {
|
||||
if (imageInput == null) {
|
||||
throw new IllegalStateException("input not set");
|
||||
}
|
||||
|
||||
if (IFDs == null) {
|
||||
// We'll validate the TIFF structure later, for now just see if we have 'CR'0x0200 at the right place
|
||||
|
||||
imageInput.skipBytes(8); // TIFF byte order mark + magic + IFD0 offset
|
||||
|
||||
if (imageInput.readByte() != 'C' || imageInput.readByte() != 'R') {
|
||||
throw new IIOException("Not a valid CR2 structure: Missing CR magic ('CR')");
|
||||
}
|
||||
|
||||
int version = imageInput.readUnsignedByte();
|
||||
int revision = imageInput.readUnsignedByte();
|
||||
|
||||
// TODO: Choke on anything but 2.0? All sample data from 400D until 5D mk III has 2.0 version...
|
||||
|
||||
imageInput.seek(0);
|
||||
|
||||
IFDs = (CompoundDirectory) new TIFFReader().read(imageInput); // NOTE: Sets byte order as a side effect
|
||||
|
||||
if (DEBUG) {
|
||||
System.err.println("Byte order: " + imageInput.getByteOrder());
|
||||
System.err.println("Version: " + version + "." + revision);
|
||||
System.err.println("Number of IFDs: " + IFDs.directoryCount());
|
||||
|
||||
for (int i = 0; i < IFDs.directoryCount(); i++) {
|
||||
System.err.printf("IFD %d: %s\n", i, IFDs.getDirectory(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void readIFD(final int ifdIndex) throws IOException {
|
||||
readMetadata();
|
||||
|
||||
if (ifdIndex < 0) {
|
||||
throw new IndexOutOfBoundsException("index < minIndex");
|
||||
}
|
||||
else if (ifdIndex >= IFDs.directoryCount()) {
|
||||
throw new IndexOutOfBoundsException("index >= numImages (" + ifdIndex + " >= " + IFDs.directoryCount() + ")");
|
||||
}
|
||||
|
||||
currentIFD = IFDs.getDirectory(ifdIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumImages(final boolean allowSearch) throws IOException {
|
||||
readMetadata();
|
||||
|
||||
// This validation is maybe a little too restrictive, but ok for now
|
||||
if (IFDs.directoryCount() != 4) {
|
||||
throw new IIOException("Unexpected number of IFDs in CR2: " + IFDs.directoryCount());
|
||||
}
|
||||
|
||||
return IFDs.directoryCount() - 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumThumbnails(int imageIndex) throws IOException {
|
||||
readMetadata();
|
||||
checkBounds(imageIndex);
|
||||
|
||||
return imageIndex == 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean readerSupportsThumbnails() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getThumbnailWidth(int imageIndex, int thumbnailIndex) throws IOException {
|
||||
readMetadata();
|
||||
checkBounds(imageIndex);
|
||||
|
||||
// TODO: Need to get from JPEGImageReader (no ImageWidth tag), this is an ok (but lame) implementation for now
|
||||
return super.getThumbnailWidth(imageIndex, thumbnailIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getThumbnailHeight(int imageIndex, int thumbnailIndex) throws IOException {
|
||||
readMetadata();
|
||||
checkBounds(imageIndex);
|
||||
|
||||
// TODO: Need to get from JPEGImageReader (no ImageHeight tag), this is an ok (but lame) implementation for now
|
||||
return super.getThumbnailHeight(imageIndex, thumbnailIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedImage readThumbnail(int imageIndex, int thumbnailIndex) throws IOException {
|
||||
readIFD(THUMBNAIL_IFD);
|
||||
|
||||
if (imageIndex != 0) {
|
||||
throw new IndexOutOfBoundsException("No thumbnail for imageIndex: " + imageIndex);
|
||||
}
|
||||
if (thumbnailIndex != 0) {
|
||||
throw new IndexOutOfBoundsException("thumbnailIndex out of bounds: " + thumbnailIndex);
|
||||
}
|
||||
|
||||
// This IFD (IFD1) lacks Compression 6 (old JPEG), but has the relevant tags for a JPEG/EXIF thumbnail
|
||||
int jpegOffset = getValueAsInt(TIFF.TAG_JPEG_INTERCHANGE_FORMAT, "JPEGInterchangeFormat");
|
||||
int jpegLength = getValueAsInt(TIFF.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, "JPEGInterchangeFormatLength");
|
||||
|
||||
imageInput.seek(jpegOffset);
|
||||
|
||||
// TODO: Consider using cached JPEGImageReader directly
|
||||
return ImageIO.read(new SubImageInputStream(imageInput, jpegLength));
|
||||
}
|
||||
|
||||
private long[] getValueAsLongArray(final int tag, final String tagName, boolean required) throws IIOException {
|
||||
Entry entry = currentIFD.getEntryById(tag);
|
||||
if (entry == null) {
|
||||
if (required) {
|
||||
throw new IIOException("Missing TIFF tag " + tagName);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
long[] value;
|
||||
|
||||
if (entry.valueCount() == 1) {
|
||||
// For single entries, this will be a boxed type
|
||||
value = new long[] {((Number) entry.getValue()).longValue()};
|
||||
}
|
||||
else if (entry.getValue() instanceof short[]) {
|
||||
short[] shorts = (short[]) entry.getValue();
|
||||
value = new long[shorts.length];
|
||||
|
||||
for (int i = 0, length = value.length; i < length; i++) {
|
||||
value[i] = shorts[i];
|
||||
}
|
||||
}
|
||||
else if (entry.getValue() instanceof int[]) {
|
||||
int[] ints = (int[]) entry.getValue();
|
||||
value = new long[ints.length];
|
||||
|
||||
for (int i = 0, length = value.length; i < length; i++) {
|
||||
value[i] = ints[i];
|
||||
}
|
||||
}
|
||||
else if (entry.getValue() instanceof long[]) {
|
||||
value = (long[]) entry.getValue();
|
||||
}
|
||||
else {
|
||||
throw new IIOException(String.format("Unsupported %s type: %s (%s)", tagName, entry.getTypeName(), entry.getValue().getClass()));
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private Number getValueAsNumberWithDefault(final int tag, final String tagName, final Number defaultValue) throws IIOException {
|
||||
Entry entry = currentIFD.getEntryById(tag);
|
||||
|
||||
if (entry == null) {
|
||||
if (defaultValue != null) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
throw new IIOException("Missing TIFF tag: " + (tagName != null ? tagName : tag));
|
||||
}
|
||||
|
||||
return (Number) entry.getValue();
|
||||
}
|
||||
|
||||
private long getValueAsLongWithDefault(final int tag, final String tagName, final Long defaultValue) throws IIOException {
|
||||
return getValueAsNumberWithDefault(tag, tagName, defaultValue).longValue();
|
||||
}
|
||||
|
||||
private long getValueAsLongWithDefault(final int tag, final Long defaultValue) throws IIOException {
|
||||
return getValueAsLongWithDefault(tag, null, defaultValue);
|
||||
}
|
||||
|
||||
private int getValueAsIntWithDefault(final int tag, final String tagName, final Integer defaultValue) throws IIOException {
|
||||
return getValueAsNumberWithDefault(tag, tagName, defaultValue).intValue();
|
||||
}
|
||||
|
||||
private int getValueAsIntWithDefault(final int tag, Integer defaultValue) throws IIOException {
|
||||
return getValueAsIntWithDefault(tag, null, defaultValue);
|
||||
}
|
||||
|
||||
private int getValueAsInt(final int tag, String tagName) throws IIOException {
|
||||
return getValueAsIntWithDefault(tag, tagName, null);
|
||||
}
|
||||
|
||||
private int imageIndexToIFDNumber(int imageIndex) {
|
||||
return imageIndex >= THUMBNAIL_IFD ? imageIndex + 1 : imageIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth(int imageIndex) throws IOException {
|
||||
readIFD(imageIndexToIFDNumber(imageIndex));
|
||||
|
||||
return getValueAsInt(TIFF.TAG_IMAGE_WIDTH, "ImageWidth");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight(int imageIndex) throws IOException {
|
||||
readIFD(imageIndexToIFDNumber(imageIndex));
|
||||
|
||||
return getValueAsInt(TIFF.TAG_IMAGE_HEIGHT, "ImageHeight");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IOException {
|
||||
readIFD(imageIndexToIFDNumber(imageIndex));
|
||||
|
||||
// TODO: For IFD0, get from JPEGImageReader delagate
|
||||
|
||||
// return Arrays.asList(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR)).iterator();
|
||||
Entry bitsPerSample = currentIFD.getEntryById(TIFF.TAG_BITS_PER_SAMPLE);
|
||||
if (bitsPerSample == null) {
|
||||
// TODO: FixME!
|
||||
return Arrays.asList(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR)).iterator();
|
||||
}
|
||||
|
||||
// For IFD1, create linear RGB, but we don't really know...
|
||||
int bitDepth = ((int[]) bitsPerSample.getValue())[0]; // Assume all equal!
|
||||
if (bitDepth == 8) {
|
||||
return Arrays.asList(ImageTypeSpecifier.createInterleaved(ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB), new int [] {0, 1, 2}, DataBuffer.TYPE_BYTE, false, false)).iterator();
|
||||
}
|
||||
else if (bitDepth == 16) {
|
||||
return Arrays.asList(ImageTypeSpecifier.createInterleaved(ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB), new int [] {0, 1, 2}, DataBuffer.TYPE_USHORT, false, false)).iterator();
|
||||
}
|
||||
|
||||
throw new IIOException("Unsupported bit depth: " + bitDepth);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedImage read(int imageIndex, ImageReadParam param) throws IOException {
|
||||
readIFD(imageIndexToIFDNumber(imageIndex));
|
||||
|
||||
if (imageIndex == 0) {
|
||||
// This one says Compression 6 (old JPEG) and contains normal JPEG data at StripOffsets (but has no JPEGInterchangeFormat tag)
|
||||
int compression = getValueAsInt(TIFF.TAG_COMPRESSION, "Compression");
|
||||
if (compression != 6) {
|
||||
throw new IIOException("Unknown TIFF compression for CR2 IFD0: " + compression);
|
||||
}
|
||||
|
||||
int stripOffsets = getValueAsInt(TIFF.TAG_STRIP_OFFSETS, "StripOffsets");
|
||||
int stripByteCounts = getValueAsInt(TIFF.TAG_STRIP_BYTE_COUNTS, "StripByteCounts");
|
||||
imageInput.seek(stripOffsets);
|
||||
|
||||
List<JPEGSegment> segments = JPEGSegmentUtil.readSegments(new SubImageInputStream(imageInput, stripByteCounts), JPEGSegmentUtil.ALL_SEGMENTS);
|
||||
System.err.println("segments: " + segments);
|
||||
|
||||
imageInput.seek(stripOffsets);
|
||||
BufferedImage image = ImageIO.read(new SubImageInputStream(imageInput, stripByteCounts));
|
||||
System.err.println("image: " + image);
|
||||
return image;
|
||||
}
|
||||
|
||||
if (imageIndex == 1) {
|
||||
// This one is semi-ok, for older cameras is says Compression 6 (old JPEG), for 7D it says Compression 1 (None)
|
||||
// We'll just ignore the compression and assume it's 1 (None).
|
||||
// TODO: Probably a good idea to verify that we have no other compression than 6/1
|
||||
// TODO: Consider just masking out this image, as it's not of much use...
|
||||
|
||||
int width = getWidth(imageIndex);
|
||||
int height = getHeight(imageIndex);
|
||||
|
||||
BufferedImage destination = getDestination(param, getImageTypes(imageIndex), width, height);
|
||||
WritableRaster raster;
|
||||
|
||||
int dataType = destination.getSampleModel().getDataType();
|
||||
if (dataType == DataBuffer.TYPE_BYTE) {
|
||||
// Emulate raw type (as dest, but RGB instead of BGR)
|
||||
raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, width, 1, width * 3, 3, new int[] {0, 1, 2}, null);
|
||||
}
|
||||
else if (dataType == DataBuffer.TYPE_USHORT) {
|
||||
// Emulate raw type (as dest, but RGB instead of BGR)
|
||||
raster = Raster.createInterleavedRaster(DataBuffer.TYPE_USHORT, width, 1, width * 3, 3, new int[] {0, 1, 2}, null);
|
||||
}
|
||||
else {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
DataBuffer dataBuffer = raster.getDataBuffer();
|
||||
|
||||
SampleModel destSampleModel = destination.getSampleModel();
|
||||
DataBuffer destBuffer = destination.getRaster().getDataBuffer();
|
||||
SampleModel srcSampleModel = raster.getSampleModel();
|
||||
|
||||
if (destBuffer.getSize() != getValueAsInt(TIFF.TAG_STRIP_BYTE_COUNTS, "StripByteCounts")) {
|
||||
System.err.println("dataBuffer: " + dataBuffer.getSize());
|
||||
System.err.println("StripByteCounts: " + getValueAsInt(TIFF.TAG_STRIP_BYTE_COUNTS, "StripByteCounts"));
|
||||
}
|
||||
|
||||
int stripOffsets = getValueAsInt(TIFF.TAG_STRIP_OFFSETS, "StripOffsets");
|
||||
imageInput.seek(stripOffsets);
|
||||
|
||||
Object data = null;
|
||||
for (int y = 0; y < height; y++) {
|
||||
if (dataType == DataBuffer.TYPE_BYTE) {
|
||||
imageInput.readFully(((DataBufferByte) dataBuffer).getData());
|
||||
}
|
||||
else {
|
||||
imageInput.readFully(((DataBufferUShort) dataBuffer).getData(), 0, dataBuffer.getSize());
|
||||
}
|
||||
|
||||
data = srcSampleModel.getDataElements(0, 0, width, 1, data, dataBuffer);
|
||||
destSampleModel.setDataElements(0, y, width, 1, data, destBuffer);
|
||||
}
|
||||
|
||||
// TODO: This seems to work as crop values, if so correct w/h from getImageWidth/Height??
|
||||
Entry unknown = currentIFD.getEntryById(50908);
|
||||
if (unknown != null) {
|
||||
Graphics2D g = destination.createGraphics();
|
||||
try {
|
||||
long[] values = (long[]) unknown.getValue();
|
||||
|
||||
g.setPaint(new Color(63, 223, 88, 128));
|
||||
// g.drawRect((int) values[2], (int) values[3], (int) values[0], (int) values[1]);
|
||||
// g.fillRect((int) values[2], (int) values[3], (int) values[0], (int) values[1]);
|
||||
g.fillRect(0, 0, width, (int) values[3]);
|
||||
g.fillRect(0, (int) values[3], (int) values[2], (int) (values[1] + values[3]));
|
||||
g.fillRect((int) (values[2] + values[0]), (int) values[3], (int) values[2], (int) (values[1] + values[3]));
|
||||
g.fillRect(0, (int) (values[3] + values[1]), width, height - (int) (values[3] + values[1]));
|
||||
}
|
||||
finally {
|
||||
g.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
return destination;
|
||||
}
|
||||
|
||||
if (imageIndex == 2) {
|
||||
// TODO: This is the real RAW data. It's supposed to be lossless JPEG encoded, single channel,
|
||||
// and has to be interpolated to become full 3 channel data from the Bayer CFA array,
|
||||
// then further processed with white balance correction, black subtraction and color scaling.
|
||||
// At least. ;-)
|
||||
|
||||
// We should probably just mask this image out, until we can read it
|
||||
|
||||
int stripOffsets = getValueAsInt(TIFF.TAG_STRIP_OFFSETS, "StripOffsets");
|
||||
int stripByteCounts = getValueAsInt(TIFF.TAG_STRIP_BYTE_COUNTS, "StripByteCounts");
|
||||
long[] slices = getValueAsLongArray(50752, "Slices", true);
|
||||
|
||||
try {
|
||||
final Slice slice = Slice.createSlice(slices);
|
||||
SliceContext.set(slice);
|
||||
|
||||
// TODO: Get correct dimensions (sensor size?)
|
||||
int width = getWidth(0);
|
||||
int height = getHeight(0);
|
||||
|
||||
imageInput.seek(stripOffsets);
|
||||
return ImageIO.read(new SubImageInputStream(imageInput, stripByteCounts));
|
||||
} finally {
|
||||
SliceContext.remove();
|
||||
}
|
||||
|
||||
// byte[] data = new LosslessJPEGDecoder().decompress(new SubImageInputStream(imageInput, stripByteCounts), null);
|
||||
//
|
||||
// // TODO: We really have 2 bytes/sample
|
||||
// short[] data2 = new short[data.length / 2];
|
||||
// ByteBuffer wrap = ByteBuffer.wrap(data);
|
||||
// wrap.asShortBuffer().get(data2);
|
||||
//
|
||||
// System.err.println("data.length: " + data2.length);
|
||||
// System.err.println("width x height: " + width * height);
|
||||
//
|
||||
// DataBuffer dataBuffer = new DataBufferUShort(data2, data2.length);
|
||||
// WritableRaster raster = Raster.createInterleavedRaster(dataBuffer, width, height, width, 1, new int[] {0}, null);
|
||||
// ColorModel cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), false, false, Transparency.OPAQUE, raster.getTransferType());
|
||||
// BufferedImage image = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
|
||||
//
|
||||
// System.err.println("image: " + image);
|
||||
//
|
||||
// return image;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
CR2ImageReader reader = new CR2ImageReader(new CR2ImageReaderSpi());
|
||||
|
||||
for (String arg : args) {
|
||||
ImageInputStream stream = ImageIO.createImageInputStream(new File(arg));
|
||||
reader.setInput(stream);
|
||||
|
||||
int numImages = reader.getNumImages(true);
|
||||
for (int i = 0; i < numImages; i++) {
|
||||
int numThumbnails = reader.getNumThumbnails(i);
|
||||
for (int n = 0; n < numThumbnails; n++) {
|
||||
showIt(reader.readThumbnail(i, n), arg + " image " + i + " thumbnail " + n);
|
||||
}
|
||||
|
||||
showIt(reader.read(i), arg + " image " + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name "TwelveMonkeys" nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.cr2;
|
||||
|
||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
|
||||
import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
|
||||
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* CR2ImageReaderSpi
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: CR2ImageReaderSpi.java,v 1.0 07.04.14 21:26 haraldk Exp$
|
||||
*/
|
||||
public final class CR2ImageReaderSpi extends ImageReaderSpiBase {
|
||||
public CR2ImageReaderSpi() {
|
||||
super(new CR2ProviderInfo());
|
||||
}
|
||||
|
||||
public boolean canDecodeInput(final Object pSource) throws IOException {
|
||||
if (!(pSource instanceof ImageInputStream)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ImageInputStream stream = (ImageInputStream) pSource;
|
||||
|
||||
stream.mark();
|
||||
try {
|
||||
byte[] bom = new byte[2];
|
||||
stream.readFully(bom);
|
||||
|
||||
ByteOrder originalOrder = stream.getByteOrder();
|
||||
|
||||
try {
|
||||
if (bom[0] == 'I' && bom[1] == 'I') {
|
||||
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
}
|
||||
else if (bom[0] == 'M' && bom[1] == 'M') {
|
||||
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
||||
int tiffMagic = stream.readUnsignedShort();
|
||||
if (tiffMagic != TIFF.TIFF_MAGIC) {
|
||||
return false;
|
||||
}
|
||||
|
||||
stream.skipBytes(4); // TIFF IFD0 offset
|
||||
|
||||
if (stream.readByte() != 'C' || stream.readByte() != 'R') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Version 2.0
|
||||
return stream.readUnsignedByte() == 2 && stream.readUnsignedByte() == 0;
|
||||
}
|
||||
finally {
|
||||
stream.setByteOrder(originalOrder);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
stream.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageReader createReaderInstance(Object extension) throws IOException {
|
||||
return new CR2ImageReader(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription(Locale locale) {
|
||||
return "Canon RAW (CR2) format Reader";
|
||||
}
|
||||
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oleg Ermolaev
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name "TwelveMonkeys" nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.cr2;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
||||
|
||||
/**
|
||||
* @author Oleg Ermolaev Date: 04.05.2018 1:50
|
||||
*/
|
||||
class CR2ProviderInfo extends ReaderWriterProviderInfo {
|
||||
protected CR2ProviderInfo() {
|
||||
super(
|
||||
CR2ProviderInfo.class,
|
||||
new String[]{"cr2", "CR2"},
|
||||
new String[]{"cr2"},
|
||||
new String[]{"image/x-canon-raw", // TODO: Look up
|
||||
},
|
||||
"com.twelvemonkeys.imageio.plugins.cr2.CR2ImageReader",
|
||||
new String[]{"com.twelvemonkeys.imageio.plugins.cr2.CR2ImageReaderSpi"},
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
com.twelvemonkeys.imageio.plugins.cr2.CR2ImageReaderSpi
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name "TwelveMonkeys" nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.cr2;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import java.awt.*;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* CR2ImageReaderTest
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: CR2ImageReaderTest.java,v 1.0 07.04.14 21:52 haraldk Exp$
|
||||
*/
|
||||
@Ignore
|
||||
public class CR2ImageReaderTest extends ImageReaderAbstractTest<CR2ImageReader> {
|
||||
@Override
|
||||
protected List<TestData> getTestData() {
|
||||
return Arrays.asList(
|
||||
new TestData(
|
||||
getClassLoaderResource("/cr2/IMG_3483.CR2"), // Canon EOS 400D
|
||||
new Dimension(3888, 2592) // This is what the TIFF structure says...
|
||||
/* from http://lclevy.free.fr/dng/:
|
||||
new Dimension(3888 / 4, 2592 / 4), // The image is supposed to be 1/4 of the size in the TIFF tags...
|
||||
new Dimension(-1, -1), // "small version", no size information, only JPEG data (EXIF thumbnail)
|
||||
new Dimension(384, 256), // according to http://lclevy.free.fr/dng/ this is not compressed, but TIFF structure says JPEG... Perhaps JPEG lossless?
|
||||
new Dimension(3888, 2592) // Full size image, no size information
|
||||
*/
|
||||
)
|
||||
// TODO: EOS 7D sample
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ImageReaderSpi createProvider() {
|
||||
return new CR2ImageReaderSpi();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getFormatNames() {
|
||||
return Arrays.asList("cr2");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getSuffixes() {
|
||||
return Arrays.asList("cr2");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getMIMETypes() {
|
||||
return Arrays.asList("image/x-canon-raw");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Known issue: Subsampled reading not supported")
|
||||
@Override
|
||||
public void testReadWithSubsampleParamPixels() throws IOException {
|
||||
super.testReadWithSubsampleParamPixels();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Known issue: Source region reading not supported")
|
||||
@Override
|
||||
public void testReadWithSourceRegionParamEqualImage() throws IOException {
|
||||
super.testReadWithSourceRegionParamEqualImage();
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,7 @@
|
||||
Contents:
|
||||
IMG_3483.CR2 - Rusty chain at Vækerø harbour, Oslo, Norway, Canon 400D, by me
|
||||
IMG_4841.CR2 - Chimneys at Casa Mila, Barcelona, Spain, Canon 400D, by me
|
||||
IMG_6933.CR2 - Panda PEZ dispenser at home, Oslo Noway, Canon 7D, by me
|
||||
|
||||
Above mentioned images are freely distributable for testing purposes.
|
||||
-- Harald K
|
||||
@@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2014, Harald Kuhr
|
||||
~ All rights reserved.
|
||||
~
|
||||
~ Redistribution and use in source and binary forms, with or without
|
||||
~ modification, are permitted provided that the following conditions are met:
|
||||
~ * Redistributions of source code must retain the above copyright
|
||||
~ notice, this list of conditions and the following disclaimer.
|
||||
~ * Redistributions in binary form must reproduce the above copyright
|
||||
~ notice, this list of conditions and the following disclaimer in the
|
||||
~ documentation and/or other materials provided with the distribution.
|
||||
~ * Neither the name "TwelveMonkeys" nor the
|
||||
~ names of its contributors may be used to endorse or promote products
|
||||
~ derived from this software without specific prior written permission.
|
||||
~
|
||||
~ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
~ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
~ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
~ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
~ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
~ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
~ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
~ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
~ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
~ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
~ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>imageio</artifactId>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>imageio-crw</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: CRW plugin</name>
|
||||
<description>ImageIO plugin for Canon RAW (CRW) format.</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
<type>test-jar</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-metadata</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<version>${project.version}</version>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-jpeg</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.twelvemonkeys.imageio.plugins.crw;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* CRW
|
||||
*
|
||||
* @see <a href="https://sno.phy.queensu.ca/~phil/exiftool/canon_raw.html">The Canon RAW (CRW) File Format</a>
|
||||
*/
|
||||
public interface CRW {
|
||||
int TAG_SLICES = 50752;
|
||||
}
|
||||
+388
@@ -0,0 +1,388 @@
|
||||
package com.twelvemonkeys.imageio.plugins.crw;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
|
||||
/**
|
||||
* CRWDecoder
|
||||
*
|
||||
* @see <a href="http://cybercom.net/~dcoffin/dcraw/decompress.c">A simple reference decompressor for CRW files</a>
|
||||
* @author Harald Kuhr (Java port)
|
||||
* @author Dave Coffin (original decompress.c)
|
||||
*/
|
||||
final class CRWDecoder {
|
||||
|
||||
static class Decode {
|
||||
Decode[] branch = new Decode[2];
|
||||
int leaf;
|
||||
}
|
||||
|
||||
private final ImageInputStream imageInput;
|
||||
|
||||
private final int height;
|
||||
private final int width;
|
||||
private final int table;
|
||||
|
||||
private Decode[] first_decode = new Decode[32];
|
||||
private Decode[] second_decode = new Decode[512];
|
||||
|
||||
CRWDecoder(ImageInputStream imageInput, int width, int height, int table) {
|
||||
this.imageInput = imageInput;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.table = table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code false} if the image starts with compressed data,
|
||||
* {@code true} if it starts with uncompressed low-order bits.
|
||||
* <p>
|
||||
* In Canon compressed data, 0xff is always followed by 0x00.
|
||||
*/
|
||||
private boolean canonHasLowbits() throws IOException {
|
||||
byte[] test = new byte[0x4000]; // TODO: Should probably be (height * width / 4) * enough bytes...
|
||||
|
||||
imageInput.seek(0);
|
||||
imageInput.readFully(test);
|
||||
|
||||
boolean ret = true;
|
||||
// for (int i = 540; i < test.length - 1; i++) {
|
||||
for (int i = 0; i < test.length - 1; i++) { // Note: The original 540 is probably CIFF header length + offset (26 + 514)
|
||||
if ((test[i] & 0xff) == 0xff) {
|
||||
if (test[i + 1] != 0) {
|
||||
return true;
|
||||
}
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
A rough description of Canon's compression algorithm:
|
||||
|
||||
+ Each pixel outputs a 10-bit sample, from 0 to 1023.
|
||||
+ Split the data into blocks of 64 samples each.
|
||||
+ Subtract from each sample the value of the sample two positions
|
||||
to the left, which has the same color filter. From the two
|
||||
leftmost samples in each row, subtract 512.
|
||||
+ For each nonzero sample, make a token consisting of two four-bit
|
||||
numbers. The low nibble is the number of bits required to
|
||||
represent the sample, and the high nibble is the number of
|
||||
zero samples preceding this sample.
|
||||
+ Output this token as a variable-length bitstring using
|
||||
one of three tablesets. Follow it with a fixed-length
|
||||
bitstring containing the sample.
|
||||
|
||||
The "first_decode" table is used for the first sample in each
|
||||
block, and the "second_decode" table is used for the others.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Construct a decode tree according the specification in *source.
|
||||
* The first 16 bytes specify how many codes should be 1-bit, 2-bit
|
||||
* 3-bit, etc. Bytes after that are the leaf values.
|
||||
* <p>
|
||||
* For example, if the source is
|
||||
* <p>
|
||||
* { 0,1,4,2,3,1,2,0,0,0,0,0,0,0,0,0,
|
||||
* 0x04,0x03,0x05,0x06,0x02,0x07,0x01,0x08,0x09,0x00,0x0a,0x0b,0xff },
|
||||
* <p>
|
||||
* then the code is
|
||||
* <p>
|
||||
* 00 0x04
|
||||
* 010 0x03
|
||||
* 011 0x05
|
||||
* 100 0x06
|
||||
* 101 0x02
|
||||
* 1100 0x07
|
||||
* 1101 0x01
|
||||
* 11100 0x08
|
||||
* 11101 0x09
|
||||
* 11110 0x00
|
||||
* 111110 0x0a
|
||||
* 1111110 0x0b
|
||||
* 1111111 0xff
|
||||
*/
|
||||
private Decode[] free; /* Next unused node */
|
||||
private int freeIndex;
|
||||
private int leaf; /* no. of leaves already added */
|
||||
|
||||
// private void make_decoder(struct decode *dest, const uchar *source, int level)
|
||||
private void make_decoder(Decode[] dest, int destIndex, final byte[] source, int level) {
|
||||
// static struct decode *free; /* Next unused node */
|
||||
// static int leaf; /* no. of leaves already added */
|
||||
int i, next;
|
||||
|
||||
if (level==0) {
|
||||
free = dest;
|
||||
freeIndex = 0;
|
||||
|
||||
leaf = 0;
|
||||
}
|
||||
// free++;
|
||||
freeIndex++;
|
||||
// At what level should the next leaf appear?
|
||||
for (i=next=0; i <= leaf && next < 16; ) {
|
||||
i += (source[next++] & 0xff);
|
||||
}
|
||||
|
||||
if (i > leaf) {
|
||||
if (level < next) { /* Are we there yet? */
|
||||
// dest->branch[0] = free;
|
||||
// make_decoder(free, source, level + 1);
|
||||
dest[destIndex].branch[0] = free[freeIndex];
|
||||
make_decoder(free, freeIndex, source, level + 1);
|
||||
// dest->branch[1] = free;
|
||||
// make_decoder(free, source, level + 1);
|
||||
dest[destIndex].branch[1] = free[freeIndex];
|
||||
make_decoder(free, freeIndex, source, level + 1);
|
||||
} else {
|
||||
// dest->leaf = source[16 + leaf++];
|
||||
dest[destIndex].leaf = (source[16 + leaf++] & 0xff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static final byte[][] first_tree/*[3][29]*/ = {
|
||||
{ 0,1,4,2,3,1,2,0,0,0,0,0,0,0,0,0,
|
||||
0x04,0x03,0x05,0x06,0x02,0x07,0x01,0x08,0x09,0x00,0x0a,0x0b, (byte) 0xff},
|
||||
|
||||
{ 0,2,2,3,1,1,1,1,2,0,0,0,0,0,0,0,
|
||||
0x03,0x02,0x04,0x01,0x05,0x00,0x06,0x07,0x09,0x08,0x0a,0x0b, (byte) 0xff},
|
||||
|
||||
{ 0,0,6,3,1,1,2,0,0,0,0,0,0,0,0,0,
|
||||
0x06,0x05,0x07,0x04,0x08,0x03,0x09,0x02,0x00,0x0a,0x01,0x0b, (byte) 0xff},
|
||||
};
|
||||
|
||||
static final byte[][] second_tree/*[3][180]*/ = {
|
||||
{ 0,2,2,2,1,4,2,1,2,5,1,1,0,0,0, (byte) 139,
|
||||
0x03,0x04,0x02,0x05,0x01,0x06,0x07,0x08,
|
||||
0x12,0x13,0x11,0x14,0x09,0x15,0x22,0x00,0x21,0x16,0x0a, (byte) 0xf0,
|
||||
0x23,0x17,0x24,0x31,0x32,0x18,0x19,0x33,0x25,0x41,0x34,0x42,
|
||||
0x35,0x51,0x36,0x37,0x38,0x29,0x79,0x26,0x1a,0x39,0x56,0x57,
|
||||
0x28,0x27,0x52,0x55,0x58,0x43,0x76,0x59,0x77,0x54,0x61, (byte) 0xf9,
|
||||
0x71,0x78,0x75, (byte) 0x96, (byte) 0x97,0x49, (byte) 0xb7,0x53, (byte) 0xd7,0x74, (byte) 0xb6, (byte) 0x98,
|
||||
0x47,0x48, (byte) 0x95,0x69, (byte) 0x99, (byte) 0x91, (byte) 0xfa, (byte) 0xb8,0x68, (byte) 0xb5, (byte) 0xb9, (byte) 0xd6,
|
||||
(byte) 0xf7, (byte) 0xd8,0x67,0x46,0x45, (byte) 0x94,(byte) 0x89,(byte) 0xf8,(byte) 0x81,(byte) 0xd5,(byte) 0xf6,(byte) 0xb4,
|
||||
(byte) 0x88,(byte) 0xb1,0x2a,0x44,0x72,(byte) 0xd9,(byte) 0x87,0x66,(byte) 0xd4,(byte) 0xf5,0x3a,(byte) 0xa7,
|
||||
0x73,(byte) 0xa9,(byte) 0xa8,(byte) 0x86,0x62,(byte) 0xc7,0x65,(byte) 0xc8,(byte) 0xc9,(byte) 0xa1,(byte) 0xf4,(byte) 0xd1,
|
||||
(byte) 0xe9,0x5a,(byte) 0x92,(byte) 0x85,(byte) 0xa6,(byte) 0xe7,(byte) 0x93,(byte) 0xe8,(byte) 0xc1,(byte) 0xc6,0x7a,0x64,
|
||||
(byte) 0xe1,0x4a,0x6a,(byte) 0xe6,(byte) 0xb3,(byte) 0xf1,(byte) 0xd3,(byte) 0xa5,(byte) 0x8a,(byte) 0xb2,(byte) 0x9a,(byte) 0xba,
|
||||
(byte) 0x84,(byte) 0xa4,0x63,(byte) 0xe5,(byte) 0xc5,(byte) 0xf3,(byte) 0xd2,(byte) 0xc4,(byte) 0x82,(byte) 0xaa,(byte) 0xda,(byte) 0xe4,
|
||||
(byte) 0xf2,(byte) 0xca,(byte) 0x83,(byte) 0xa3,(byte) 0xa2,(byte) 0xc3,(byte) 0xea,(byte) 0xc2,(byte) 0xe2,(byte) 0xe3,(byte) 0xff,(byte) 0xff },
|
||||
|
||||
{ 0,2,2,1,4,1,4,1,3,3,1,0,0,0,0, (byte) 140,
|
||||
0x02,0x03,0x01,0x04,0x05,0x12,0x11,0x06,
|
||||
0x13,0x07,0x08,0x14,0x22,0x09,0x21,0x00,0x23,0x15,0x31,0x32,
|
||||
0x0a,0x16,(byte) 0xf0,0x24,0x33,0x41,0x42,0x19,0x17,0x25,0x18,0x51,
|
||||
0x34,0x43,0x52,0x29,0x35,0x61,0x39,0x71,0x62,0x36,0x53,0x26,
|
||||
0x38,0x1a,0x37,(byte) 0x81,0x27,(byte) 0x91,0x79,0x55,0x45,0x28,0x72,0x59,
|
||||
(byte) 0xa1,(byte) 0xb1,0x44,0x69,0x54,0x58,(byte) 0xd1,(byte) 0xfa,0x57,(byte) 0xe1,(byte) 0xf1,(byte) 0xb9,
|
||||
0x49,0x47,0x63,0x6a,(byte) 0xf9,0x56,0x46,(byte) 0xa8,0x2a,0x4a,0x78,(byte) 0x99,
|
||||
0x3a,0x75,0x74,(byte) 0x86,0x65,(byte) 0xc1,0x76,(byte) 0xb6,(byte) 0x96,(byte) 0xd6,(byte) 0x89,(byte) 0x85,
|
||||
(byte) 0xc9,(byte) 0xf5,(byte) 0x95,(byte) 0xb4,(byte) 0xc7,(byte) 0xf7,(byte) 0x8a,(byte) 0x97,(byte) 0xb8,0x73,(byte) 0xb7,(byte) 0xd8,
|
||||
(byte) 0xd9,(byte) 0x87,(byte) 0xa7,0x7a,0x48,(byte) 0x82,(byte) 0x84,(byte) 0xea,(byte) 0xf4,(byte) 0xa6,(byte) 0xc5,0x5a,
|
||||
(byte) 0x94,(byte) 0xa4,(byte) 0xc6,(byte) 0x92,(byte) 0xc3,0x68,(byte) 0xb5,(byte) 0xc8,(byte) 0xe4,(byte) 0xe5,(byte) 0xe6,(byte) 0xe9,
|
||||
(byte) 0xa2,(byte) 0xa3,(byte) 0xe3,(byte) 0xc2,0x66,0x67,(byte) 0x93,(byte) 0xaa,(byte) 0xd4,(byte) 0xd5,(byte) 0xe7,(byte) 0xf8,
|
||||
(byte) 0x88,(byte) 0x9a,(byte) 0xd7,0x77,(byte) 0xc4,0x64,(byte) 0xe2,(byte) 0x98,(byte) 0xa5,(byte) 0xca,(byte) 0xda,(byte) 0xe8,
|
||||
(byte) 0xf3,(byte) 0xf6,(byte) 0xa9,(byte) 0xb2,(byte) 0xb3,(byte) 0xf2,(byte) 0xd2,(byte) 0x83,(byte) 0xba,(byte) 0xd3,(byte) 0xff,(byte) 0xff },
|
||||
|
||||
{ 0,0,6,2,1,3,3,2,5,1,2,2,8,10,0,117,
|
||||
0x04,0x05,0x03,0x06,0x02,0x07,0x01,0x08,
|
||||
0x09,0x12,0x13,0x14,0x11,0x15,0x0a,0x16,0x17,(byte) 0xf0,0x00,0x22,
|
||||
0x21,0x18,0x23,0x19,0x24,0x32,0x31,0x25,0x33,0x38,0x37,0x34,
|
||||
0x35,0x36,0x39,0x79,0x57,0x58,0x59,0x28,0x56,0x78,0x27,0x41,
|
||||
0x29,0x77,0x26,0x42,0x76,(byte) 0x99,0x1a,0x55,(byte) 0x98,(byte) 0x97,(byte) 0xf9,0x48,
|
||||
0x54,(byte) 0x96,(byte) 0x89,0x47,(byte) 0xb7,0x49,(byte) 0xfa,0x75,0x68,(byte) 0xb6,0x67,0x69,
|
||||
(byte) 0xb9,(byte) 0xb8,(byte) 0xd8,0x52,(byte) 0xd7,(byte) 0x88,(byte) 0xb5,0x74,0x51,0x46,(byte) 0xd9,(byte) 0xf8,
|
||||
0x3a,(byte) 0xd6,(byte) 0x87,0x45,0x7a,(byte) 0x95,(byte) 0xd5,(byte) 0xf6,(byte) 0x86,(byte) 0xb4,(byte) 0xa9,(byte) 0x94,
|
||||
0x53,0x2a,(byte) 0xa8,0x43,(byte) 0xf5,(byte) 0xf7,(byte) 0xd4,0x66,(byte) 0xa7,0x5a,0x44,(byte) 0x8a,
|
||||
(byte) 0xc9,(byte) 0xe8,(byte) 0xc8,(byte) 0xe7,(byte) 0x9a,0x6a,0x73,0x4a,0x61,(byte) 0xc7,(byte) 0xf4,(byte) 0xc6,
|
||||
0x65,(byte) 0xe9,0x72,(byte) 0xe6,0x71,(byte) 0x91,(byte) 0x93,(byte) 0xa6,(byte) 0xda,(byte) 0x92,(byte) 0x85,0x62,
|
||||
(byte) 0xf3,(byte) 0xc5,(byte) 0xb2,(byte) 0xa4,(byte) 0x84,(byte) 0xba,0x64,(byte) 0xa5,(byte) 0xb3,(byte) 0xd2,(byte) 0x81,(byte) 0xe5,
|
||||
(byte) 0xd3,(byte) 0xaa,(byte) 0xc4,(byte) 0xca,(byte) 0xf2,(byte) 0xb1,(byte) 0xe4,(byte) 0xd1,(byte) 0x83,0x63,(byte) 0xea,(byte) 0xc3,
|
||||
(byte) 0xe2,(byte) 0x82,(byte) 0xf1,(byte) 0xa3,(byte) 0xc2,(byte) 0xa1,(byte) 0xc1,(byte) 0xe3,(byte) 0xa2,(byte) 0xe1,(byte) 0xff,(byte) 0xff }
|
||||
};
|
||||
|
||||
private void init_tables(int table) {
|
||||
if (table > 2) table = 2;
|
||||
// memset( first_decode, 0, sizeof first_decode);
|
||||
// memset(second_decode, 0, sizeof second_decode);
|
||||
// make_decoder( first_decode, first_tree[table], 0);
|
||||
// make_decoder(second_decode, second_tree[table], 0);
|
||||
|
||||
for (int i = 0; i < first_decode.length; i++) {
|
||||
first_decode[i] = new Decode();
|
||||
}
|
||||
for (int i = 0; i < second_decode.length; i++) {
|
||||
second_decode[i] = new Decode();
|
||||
}
|
||||
|
||||
make_decoder( first_decode, 0, first_tree[table], 0);
|
||||
make_decoder(second_decode, 0, second_tree[table], 0);
|
||||
}
|
||||
|
||||
//#if 0
|
||||
// writebits (int val, int nbits)
|
||||
// {
|
||||
// val <<= 32 - nbits;
|
||||
// while (nbits--) {
|
||||
// putchar(val & 0x80000000 ? '1':'0');
|
||||
// val <<= 1;
|
||||
// }
|
||||
// }
|
||||
//#endif
|
||||
|
||||
/*
|
||||
getbits(-1) initializes the buffer
|
||||
getbits(n) where 0 <= n <= 25 returns an n-bit integer
|
||||
*/
|
||||
private int bitbuf=0;
|
||||
private int vbits=0;
|
||||
|
||||
int getbits(int nbits) throws IOException {
|
||||
int c;
|
||||
|
||||
if (nbits == 0) return 0;
|
||||
|
||||
int ret;
|
||||
|
||||
if (nbits == -1) {
|
||||
ret = bitbuf = vbits = 0;
|
||||
}
|
||||
else {
|
||||
// ret = bitbuf << (32 - vbits) >> (32 - nbits);
|
||||
ret = bitbuf << (32 - vbits) >>> (32 - nbits);
|
||||
vbits -= nbits;
|
||||
}
|
||||
while (vbits < 25) {
|
||||
// c=fgetc(ifp);
|
||||
c=imageInput.readUnsignedByte();
|
||||
bitbuf = (bitbuf << 8) + c;
|
||||
// if (c == 0xff) fgetc(ifp); /* always extra 00 after ff */
|
||||
if (c == 0xff) {
|
||||
imageInput.readUnsignedByte(); // always extra 00 after ff
|
||||
}
|
||||
vbits += 8;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
short[] decode() throws IOException {
|
||||
short[] result = new short[width * height];
|
||||
|
||||
// struct Decode *decode, *dindex;
|
||||
Decode decode;
|
||||
Decode dindex;
|
||||
int i, j, leaf, len, diff, r;
|
||||
long save;
|
||||
int[] diffbuf = new int[64]; // h * w = 8 * 8 for each compressed block
|
||||
int carry=0, column=0;
|
||||
int[] base = new int[2];
|
||||
// unsigned short outbuf[64];
|
||||
short[] outbuf = new short[64];
|
||||
|
||||
int c;
|
||||
|
||||
boolean lowbits = canonHasLowbits();
|
||||
|
||||
init_tables(table);
|
||||
|
||||
// fseek (ifp, 540 + lowbits*height*width/4, SEEK_SET);
|
||||
// imageInput.seek(540 + (lowbits ? 1 : 0) * height * width / 4);
|
||||
imageInput.seek((lowbits ? 1 : 0) * height * width / 4); // NOTE: The original 540 offset is probably CIFF header length: 26 + DecoderTable[2]: 514
|
||||
getbits(-1); /* Prime the bit buffer */
|
||||
|
||||
while (column < width * height) {
|
||||
// memset(diffbuf,0,sizeof diffbuf);
|
||||
Arrays.fill(diffbuf, 0);
|
||||
|
||||
// decode = first_decode;
|
||||
decode = first_decode[0];
|
||||
for (i = 0; i < 64; i++) {
|
||||
|
||||
// for (dindex=decode; dindex->branch[0]; )
|
||||
// dindex = dindex->branch[getbits(1)];
|
||||
for (dindex = decode; dindex.branch[0] != null; ) {
|
||||
dindex = dindex.branch[getbits(1)];
|
||||
}
|
||||
|
||||
// leaf = dindex->leaf;
|
||||
leaf = dindex.leaf;
|
||||
// decode = second_decode;
|
||||
decode = second_decode[0];
|
||||
|
||||
if (leaf == 0 && i != 0) {
|
||||
break;
|
||||
}
|
||||
if (leaf == 0xff) {
|
||||
continue;
|
||||
}
|
||||
|
||||
i += (leaf >> 4);
|
||||
len = leaf & 15;
|
||||
|
||||
if (len == 0) {
|
||||
continue;
|
||||
}
|
||||
diff = getbits(len);
|
||||
if ((diff & (1 << (len - 1))) == 0) {
|
||||
diff -= (1 << len) - 1;
|
||||
}
|
||||
|
||||
if (i < 64) {
|
||||
diffbuf[i] = diff;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Ok ---
|
||||
|
||||
diffbuf[0] += carry;
|
||||
carry = diffbuf[0];
|
||||
|
||||
for (i = 0; i < 64; i++) {
|
||||
if (column++ % width == 0) {
|
||||
base[0] = base[1] = 512;
|
||||
}
|
||||
|
||||
// TODO: The original C code uses unsigned short, so this may overflow differently
|
||||
// outbuf[i] = ( base[i & 1] += diffbuf[i] );
|
||||
|
||||
outbuf[i] = (short) (base[i & 1] += diffbuf[i]);
|
||||
}
|
||||
|
||||
// -- Ok ---
|
||||
|
||||
if (lowbits) {
|
||||
// save = ftell(ifp);
|
||||
save = imageInput.getStreamPosition();
|
||||
// fseek (ifp, (column-64)/4 + 26, SEEK_SET);
|
||||
// imageInput.seek((column - 64) / 4 + 26);
|
||||
imageInput.seek((column - 64) / 4); // Note: The original 26 is CIFF header length (?)
|
||||
for (i = j = 0; j < 64 / 4; j++) {
|
||||
// c = fgetc(ifp);
|
||||
c = imageInput.readUnsignedByte();
|
||||
for (r = 0; r < 8; r += 2) {
|
||||
// TODO: Is this correct? The original C code is on one line, the Java equivalent then throws an AIOOBE...
|
||||
// outbuf[i++] = (outbuf[i] << 2) + ((c >> r) & 3);
|
||||
short sample = (short) ((outbuf[i] << 2) + ((c >> r) & 3));
|
||||
outbuf[i++] = sample;
|
||||
}
|
||||
}
|
||||
// fseek (ifp, save, SEEK_SET);
|
||||
imageInput.seek(save);
|
||||
}
|
||||
|
||||
// fwrite(outbuf,2,64,stdout);
|
||||
System.arraycopy(outbuf, 0, result, column - 64, 64);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+294
@@ -0,0 +1,294 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name "TwelveMonkeys" nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.crw;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFFReader;
|
||||
import com.twelvemonkeys.imageio.plugins.crw.ciff.CIFF;
|
||||
import com.twelvemonkeys.imageio.plugins.crw.ciff.CIFFDirectory;
|
||||
import com.twelvemonkeys.imageio.plugins.crw.ciff.CIFFEntry;
|
||||
import com.twelvemonkeys.imageio.plugins.crw.ciff.CIFFReader;
|
||||
import com.twelvemonkeys.imageio.stream.BufferedImageInputStream;
|
||||
import com.twelvemonkeys.imageio.stream.SubImageInputStream;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Canon CRW RAW ImageReader.
|
||||
* <p/>
|
||||
*
|
||||
* @see <a href="http://cybercom.net/~dcoffin/dcraw/">Decoding raw digital photos in Linux</a>
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author CRW C reference decoder written by Dave Coffin.
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: CRWImageReader.java,v 1.0 07.04.14 21:31 haraldk Exp$
|
||||
*/
|
||||
public final class CRWImageReader extends ImageReaderBase {
|
||||
// TODO: Avoid duped code from TIFFImageReader, create a ExifRAWBaseImageReader something
|
||||
// TODO: Probably a good idea to move some of the getAsShort/Int/Long/Array to TIFF/EXIF metadata module
|
||||
// TODO: Automatic EXIF rotation, if we find a good way to do that for JPEG/EXIF/TIFF and keeping the metadata sane...
|
||||
|
||||
final static boolean DEBUG = true; //"true".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.imageio.plugins.crw.debug"));
|
||||
|
||||
// TODO: Thumbnail may or may not be present
|
||||
|
||||
private CIFFDirectory heap;
|
||||
private boolean hasJPEGPreview;
|
||||
private boolean hasRAWData;
|
||||
|
||||
CRWImageReader(final ImageReaderSpi provider) {
|
||||
super(provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resetMembers() {
|
||||
heap = null;
|
||||
hasJPEGPreview = false;
|
||||
hasRAWData = false;
|
||||
}
|
||||
|
||||
private void readMetadata() throws IOException {
|
||||
if (imageInput == null) {
|
||||
throw new IllegalStateException("input not set");
|
||||
}
|
||||
|
||||
if (heap == null) {
|
||||
heap = new CIFFReader().read(imageInput);
|
||||
|
||||
hasJPEGPreview = heap.getEntryById(CIFF.TAG_JPEG_PREVIEW) != null;
|
||||
hasRAWData = heap.getEntryById(CIFF.TAG_RAW_DATA) != null;
|
||||
|
||||
if (DEBUG) {
|
||||
System.err.println("directory: " + heap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumImages(final boolean allowSearch) throws IOException {
|
||||
readMetadata();
|
||||
|
||||
return (hasJPEGPreview ? 1 : 0) + (hasRAWData ? 1 : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumThumbnails(int imageIndex) throws IOException {
|
||||
readMetadata();
|
||||
checkBounds(imageIndex);
|
||||
|
||||
// TODO: Count thumbnails!
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean readerSupportsThumbnails() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getThumbnailWidth(int imageIndex, int thumbnailIndex) throws IOException {
|
||||
readMetadata();
|
||||
checkBounds(imageIndex);
|
||||
|
||||
// TODO: Need to get from JPEGImageReader (no ImageWidth tag), this is an ok (but lame) implementation for now
|
||||
return super.getThumbnailWidth(imageIndex, thumbnailIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getThumbnailHeight(int imageIndex, int thumbnailIndex) throws IOException {
|
||||
readMetadata();
|
||||
checkBounds(imageIndex);
|
||||
|
||||
// TODO: Need to get from JPEGImageReader (no ImageHeight tag), this is an ok (but lame) implementation for now
|
||||
return super.getThumbnailHeight(imageIndex, thumbnailIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedImage readThumbnail(int imageIndex, int thumbnailIndex) throws IOException {
|
||||
readMetadata();
|
||||
|
||||
if (imageIndex != 0) {
|
||||
throw new IndexOutOfBoundsException("No thumbnail for imageIndex: " + imageIndex);
|
||||
}
|
||||
if (thumbnailIndex >= getNumThumbnails(0)) {
|
||||
throw new IndexOutOfBoundsException("thumbnailIndex out of bounds: " + thumbnailIndex);
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException("readThumbnail");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth(int imageIndex) throws IOException {
|
||||
readMetadata();
|
||||
|
||||
if (imageIndex == 0 && hasJPEGPreview) {
|
||||
return getJPEGPreviewDimension().width;
|
||||
}
|
||||
|
||||
return getRawImageDimension().width;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight(int imageIndex) throws IOException {
|
||||
readMetadata();
|
||||
|
||||
if (imageIndex == 0 && hasJPEGPreview) {
|
||||
return getJPEGPreviewDimension().height;
|
||||
}
|
||||
|
||||
return getRawImageDimension().height;
|
||||
}
|
||||
|
||||
private Dimension getJPEGPreviewDimension() {
|
||||
// TODO: This is incorrect for the G1 sample, which stores the RAW size here...
|
||||
CIFFDirectory imageProperties = heap.getSubDirectory(CIFF.TAG_IMAGE_PROPERTIES);
|
||||
CIFFEntry imageSpec = imageProperties.getEntryById(CIFF.TAG_IMAGE_SPEC);
|
||||
int[] imageSpecValue = (int[]) imageSpec.getValue();
|
||||
|
||||
return new Dimension(imageSpecValue[0], imageSpecValue[1]);
|
||||
}
|
||||
|
||||
private Dimension getRawImageDimension() {
|
||||
CIFFDirectory exifInfo = getExifInfo();
|
||||
CIFFEntry sensorInfo = exifInfo.getEntryById(CIFF.TAG_SENSOR_INFO);
|
||||
|
||||
if (sensorInfo != null) {
|
||||
short[] sensorInfoValue = (short[]) sensorInfo.getValue();
|
||||
|
||||
return new Dimension(sensorInfoValue[1], sensorInfoValue[2]);
|
||||
}
|
||||
|
||||
// PowerShot Pro70 et al, don't have a JPEG preview and contains the dimensions in the ImageSpec tag
|
||||
return getJPEGPreviewDimension();
|
||||
}
|
||||
|
||||
private CIFFDirectory getExifInfo() {
|
||||
CIFFDirectory imageProperties = heap.getSubDirectory(CIFF.TAG_IMAGE_PROPERTIES);
|
||||
return imageProperties.getSubDirectory(CIFF.TAG_EXIF_INFORMATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IOException {
|
||||
readMetadata();
|
||||
|
||||
if (imageIndex == 0 && hasJPEGPreview) {
|
||||
BufferedImage image = readJPEGPreview();
|
||||
return singletonList(ImageTypeSpecifier.createFromRenderedImage(image)).iterator();
|
||||
}
|
||||
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedImage read(int imageIndex, ImageReadParam param) throws IOException {
|
||||
readMetadata();
|
||||
|
||||
if (imageIndex == 0 && hasJPEGPreview) {
|
||||
return readJPEGPreview();
|
||||
}
|
||||
|
||||
return readRAWData();
|
||||
}
|
||||
|
||||
private BufferedImage readJPEGPreview() throws IOException {
|
||||
CIFFEntry jpegPreview = heap.getEntryById(CIFF.TAG_JPEG_PREVIEW);
|
||||
|
||||
imageInput.seek(jpegPreview.offset());
|
||||
|
||||
return ImageIO.read(new SubImageInputStream(imageInput, jpegPreview.length()));
|
||||
}
|
||||
|
||||
private BufferedImage readRAWData() throws IOException {
|
||||
CIFFDirectory exifInfo = getExifInfo();
|
||||
CIFFEntry decoderTable = exifInfo.getEntryById(CIFF.TAG_DECODER_TABLE);
|
||||
int[] decoderTableValue = decoderTable != null ? (int[]) decoderTable.getValue() : null;
|
||||
int table = decoderTableValue != null ? decoderTableValue[0] : 0;
|
||||
long offset = decoderTableValue != null ? decoderTableValue[2] : 0;
|
||||
|
||||
Dimension size = getRawImageDimension();
|
||||
int width = size.width;
|
||||
int height = size.height;
|
||||
|
||||
// TODO: This is probably not the best way to get the bits/pixel, but seems to be correct (when it's there).
|
||||
// However,
|
||||
CIFFEntry whiteSample = exifInfo.getEntryById(CIFF.TAG_WHITE_SAMPLE);
|
||||
short[] whiteSampleValue = whiteSample != null ? (short[]) whiteSample.getValue() : null;
|
||||
short bitsPerSample = whiteSampleValue != null && whiteSample.length() > 5 ? whiteSampleValue[5] : 12;
|
||||
|
||||
CIFFEntry rawData = heap.getEntryById(CIFF.TAG_RAW_DATA);
|
||||
|
||||
imageInput.seek(rawData.offset() + offset);
|
||||
|
||||
ImageInputStream stream = new BufferedImageInputStream(new SubImageInputStream(imageInput, rawData.length()));
|
||||
|
||||
CRWDecoder decoder = new CRWDecoder(stream, width, height, table);
|
||||
short[] data = decoder.decode();
|
||||
|
||||
DataBuffer dataBuffer = new DataBufferUShort(data, data.length);
|
||||
WritableRaster raster = Raster.createInterleavedRaster(dataBuffer, width, height, width, 1, new int[] {0}, null);
|
||||
|
||||
ColorModel cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), new int[]{bitsPerSample}, false, false, Transparency.OPAQUE, dataBuffer.getDataType());
|
||||
|
||||
return new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
CRWImageReader reader = new CRWImageReader(new CRWImageReaderSpi());
|
||||
|
||||
for (String arg : args) {
|
||||
ImageInputStream stream = ImageIO.createImageInputStream(new File(arg));
|
||||
System.err.println("canDecode: " + reader.getOriginatingProvider().canDecodeInput(stream));
|
||||
|
||||
reader.setInput(stream);
|
||||
|
||||
int numImages = reader.getNumImages(true);
|
||||
for (int i = 0; i < numImages; i++) {
|
||||
int numThumbnails = reader.getNumThumbnails(i);
|
||||
for (int n = 0; n < numThumbnails; n++) {
|
||||
showIt(reader.readThumbnail(i, n), arg + " image " + i + " thumbnail " + n);
|
||||
}
|
||||
|
||||
showIt(reader.read(i), arg + " image " + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name "TwelveMonkeys" nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.crw;
|
||||
|
||||
import static java.util.Arrays.copyOfRange;
|
||||
|
||||
import com.twelvemonkeys.imageio.plugins.crw.ciff.CIFF;
|
||||
import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
|
||||
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.spi.ServiceRegistry;
|
||||
|
||||
/**
|
||||
* CRWImageReaderSpi
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: CRWImageReaderSpi.java,v 1.0 07.04.14 21:26 haraldk Exp$
|
||||
*/
|
||||
public final class CRWImageReaderSpi extends ImageReaderSpiBase {
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public CRWImageReaderSpi() {
|
||||
super(new CRWProviderInfo());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRegistration(ServiceRegistry registry, Class<?> category) {
|
||||
// TODO: This code assumes that any TIFFImageReaderSpi is already installed... It may be installed at a later time. :-(
|
||||
// Make sure we're ordered before any TIFF reader
|
||||
Iterator<ImageReaderSpi> spis = registry.getServiceProviders(ImageReaderSpi.class, new ServiceRegistry.Filter() {
|
||||
@Override
|
||||
public boolean filter(Object provider) {
|
||||
return provider instanceof ImageReaderSpi && isTIFFReaderSpi((ImageReaderSpi) provider);
|
||||
}
|
||||
|
||||
private boolean isTIFFReaderSpi(final ImageReaderSpi provider) {
|
||||
String[] formatNames = provider.getFormatNames();
|
||||
for (String formatName : formatNames) {
|
||||
if (formatName.equalsIgnoreCase("TIFF")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}, true);
|
||||
|
||||
while (spis.hasNext()) {
|
||||
ImageReaderSpi spi = spis.next();
|
||||
registry.setOrdering(ImageReaderSpi.class, this, spi);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canDecodeInput(final Object pSource) throws IOException {
|
||||
if (!(pSource instanceof ImageInputStream)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ImageInputStream stream = (ImageInputStream) pSource;
|
||||
|
||||
stream.mark();
|
||||
try {
|
||||
byte[] bom = new byte[2];
|
||||
stream.readFully(bom);
|
||||
|
||||
ByteOrder originalOrder = stream.getByteOrder();
|
||||
|
||||
try {
|
||||
// CIFF byte order mark II (Intel) or MM (Motorola), just like TIFF
|
||||
if (bom[0] == 'I' && bom[1] == 'I') {
|
||||
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
}
|
||||
else if (bom[0] == 'M' && bom[1] == 'M') {
|
||||
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
||||
// CIFF header is always 26 bytes
|
||||
int size = stream.readInt();
|
||||
if (size != CIFF.HEADER_SIZE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// CRW uses type HEAP and subtype CCDR
|
||||
byte[] type = new byte[8];
|
||||
stream.readFully(type);
|
||||
|
||||
if (!Arrays.equals(CIFF.TYPE_HEAP, copyOfRange(type, 0, 4))
|
||||
|| !Arrays.equals(CIFF.SUBTYPE_CCDR, copyOfRange(type, 4, 8))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Version 1.2
|
||||
return stream.readUnsignedInt() == CIFF.VERSION_1_2;
|
||||
}
|
||||
finally {
|
||||
stream.setByteOrder(originalOrder);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
stream.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CRWImageReader createReaderInstance(Object extension) {
|
||||
return new CRWImageReader(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription(Locale locale) {
|
||||
return "Canon RAW (CRW) format Reader";
|
||||
}
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package com.twelvemonkeys.imageio.plugins.crw;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
||||
|
||||
/**
|
||||
* CRWProviderInfo
|
||||
*/
|
||||
final class CRWProviderInfo extends ReaderWriterProviderInfo {
|
||||
CRWProviderInfo() {
|
||||
super(
|
||||
CRWProviderInfo.class,
|
||||
new String[] {"crw", "CRW"},
|
||||
new String[] {"crw"},
|
||||
new String[] {
|
||||
"image/x-canon-raw", // TODO: Look up
|
||||
},
|
||||
"CRWImageReader",
|
||||
new String[] {"CRWImageReaderSpi"},
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
null, null,
|
||||
null, null,
|
||||
true,
|
||||
null, null,
|
||||
null, null
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oleg Ermolaev
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name "TwelveMonkeys" nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.crw;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* For CR2 RAW image.
|
||||
*
|
||||
* @author Oleg Ermolaev Date: 05.05.2018 2:04
|
||||
*/
|
||||
final class Slice {
|
||||
private final int firstWidthCount;
|
||||
private final int firstWidth;
|
||||
private final int lastWidth;
|
||||
|
||||
private Slice(int firstWidthCount, int firstWidth, int lastWidth) {
|
||||
this.firstWidthCount = firstWidthCount;
|
||||
this.firstWidth = firstWidth;
|
||||
this.lastWidth = lastWidth;
|
||||
}
|
||||
|
||||
static Slice createSlice(long[] values) throws IIOException {
|
||||
if (values == null || values.length != 3) {
|
||||
throw new IIOException("Unexpected slices array: " + Arrays.toString(values));
|
||||
}
|
||||
|
||||
long firstWidthCount = values[0];
|
||||
long firstWidth = values[1];
|
||||
long lastWidth = values[2];
|
||||
|
||||
if (!(0 < firstWidthCount && firstWidthCount <= Integer.MAX_VALUE) ||
|
||||
!(0 < firstWidth && firstWidth <= Integer.MAX_VALUE) ||
|
||||
!(0 < lastWidth && lastWidth <= Integer.MAX_VALUE) ||
|
||||
firstWidthCount * firstWidth + lastWidth > Integer.MAX_VALUE) {
|
||||
throw new IIOException("Unexpected slices array: " + Arrays.toString(values));
|
||||
}
|
||||
|
||||
return new Slice((int) firstWidthCount, (int) firstWidth, (int) lastWidth);
|
||||
}
|
||||
|
||||
private int getWidth() {
|
||||
return firstWidthCount * firstWidth + lastWidth;
|
||||
}
|
||||
|
||||
int[] unslice(int[][] data, int componentCount, int height) throws IIOException {
|
||||
final int width = getWidth();
|
||||
final int[] result = new int[width * height];
|
||||
|
||||
for (int componentIndex = 0; componentIndex < componentCount; componentIndex++) {
|
||||
if (result.length != data[componentIndex].length * componentCount) {
|
||||
throw new IIOException(String.format("Invalid array size for component #%d", componentIndex));
|
||||
}
|
||||
}
|
||||
|
||||
int position = 0;
|
||||
int currentWidth = firstWidth / componentCount;
|
||||
|
||||
for (int sliceIndex = 0; sliceIndex < firstWidthCount + 1; ++sliceIndex) {
|
||||
if (sliceIndex == firstWidthCount) {
|
||||
currentWidth = lastWidth / componentCount;
|
||||
}
|
||||
|
||||
final int sliceOffset = sliceIndex * firstWidth;
|
||||
for (int y = 0; y < height; ++y) {
|
||||
final int yOffset = y * width;
|
||||
|
||||
for (int x = 0; x < currentWidth; ++x) {
|
||||
final int xOffset = x * componentCount;
|
||||
|
||||
for (int componentIndex = 0; componentIndex < componentCount; componentIndex++) {
|
||||
result[sliceOffset + yOffset + xOffset + componentIndex] = data[componentIndex][position];
|
||||
}
|
||||
|
||||
position++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
package com.twelvemonkeys.imageio.plugins.crw.ciff;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* CIFF
|
||||
*
|
||||
* @see <a href="http://xyrion.org/ciff/CIFFspecV1R04.pdf">CIFF: Specification on Image Data File</a>
|
||||
*/
|
||||
public interface CIFF {
|
||||
|
||||
int HEADER_SIZE = 26;
|
||||
int VERSION_1_2 = 0x00010002;
|
||||
|
||||
byte[] TYPE_HEAP = "HEAP".getBytes(StandardCharsets.US_ASCII);
|
||||
|
||||
// According to CIFF spec, other subtypes are "JPGM", "TIFP" and "ARCH".
|
||||
byte[] SUBTYPE_ARCH = "ARCH".getBytes(StandardCharsets.US_ASCII);
|
||||
byte[] SUBTYPE_CCDR = "CCDR".getBytes(StandardCharsets.US_ASCII);
|
||||
byte[] SUBTYPE_JPGM = "JPGM".getBytes(StandardCharsets.US_ASCII);
|
||||
byte[] SUBTYPE_TIFP = "TIFP".getBytes(StandardCharsets.US_ASCII);
|
||||
|
||||
int STORAGE_HEAP = 0x0000; // kStg_InHeapSpace
|
||||
int STORAGE_RECORD = 0x4000; // kStg_InRecordEntry
|
||||
|
||||
int DATA_TYPE_BYTE = 0x0000;
|
||||
int DATA_TYPE_ASCII = 0x0800;
|
||||
int DATA_TYPE_WORD = 0x1000;
|
||||
int DATA_TYPE_DWORD = 0x1800;
|
||||
int DATA_TYPE_UNDEFINED = 0x2000; // kDT_BYTE2
|
||||
|
||||
int DATA_TYPE_HEAP_1 = 0x2800; // kDT_HeapTypeProperty1
|
||||
int DATA_TYPE_HEAP_2 = 0x3000; // kDT_HeapTypeProperty2
|
||||
|
||||
// From Spec
|
||||
int TAG_WILDCARD = 0xffff;
|
||||
int TAG_NULL = 0x0000; // null record
|
||||
int TAG_FREE = 0x0001; // free record
|
||||
int TAG_EX_USED = 0x0002; // special type for implementation purpose
|
||||
|
||||
int TAG_DESCRIPTION = DATA_TYPE_ASCII | 0x0005;
|
||||
int TAG_MODEL_NAME = DATA_TYPE_ASCII | 0x000a;
|
||||
int TAG_FIRMWARE_VERSION = DATA_TYPE_ASCII | 0x000b;
|
||||
int TAG_COMPONENT_VERSION = DATA_TYPE_ASCII | 0x000c;
|
||||
int TAG_ROM_OPERATION_MODE = DATA_TYPE_ASCII | 0x000d;
|
||||
int TAG_OWNER_NAME = DATA_TYPE_ASCII | 0x0010;
|
||||
int TAG_IMAGE_FILE_NAME = DATA_TYPE_ASCII | 0x0016;
|
||||
int TAG_THUMBNAIL_FILE_NAME = DATA_TYPE_ASCII | 0x0017;
|
||||
int TAG_TARGET_IMAGE_TYPE = DATA_TYPE_WORD | 0x000a;
|
||||
int TAG_SR_RELEASE_METHOD = DATA_TYPE_WORD | 0x0010;
|
||||
int TAG_SR_RELEASE_TIMING = DATA_TYPE_WORD | 0x0011;
|
||||
int TAG_RELEASE_SETTING = DATA_TYPE_WORD | 0x0016;
|
||||
int TAG_BODY_SENSITIVITY = DATA_TYPE_WORD | 0x001c;
|
||||
int TAG_IMAGE_FORMAT = DATA_TYPE_DWORD | 0x0003;
|
||||
int TAG_RECORD_ID = DATA_TYPE_DWORD | 0x0004;
|
||||
int TAG_SELF_TIMER_TIME = DATA_TYPE_DWORD | 0x0006;
|
||||
int TAG_SR_TARGET_DISTANCE_SETTING = DATA_TYPE_DWORD | 0x0007;
|
||||
int TAG_BODY_ID = DATA_TYPE_DWORD | 0x000b;
|
||||
int TAG_CAPTURED_TIME = DATA_TYPE_DWORD | 0x000e;
|
||||
int TAG_IMAGE_SPEC = DATA_TYPE_DWORD | 0x0010;
|
||||
int TAG_SR_EF = DATA_TYPE_DWORD | 0x0013;
|
||||
int TAG_MI_EV = DATA_TYPE_DWORD | 0x0014;
|
||||
int TAG_SERIAL_NUMBER = DATA_TYPE_DWORD | 0x0017;
|
||||
int TAG_SR_EXPOSURE = DATA_TYPE_DWORD | 0x0018;
|
||||
int TAG_CAMERA_OBJECT = 0x0007 | DATA_TYPE_HEAP_1;
|
||||
int TAG_SHOOTING_RECORD = 0x0002 | DATA_TYPE_HEAP_2;
|
||||
int TAG_MEASURED_INFO = 0x0003 | DATA_TYPE_HEAP_2;
|
||||
int TAG_CAMERA_SPECIFICATiON = 0x0004 | DATA_TYPE_HEAP_2; // kTC_CameraSpecificaiton
|
||||
|
||||
// From Phil Harvey's ExifTool (https://sno.phy.queensu.ca/~phil/exiftool/canon_raw.html)
|
||||
|
||||
// 0x0006 - 0x300b - 8 -
|
||||
int TAG_CANON_COLOR_INFO1 = DATA_TYPE_BYTE | 0x0032; // - Subdir: 0x300b, Exif: -
|
||||
// 0x0036 - 0x300b ? varies -
|
||||
// 0x003f - 0x300b ? 5120 -
|
||||
// 0x0040 - 0x300b ? 256 -
|
||||
// 0x0041 - 0x300b ? 256 -
|
||||
|
||||
// ASCII Strings
|
||||
// int TAG_CANON_FILE_DESCRIPTION = 0x0805; // - Subdir: 0x2804, Exif: -
|
||||
// int TAG_USER_COMMENT = DATA_TYPE_ASCII | 0x0005; // - Subdir: 0x300a, Exif: -
|
||||
// int TAG_CANON_RAW_MAKE_MODEL = DATA_TYPE_ASCII | 0x080a; // - Subdir: 0x2807, Exif: -
|
||||
// int TAG_CANON_FIRMWARE_VERSION = DATA_TYPE_ASCII | 0x080b; // 32 Firmware version. eg) "Firmware Version 1.1.1" - Subdir: 0x3004, Exif: 0x07
|
||||
// int TAG_COMPONENT_VERSION = 0x080c; // ?
|
||||
// int TAG_ROM_OPERATION_MODE = 0x080d; // 4 eg) The string "USA" for 300D's sold in North America - Subdir: 0x3004, Exif: -
|
||||
// int TAG_OWNER_NAME = 0x0810; // 32 Owner's name. eg) "Phil Harvey" - Subdir: 0x2807, Exif: 0x09
|
||||
int TAG_CANON_IMAGE_TYPE = DATA_TYPE_ASCII | 0x0815; // 32 Type of file. eg) "CRW:EOS DIGITAL REBEL CMOS RAW" - Subdir: 0x2804, Exif: 0x06
|
||||
// int TAG_ORIGINAL_FILE_NAME = 0x0816; // 32 Original file name. eg) "CRW_1834.CRW" - Subdir: 0x300a, Exif: -
|
||||
// int TAG_THUMBNAIL_FILE_NAME = 0x0817; // 32 Thumbnail file name. eg) "CRW_1834.THM" - Subdir: 0x300a, Exif: -
|
||||
|
||||
// SHORT (2-Byte Alignmnt)
|
||||
// int TAG_TARGET_IMAGE_TYPE = 0x100a; // 2 0=real-world subject, 1=written document - Subdir: 0x300a, Exif: -
|
||||
// int TAG_SHUTTER_RELEASE_METHOD = 0x1010; // 2 0=single shot, 1=continuous shooting - Subdir: 0x3002, Exif: -
|
||||
// int TAG_SHUTTER_RELEASE_TIMING = 0x1011; // 2 0=priority on shutter, 1=priority on focus - Subdir: 0x3002, Exif: -
|
||||
// 0x1014 - 0x3002 - 8 -
|
||||
// int TAG_RELEASE_SETTING = 0x1016; // 2 - - Subdir: 0x3002, Exif: -
|
||||
int TAG_BASE_ISO = DATA_TYPE_WORD | 0x101c; // 2 The camera body's base ISO sensitivity - Subdir: 0x3004, Exif: -
|
||||
// 0x1026 - 0x300a - 6 -
|
||||
int TAG_CANON_FLASH_INFO = DATA_TYPE_WORD | 0x1028; // 8 Unknown information, flash related - Subdir: 0x300b, Exif: 0x03
|
||||
int TAG_FOCAL_LENGTH = DATA_TYPE_WORD | 0x1029; // 8 Four 16 bit integers: 0) unknown, 1) focal length in mm, 2-3) sensor width and height in units of 1/1000 inch - Subdir: 0x300b, Exif: 0x02
|
||||
int TAG_CANON_SHOT_INFO = DATA_TYPE_WORD | 0x102a; // varies Data block giving shot information - Subdir: 0x300b, Exif: 0x04
|
||||
int TAG_CANON_COLOR_INFO2 = DATA_TYPE_WORD | 0x102c; // 256 Data block of color information (format unknown) - Subdir: 0x300b, Exif: -
|
||||
int TAG_CANON_CAMERA_SETTINGS = DATA_TYPE_WORD | 0x102d; // varies Data block giving camera settings - Subdir: 0x300b, Exif: 0x01
|
||||
int TAG_WHITE_SAMPLE = DATA_TYPE_WORD | 0x1030; // 102 or 118 White sample information with encrypted 8x8 sample data - Subdir: 0x300b, Exif: -
|
||||
int TAG_SENSOR_INFO = DATA_TYPE_WORD | 0x1031; // 34 Sensor size and resolution information - Subdir: 0x300b, Exif: -
|
||||
int TAG_CANON_CUSTOM_FUNCTIONS = DATA_TYPE_WORD | 0x1033; // varies Data block giving Canon custom settings - Subdir: 0x300b, Exif: 0x0f
|
||||
int TAG_CANON_AF_INFO = DATA_TYPE_WORD | 0x1038; // varies Data block giving AF-specific information - Subdir: 0x300b, Exif: 0x12
|
||||
// 0x1039 0x13 0x300b ? 8 -
|
||||
// 0x103c - 0x300b ? 156 -
|
||||
// 0x107f - 0x300b - varies -
|
||||
int TAG_CANON_FILE_INFO = DATA_TYPE_WORD | 0x1093; // 18 Data block giving file-specific information - Subdir: 0x300b, Exif: 0x93
|
||||
// 0x10a8 0xa8 0x300b ? 20 -
|
||||
int TAG_COLOR_BALANCE = DATA_TYPE_WORD | 0x10a9; // 82 Table of 16-bit integers. The first integer (like many other data blocks) is the number of bytes in the record. This is followed by red, green1, green2 and blue levels for WhiteBalance settings: auto, daylight, shade, cloudy, tungsten, fluorescent, flash, custom and kelvin. The final 4 entries appear to be some sort of baseline red, green1, green2 and blue levels. - Subdir: 0x300b, Exif: 0xa9
|
||||
// 0x10aa 0xaa 0x300b ? 10 -
|
||||
// 0x10ad - 0x300b ? 62 -
|
||||
int TAG_COLOR_TEMPERATURE = DATA_TYPE_WORD | 0x10ae; // 2 16-bit integer giving the color temperature - Subdir: 0x300b, Exif: 0xae
|
||||
// 0x10af - 0x300b ? 2 -
|
||||
int TAG_COLOR_SPACE = DATA_TYPE_WORD | 0x10b4; // 2 16-bit integer specifying the color space (1=sRGB, 2=Adobe RGB, 0xffff=uncalibrated) - Subdir: 0x300b, Exif: 0xb4
|
||||
int TAG_RAW_JPEG_INFO = DATA_TYPE_WORD | 0x10b5; // 10 Data block giving embedded JPG information - Subdir: 0x300b, Exif: 0xb5
|
||||
// 0x10c0 0xc0 0x300b ? 26 -
|
||||
// 0x10c1 0xc1 0x300b ? 26 -
|
||||
// 0x10c2 - 0x300b ? 884 -
|
||||
|
||||
// LONG (4-Byte Alignment)
|
||||
// int TAG_IMAGE_FORMAT = 0x1803; // 8 32-bit integer specifying image format (0x20001 for CRW), followed by 32-bit float giving target compression ratio - Subdir: 0x300a, Exif: -
|
||||
// int TAG_RECORD_ID = 0x1804; // 4 The number of pictures taken since the camera was manufactured - Subdir: 0x300a, Exif: -
|
||||
// 0x1805 - 0x3002 - 8 -
|
||||
// int TAG_SELF_TIMER_TIME = 0x1806; // 4 32-bit integer giving self-timer time in milliseconds - Subdir: 0x3002, Exif: -
|
||||
// int TAG_TARGET_DISTANCE_SETTING = 0x1807; // 4 32-bit float giving target distance in mm - Subdir: 0x3002, Exif: -
|
||||
// int TAG_SERIAL_NUMBER = 0x180b; // 4 The camera body number for EOS models. eg) 00560012345 - Subdir: 0x3004, Exif: 0x0c
|
||||
int TAG_TIME_STAMP = DATA_TYPE_DWORD | 0x180e; // 12 32-bit integer giving the time in seconds when the picture was taken, followed by a 32-bit timezone in seconds - Subdir: 0x300a, Exif: -
|
||||
int TAG_IMAGE_INFO = DATA_TYPE_DWORD | 0x1810; // 28 Data block containing image information, including rotation - Subdir: 0x300a, Exif: -
|
||||
// 0x1812 - 0x3004 - 40 -
|
||||
int TAG_FLASH_INFO = DATA_TYPE_DWORD | 0x1813; // 8 Two 32-bit floats: The flash guide number and the flash threshold - Subdir: 0x3002, Exif: -
|
||||
int TAG_MEASURED_EV = DATA_TYPE_DWORD | 0x1814; // 4 32-bit float giving the measured EV - Subdir: 0x3003, Exif: -
|
||||
int TAG_FILE_NUMBER = DATA_TYPE_DWORD | 0x1817; // 4 32-bit integer giving the number of this file. eg) 1181834 - Subdir: 0x300a, Exif: 0x08
|
||||
// int TAG_EXPOSURE_INFO = DATA_TYPE_DWORD | 0x1818; // 12 Three 32-bit floats: Exposure compensation, Tv, Av - Subdir: 0x3002, Exif: -
|
||||
// 0x1819 - 0x300b - 64 -
|
||||
int TAG_CANON_MODEL_ID = DATA_TYPE_DWORD | 0x1834; // 4 Unsigned 32-bit integer giving unique model ID - Subdir: 0x300b, Exif: 0x10
|
||||
int TAG_DECODER_TABLE = DATA_TYPE_DWORD | 0x1835; // 16 RAW decoder table information - Subdir: 0x300b, Exif: -
|
||||
int TAG_SERIAL_NUMBER_FORMAT = DATA_TYPE_DWORD | 0x183b; // 4 32-bit integer (0x90000000=format 1, 0xa0000000=format 2) - Subdir: 0x300b, Exif: 0x15
|
||||
|
||||
// UNDEFINED (Mixed Data Records)
|
||||
int TAG_RAW_DATA = DATA_TYPE_UNDEFINED | 0x2005; // The raw data itself (the bulk of the CRW file) - Subdir: root
|
||||
int TAG_JPEG_PREVIEW = DATA_TYPE_UNDEFINED | 0x2007; // The embedded JPEG image (2048x1360 pixels for the 300D with Canon firmware) - Subdir: root
|
||||
int TAG_THUMBNAIL = DATA_TYPE_UNDEFINED | 0x2008; // Thumbnail image (JPEG, 160x120 pixels) - Subdir: root
|
||||
|
||||
// SubDirectory Blocks
|
||||
int TAG_IMAGE_DESCRIPTION = DATA_TYPE_HEAP_1 | 0x2804; // The image description subdirectory - Subdir: 0x300a
|
||||
// int TAG_CAMERA_OBJECT = 0x2807; // The camera object subdirectory - Subdir: 0x300a
|
||||
// int TAG_SHOOTING_RECORD = 0x3002; // The shooting record subdirectory - Subdir: 0x300a
|
||||
// int TAG_MEASURED_INFO = 0x3003; // The measured information subdirectory - Subdir: 0x300a
|
||||
int TAG_CAMERA_SPECIFICATION = DATA_TYPE_HEAP_2 | 0x3004; // The camera specification subdirectory - Subdir: 0x2807
|
||||
|
||||
int TAG_IMAGE_PROPERTIES = DATA_TYPE_HEAP_2 | 0x300a; // The main subdirectory containing all meta information - Subdir: root
|
||||
int TAG_EXIF_INFORMATION = DATA_TYPE_HEAP_2 | 0x300b; // The subdirectory containing most of the JPEG/TIFF Exif information - Subdir: 0x300a
|
||||
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.twelvemonkeys.imageio.plugins.crw.ciff;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.twelvemonkeys.imageio.metadata.AbstractDirectory;
|
||||
|
||||
/**
|
||||
* CIFFDirectory
|
||||
*/
|
||||
public final class CIFFDirectory extends AbstractDirectory {
|
||||
|
||||
CIFFDirectory(Collection<CIFFEntry> entries) {
|
||||
super(entries);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CIFFEntry getEntryById(Object identifier) {
|
||||
return (CIFFEntry) super.getEntryById(identifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CIFFEntry getEntryByFieldName(String fieldName) {
|
||||
return (CIFFEntry) super.getEntryByFieldName(fieldName);
|
||||
}
|
||||
|
||||
public CIFFDirectory getSubDirectory(int tagId) {
|
||||
CIFFEntry entry = getEntryById(tagId);
|
||||
return entry == null ? null : (CIFFDirectory) entry.getValue();
|
||||
}
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
package com.twelvemonkeys.imageio.plugins.crw.ciff;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import com.twelvemonkeys.imageio.metadata.AbstractEntry;
|
||||
import com.twelvemonkeys.lang.StringUtil;
|
||||
|
||||
/**
|
||||
* CIFFEntry
|
||||
*/
|
||||
public final class CIFFEntry extends AbstractEntry {
|
||||
|
||||
private final long offset;
|
||||
private final long length;
|
||||
|
||||
/**
|
||||
* Creates a CIFFEntry for "in-record" storage.
|
||||
*
|
||||
* @param identifier
|
||||
* @param value
|
||||
*/
|
||||
CIFFEntry(int identifier, Object value) {
|
||||
super(identifier, value);
|
||||
this.offset = -1;
|
||||
this.length = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a CIFFEntry for "in-heap" storage.
|
||||
*
|
||||
* @param identifier
|
||||
* @param value
|
||||
*/
|
||||
CIFFEntry(int identifier, Object value, long offset, long length) {
|
||||
super(identifier, value);
|
||||
this.offset = offset;
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
int tagId() {
|
||||
return (int) getIdentifier();
|
||||
}
|
||||
|
||||
boolean isHeapStorage() {
|
||||
return offset != -1 && length != -1;
|
||||
}
|
||||
|
||||
public long offset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
public long length() {
|
||||
return length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFieldName() {
|
||||
switch ((Integer) getIdentifier()) {
|
||||
case CIFF.TAG_RAW_DATA:
|
||||
return "RawData";
|
||||
case CIFF.TAG_JPEG_PREVIEW:
|
||||
return "JPEGPreview";
|
||||
case CIFF.TAG_THUMBNAIL:
|
||||
return "Thumbnail";
|
||||
case CIFF.TAG_IMAGE_PROPERTIES:
|
||||
return "ImageProperties";
|
||||
case CIFF.TAG_EXIF_INFORMATION:
|
||||
return "ExifInformation";
|
||||
default:
|
||||
Field[] fields = CIFF.class.getFields();
|
||||
|
||||
for (Field field : fields) {
|
||||
try {
|
||||
if (field.getType() == Integer.TYPE && field.getName().startsWith("TAG_")) {
|
||||
if (field.get(null).equals(getIdentifier())) {
|
||||
return StringUtil.lispToCamel(field.getName().substring(4).replace("_", "-").toLowerCase(), true);
|
||||
}
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
// Should never happen, but in case, abort
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected String getNativeIdentifier() {
|
||||
return String.format("0x%04x", (Integer) getIdentifier());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeName() {
|
||||
int dataType = tagId() & 0x3800;
|
||||
|
||||
switch (dataType) {
|
||||
case CIFF.DATA_TYPE_BYTE:
|
||||
return "BYTE";
|
||||
case CIFF.DATA_TYPE_ASCII:
|
||||
return "ASCII";
|
||||
case CIFF.DATA_TYPE_WORD:
|
||||
return "SHORT";
|
||||
case CIFF.DATA_TYPE_DWORD:
|
||||
return "LONG";
|
||||
case CIFF.DATA_TYPE_UNDEFINED:
|
||||
return "UNDEFINED";
|
||||
case CIFF.DATA_TYPE_HEAP_1:
|
||||
case CIFF.DATA_TYPE_HEAP_2:
|
||||
return super.getTypeName();
|
||||
default:
|
||||
throw new IllegalStateException(String.format("Unsupported data type: 0x%04x", dataType));
|
||||
}
|
||||
}
|
||||
}
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
package com.twelvemonkeys.imageio.plugins.crw.ciff;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
|
||||
import com.twelvemonkeys.imageio.metadata.MetadataReader;
|
||||
|
||||
/**
|
||||
* CIFFReader
|
||||
*/
|
||||
public final class CIFFReader extends MetadataReader {
|
||||
|
||||
@Override
|
||||
public CIFFDirectory read(ImageInputStream input) throws IOException {
|
||||
long start = readCIFFHeader(input);
|
||||
|
||||
// Spec: "No information regarding the length of the heap is given within the actual heap data structure itself"
|
||||
return readHeap(input, start, input.length() - start); // TODO: If length is unknown, we'll have to search for it...
|
||||
}
|
||||
|
||||
private int readCIFFHeader(ImageInputStream input) throws IOException {
|
||||
byte[] bom = new byte[2];
|
||||
input.readFully(bom);
|
||||
|
||||
// CIFF byte order mark II (Intel) or MM (Motorola), just like TIFF
|
||||
if (bom[0] == 'I' && bom[1] == 'I') {
|
||||
input.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
}
|
||||
else if (bom[0] == 'M' && bom[1] == 'M') {
|
||||
input.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
}
|
||||
else {
|
||||
throw new IIOException("No CIFF byte order mark found, expected 'II' or 'MM'");
|
||||
}
|
||||
|
||||
int size = input.readInt();
|
||||
if (size != CIFF.HEADER_SIZE) {
|
||||
// TODO: Other sizes?
|
||||
throw new IIOException(String.format("Unexpected CIFF header size, expected %d: %d ", CIFF.HEADER_SIZE, size));
|
||||
}
|
||||
|
||||
byte[] typeInfo = new byte[8];
|
||||
input.readFully(typeInfo);
|
||||
|
||||
byte[] type = Arrays.copyOfRange(typeInfo, 0, 4);
|
||||
if (!Arrays.equals(CIFF.TYPE_HEAP, type)) {
|
||||
throw new IIOException(String.format("Unexpected CIFF type, expected 'HEAP': '%s'", new String(type, StandardCharsets.US_ASCII)));
|
||||
}
|
||||
|
||||
byte[] subtype = Arrays.copyOfRange(typeInfo, 4, 8);
|
||||
if (!(Arrays.equals(CIFF.SUBTYPE_ARCH, subtype) || Arrays.equals(CIFF.SUBTYPE_CCDR, subtype)
|
||||
|| Arrays.equals(CIFF.SUBTYPE_JPGM, subtype) ||Arrays.equals(CIFF.SUBTYPE_TIFP, subtype))) {
|
||||
throw new IIOException(String.format("Unsupported CIFF subtype, expected 'ARCH', 'CCDR', 'JPGM' or 'TIFP': '%s'",
|
||||
new String(subtype, StandardCharsets.US_ASCII)));
|
||||
}
|
||||
|
||||
// Version 1.2
|
||||
long version = input.readUnsignedInt();
|
||||
if (version != CIFF.VERSION_1_2) {
|
||||
throw new IIOException(String.format("Unsupported CIFF version, expected 1.2: %d.%d", version >> 16, version & 0xffff));
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
private CIFFDirectory readHeap(ImageInputStream input, long heapOffset, long heapLength) throws IOException {
|
||||
input.seek(heapOffset + heapLength - 4);
|
||||
long offsetTableOffset = input.readUnsignedInt();
|
||||
|
||||
return readOffsetTable(input, heapOffset, offsetTableOffset + heapOffset);
|
||||
}
|
||||
|
||||
private CIFFDirectory readOffsetTable(ImageInputStream input, long heapOffset, long tableOffset) throws IOException {
|
||||
input.seek(tableOffset);
|
||||
|
||||
// DC_UINT16 numRecords;/* the number tblArray elements */
|
||||
// DC_RECORD_ENTRY tblArray[1];/* Array of the record entries */
|
||||
int count = input.readUnsignedShort(); // 0 entries is allowed
|
||||
CIFFEntry[] entries = new CIFFEntry[count];
|
||||
|
||||
// TYPECODE typeCode;/* type code of the record */
|
||||
// DC_UINT32 length;/* record length */
|
||||
// DC_UINT32 offset;/* offset of the record in the heap*/
|
||||
for (int i = 0; i < count; i++) {
|
||||
short typeCode = input.readShort();
|
||||
|
||||
long entryLength = input.readUnsignedInt();
|
||||
long entryOffset = input.readUnsignedInt();
|
||||
|
||||
int recordType = typeCode & 0xc000;
|
||||
int dataType = typeCode & 0x3800;
|
||||
int idCode = typeCode & 0x7ff;
|
||||
|
||||
// typeIdCode = dataType + idCode
|
||||
int typeIdCode = typeCode & 0x3fff;
|
||||
|
||||
|
||||
if (recordType == CIFF.STORAGE_RECORD) {
|
||||
Object value = null;
|
||||
// TODO: Even for these records, we need to know the data structure for each tag... :-P
|
||||
switch (dataType) {
|
||||
case CIFF.DATA_TYPE_BYTE:
|
||||
value = (byte) entryLength & 0xff;
|
||||
break;
|
||||
case CIFF.DATA_TYPE_ASCII:
|
||||
ByteBuffer buffer = ByteBuffer.allocate(8);
|
||||
buffer.putInt((int) entryLength);
|
||||
buffer.putInt((int) entryOffset);
|
||||
|
||||
value = toNullTerminatedStrings(buffer.array());
|
||||
break;
|
||||
case CIFF.DATA_TYPE_WORD:
|
||||
value = (short) entryLength & 0xffff;
|
||||
break;
|
||||
case CIFF.DATA_TYPE_DWORD:
|
||||
value = (int) entryLength;
|
||||
break;
|
||||
case CIFF.DATA_TYPE_UNDEFINED:
|
||||
value = entryLength << 32L | entryOffset;
|
||||
break;
|
||||
}
|
||||
|
||||
entries[i] = new CIFFEntry(typeIdCode, value);
|
||||
}
|
||||
else {
|
||||
entries[i] = new CIFFEntry(typeIdCode, new long[] {heapOffset + entryOffset, entryLength}, heapOffset + entryOffset, entryLength);
|
||||
}
|
||||
}
|
||||
|
||||
// Fill inn sub entries
|
||||
input.mark();
|
||||
try {
|
||||
for (int i = 0; i < count; i++) {
|
||||
CIFFEntry entry = entries[i];
|
||||
int dataType = entry.tagId() & 0x3800;
|
||||
|
||||
Object value;
|
||||
if (entry.isHeapStorage()) {
|
||||
switch (dataType) {
|
||||
case CIFF.DATA_TYPE_HEAP_1:
|
||||
case CIFF.DATA_TYPE_HEAP_2:
|
||||
value = readHeap(input, entry.offset(), entry.length());
|
||||
break;
|
||||
|
||||
case CIFF.DATA_TYPE_BYTE:
|
||||
case CIFF.DATA_TYPE_UNDEFINED:
|
||||
case CIFF.DATA_TYPE_ASCII:
|
||||
byte[] bytes = new byte[(int) entry.length()];
|
||||
input.seek(entry.offset());
|
||||
input.readFully(bytes);
|
||||
value = dataType == CIFF.DATA_TYPE_ASCII ? toNullTerminatedStrings(bytes) : bytes;
|
||||
break;
|
||||
|
||||
case CIFF.DATA_TYPE_WORD:
|
||||
short[] shorts = new short[(int) (entry.length() / 2)];
|
||||
input.seek(entry.offset());
|
||||
input.readFully(shorts, 0, shorts.length);
|
||||
value = shorts;
|
||||
break;
|
||||
|
||||
case CIFF.DATA_TYPE_DWORD:
|
||||
int[] ints = new int[(int) (entry.length() / 4)];
|
||||
input.seek(entry.offset());
|
||||
input.readFully(ints, 0, ints.length);
|
||||
value = ints;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new IIOException(String.format("Unsupported data type: 0x%04x", dataType));
|
||||
}
|
||||
|
||||
entries[i] = new CIFFEntry(entry.tagId(), value, entry.offset(), entry.length());
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
input.reset();
|
||||
}
|
||||
|
||||
return new CIFFDirectory(asList(entries));
|
||||
}
|
||||
|
||||
private String[] toNullTerminatedStrings(byte[] buffer) {
|
||||
int len = buffer.length;
|
||||
|
||||
while (len > 0 && buffer[len - 1] == 0) {
|
||||
len--;
|
||||
}
|
||||
|
||||
return new String(buffer, 0, len, StandardCharsets.US_ASCII).split("\u0000");
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
com.twelvemonkeys.imageio.plugins.crw.CRWImageReaderSpi
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name "TwelveMonkeys" nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.crw;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import java.awt.*;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* CRWImageReaderTest
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: CRWImageReaderTest.java,v 1.0 07.04.14 21:52 haraldk Exp$
|
||||
*/
|
||||
@Ignore
|
||||
public class CRWImageReaderTest extends ImageReaderAbstractTest<CRWImageReader> {
|
||||
@Override
|
||||
protected List<TestData> getTestData() {
|
||||
return Arrays.asList(
|
||||
new TestData(getClassLoaderResource("/crw/RAW_CANON_G1.CRW"), new Dimension(640, 480)), // Canon G1
|
||||
new TestData(getClassLoaderResource("/crw/RAW_CANON_300D.CRW"), new Dimension(3888, 2592)) // Canon EOS 300D
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ImageReaderSpi createProvider() {
|
||||
return new CRWImageReaderSpi();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getFormatNames() {
|
||||
return Arrays.asList("crw");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getSuffixes() {
|
||||
return Arrays.asList("crw");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getMIMETypes() {
|
||||
return Arrays.asList("image/x-canon-raw");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Known issue: Subsampled reading not supported")
|
||||
@Override
|
||||
public void testReadWithSubsampleParamPixels() throws IOException {
|
||||
super.testReadWithSubsampleParamPixels();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Known issue: Source region reading not supported")
|
||||
@Override
|
||||
public void testReadWithSourceRegionParamEqualImage() throws IOException {
|
||||
super.testReadWithSourceRegionParamEqualImage();
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2014, Harald Kuhr
|
||||
~ All rights reserved.
|
||||
~
|
||||
~ Redistribution and use in source and binary forms, with or without
|
||||
~ modification, are permitted provided that the following conditions are met:
|
||||
~ * Redistributions of source code must retain the above copyright
|
||||
~ notice, this list of conditions and the following disclaimer.
|
||||
~ * Redistributions in binary form must reproduce the above copyright
|
||||
~ notice, this list of conditions and the following disclaimer in the
|
||||
~ documentation and/or other materials provided with the distribution.
|
||||
~ * Neither the name "TwelveMonkeys" nor the
|
||||
~ names of its contributors may be used to endorse or promote products
|
||||
~ derived from this software without specific prior written permission.
|
||||
~
|
||||
~ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
~ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
~ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
~ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
~ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
~ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
~ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
~ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
~ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
~ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
~ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>imageio</artifactId>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>imageio-dng</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: DNG plugin</name>
|
||||
<description>ImageIO plugin for Adobe Digital Negative and TIFF/EP (DNG).</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
<classifier>tests</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-metadata</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<version>${project.version}</version>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-jpeg</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,127 @@
|
||||
package com.twelvemonkeys.imageio.plugins.dng;
|
||||
|
||||
/**
|
||||
* DNG
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: DNG.java,v 1.0 03.10.14 10:49 haraldk Exp$
|
||||
*/
|
||||
interface DNG {
|
||||
// TODO: Some (all?) of these tags are defined by TIFF/EP, should we reflect that in package/class names?
|
||||
|
||||
/** CFA (Color Filter Array). */
|
||||
int PHOTOMETRIC_CFA = 32803;
|
||||
/** LinearRaw. */
|
||||
int PHOTOMETRIC_LINEAR_RAW = 34892;
|
||||
|
||||
/**
|
||||
* Lossy JPEG.
|
||||
* <p/>
|
||||
* Lossy JPEG (34892) is allowed for IFDs that use PhotometricInterpretation = 34892
|
||||
* (LinearRaw) and 8-bit integer data. This new compression code is required to let the DNG
|
||||
* reader know to use a lossy JPEG decoder rather than a lossless JPEG decoder for this
|
||||
* combination of PhotometricInterpretation and BitsPerSample.
|
||||
*/
|
||||
int COMPRESSION_LOSSY_JPEG = 34892;
|
||||
|
||||
/**
|
||||
* CFARepeatPatternDim
|
||||
* <p/>
|
||||
* This tag encodes the number of pixels horizontally and vertically that are needed to uniquely define the repeat
|
||||
* pattern of the color filter array (CFA) pattern used in the color image sensor. It is mandatory when
|
||||
* PhotometricInterpretation = 32803, and there are no defaults allowed. It is optional when
|
||||
* PhotometricInterpretation = 2 or 6 and SensingMethod = 2, where it can be used to indicate the original sensor
|
||||
* sampling positions.
|
||||
*/
|
||||
int TAG_CFA_REPEAT_PATTERN_DIM = 33421;
|
||||
/**
|
||||
* Indicates the color filter array (CFA) geometric pattern of the image sensor
|
||||
* when a one-chip color area sensor is used.
|
||||
* NOTE: This tag (defined in TIFF/EP) is different from the CFAPattern defined in normal TIFF.
|
||||
*/
|
||||
int TAG_CFA_PATTERN = 33422;
|
||||
|
||||
/** Indicates the image sensor type on the camera or input device. */
|
||||
int TAG_SENSING_METHOD = 37399;
|
||||
|
||||
// From http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/cfapattern.html
|
||||
byte CFA_PATTERN_RED = 0;
|
||||
byte CFA_PATTERN_GREEN = 1;
|
||||
byte CFA_PATTERN_BLUE = 2;
|
||||
byte CFA_PATTERN_CYAN = 3;
|
||||
byte CFA_PATTERN_MAGENTA = 4;
|
||||
byte CFA_PATTERN_YELLOW = 5;
|
||||
byte CFA_PATTERN_WHITE = 6; // ???? Should be KEY?
|
||||
|
||||
/**
|
||||
* This tag encodes the DNG four-tier version number. For files compliant with this version of
|
||||
* the DNG specification (1.4.0.0), this tag should contain the bytes: 1, 4, 0, 0.
|
||||
*/
|
||||
int TAG_DNG_VERSION = 50706;
|
||||
int TAG_DNG_BACKWARD_VERSION = 50707;
|
||||
|
||||
/** UniqueCameraModel defines a unique, non-localized name for the camera model that created the image in the raw file. */
|
||||
int TAG_UNIQUE_CAMERA_MODEL = 50708;
|
||||
int TAG_LOCALIZED_CAMERA_MODEL = 50709;
|
||||
|
||||
/** CFA plane to RGB mapping (default: [0, 1, 2]). */
|
||||
int TAG_CFA_PLANE_COLOR = 50710;
|
||||
/** CFA spatial layout (default: 1). */
|
||||
int TAG_CFA_LAYOUT = 50711;
|
||||
|
||||
/** 1 = Rectangular (or square) layout. */
|
||||
int CFA_LAYOUT_RECTANGULAR = 1;
|
||||
/** 2 = Staggered layout A: even columns are offset down by 1/2 row. */
|
||||
int CFA_LAYOUT_STAGGERED_A = 2;
|
||||
/** 3 = Staggered layout B: even columns are offset up by 1/2 row. */
|
||||
int CFA_LAYOUT_STAGGERED_B = 3;
|
||||
/** 4 = Staggered layout C: even rows are offset right by 1/2 column. */
|
||||
int CFA_LAYOUT_STAGGERED_C = 4;
|
||||
/** 5 = Staggered layout D: even rows are offset left by 1/2 column. */
|
||||
int CFA_LAYOUT_STAGGERED_D = 5;
|
||||
/** 6 = Staggered layout E: even rows are offset up by 1/2 row, even columns are offset left by 1/2 column. */
|
||||
int CFA_LAYOUT_STAGGERED_E = 6;
|
||||
/** 7 = Staggered layout F: even rows are offset up by 1/2 row, even columns are offset right by 1/2 column. */
|
||||
int CFA_LAYOUT_STAGGERED_F = 7;
|
||||
/** 8 = Staggered layout G: even rows are offset down by 1/2 row, even columns are offset left by 1/2 column. */
|
||||
int CFA_LAYOUT_STAGGERED_G = 8;
|
||||
/** 9 = Staggered layout H: even rows are offset down by 1/2 row, even columns are offset right by 1/2 column. */
|
||||
int CFA_LAYOUT_STAGGERED_H = 9;
|
||||
|
||||
/** LinearizationTable describes a lookup table that maps stored values into linear values. */
|
||||
int TAG_LINEARIZATION_TABLE = 50712;
|
||||
|
||||
/** This tag specifies repeat pattern size for the BlackLevel tag. Default: [1, 1]. */
|
||||
int TAG_BLACK_LEVEL_REPEAT_DIM = 50713;
|
||||
|
||||
/**
|
||||
* This tag specifies the zero light (a.k.a. thermal black or black current) encoding level,
|
||||
* as a repeating pattern. Default: 0.
|
||||
*/
|
||||
int TAG_BLACK_LEVEL = 50714;
|
||||
|
||||
// TODO: Rest of DNG tags.
|
||||
// ...
|
||||
|
||||
/**
|
||||
* Horizontal Difference X2.
|
||||
* Same as Horizontal Difference except the pixel two to the left is used rather than the pixel one to the left.
|
||||
*/
|
||||
int PREDICTOR_HORIZONTAL_X2 = 34892;
|
||||
/**
|
||||
* Horizontal Difference X4.
|
||||
* Same as Horizontal Difference except the pixel four to the left is used rather than the pixel one to the left.
|
||||
*/
|
||||
int PREDICTOR_HORIZONTAL_X4 = 34893;
|
||||
/**
|
||||
* Floating Point X2.
|
||||
* Same as Floating Point except the pixel two to the left is used rather than the pixel one to the left.
|
||||
*/
|
||||
int PREDICTOR_FLOATINGPOINT_X2 = 34894;
|
||||
/**
|
||||
* Floating Point X4.
|
||||
* Same as Floating Point except the pixel four to the left is used rather than the pixel one to the left
|
||||
*/
|
||||
int PREDICTOR_FLOATINGPOINT_X4 = 34895;
|
||||
}
|
||||
+1051
File diff suppressed because it is too large
Load Diff
+104
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name "TwelveMonkeys" nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.dng;
|
||||
|
||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
|
||||
import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
|
||||
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* CR2ImageReaderSpi
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: CR2ImageReaderSpi.java,v 1.0 07.04.14 21:26 haraldk Exp$
|
||||
*/
|
||||
public final class DNGImageReaderSpi extends ImageReaderSpiBase {
|
||||
public DNGImageReaderSpi() {
|
||||
super(new DNGProviderInfo());
|
||||
}
|
||||
|
||||
public boolean canDecodeInput(final Object pSource) throws IOException {
|
||||
if (!(pSource instanceof ImageInputStream)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ImageInputStream stream = (ImageInputStream) pSource;
|
||||
|
||||
stream.mark();
|
||||
try {
|
||||
byte[] bom = new byte[2];
|
||||
stream.readFully(bom);
|
||||
|
||||
ByteOrder originalOrder = stream.getByteOrder();
|
||||
|
||||
try {
|
||||
if (bom[0] == 'I' && bom[1] == 'I') {
|
||||
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
}
|
||||
else if (bom[0] == 'M' && bom[1] == 'M') {
|
||||
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
||||
int tiffMagic = stream.readUnsignedShort();
|
||||
if (tiffMagic != TIFF.TIFF_MAGIC) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: This is not different from a normal TIFF...
|
||||
|
||||
return true;
|
||||
}
|
||||
finally {
|
||||
stream.setByteOrder(originalOrder);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
stream.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageReader createReaderInstance(Object extension) throws IOException {
|
||||
return new DNGImageReader(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription(Locale locale) {
|
||||
return "Adobe Digital Negative (DNG) format Reader";
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oleg Ermolaev
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name "TwelveMonkeys" nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.dng;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
||||
|
||||
/**
|
||||
* @author Oleg Ermolaev Date: 04.05.2018 1:50
|
||||
*/
|
||||
class DNGProviderInfo extends ReaderWriterProviderInfo {
|
||||
protected DNGProviderInfo() {
|
||||
super(
|
||||
DNGProviderInfo.class,
|
||||
new String[]{"dng", "NDG"},
|
||||
new String[]{"dng"},
|
||||
new String[]{
|
||||
"image/x-adobe-dng", // TODO: Look up
|
||||
},
|
||||
"com.twelvemonkeys.imageio.plugins.dng.DNGImageReader",
|
||||
new String[]{"com.twelvemonkeys.imageio.plugins.dng.DNGImageReaderSpi"},
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name "TwelveMonkeys" nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.dng;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import java.awt.*;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* CR2ImageReaderTest
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: CR2ImageReaderTest.java,v 1.0 07.04.14 21:52 haraldk Exp$
|
||||
*/
|
||||
@Ignore
|
||||
public class DNGImageReaderTest extends ImageReaderAbstractTest<DNGImageReader> {
|
||||
@Override
|
||||
protected List<TestData> getTestData() {
|
||||
return Arrays.asList(
|
||||
new TestData(getClassLoaderResource("/dng/L1004220.DNG"),
|
||||
// Uncompressed RGB (thumbnail), Ucompressed CFA
|
||||
// new Dimension(320, 216),
|
||||
new Dimension(5216, 3472)),
|
||||
new TestData(getClassLoaderResource("/dng/IMG_2224.dng"),
|
||||
// Uncompressed RGB (thumbnail), JPEG Lossless CFA, JPEG DCT YCbCr
|
||||
// new Dimension(256, 171),
|
||||
new Dimension(3516, 2328),
|
||||
new Dimension(1024, 683))
|
||||
// new TestData(getClassLoaderResource("/dng/test.dng"), new Dimension(2, 2)) // Only JPEG Lossless CFA
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ImageReaderSpi createProvider() {
|
||||
return new DNGImageReaderSpi();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getFormatNames() {
|
||||
return Arrays.asList("dng");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getSuffixes() {
|
||||
return Arrays.asList("dng");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getMIMETypes() {
|
||||
return Arrays.asList("image/x-dng");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Known issue: Subsampled reading not supported")
|
||||
@Override
|
||||
public void testReadWithSubsampleParamPixels() throws IOException {
|
||||
super.testReadWithSubsampleParamPixels();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Known issue: Source region reading not supported")
|
||||
@Override
|
||||
public void testReadWithSourceRegionParamEqualImage() throws IOException {
|
||||
super.testReadWithSourceRegionParamEqualImage();
|
||||
}
|
||||
}
|
||||
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.8.3</version>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-hdr</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: HDR plugin</name>
|
||||
|
||||
+2
-10
@@ -38,14 +38,12 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
/**
|
||||
* HDRImageReaderTest
|
||||
* TGAImageReaderTest
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: HDRImageReaderTest.java,v 1.0 03.07.14 22:28 haraldk Exp$
|
||||
* @version $Id: TGAImageReaderTest.java,v 1.0 03.07.14 22:28 haraldk Exp$
|
||||
*/
|
||||
public class HDRImageReaderTest extends ImageReaderAbstractTest<HDRImageReader> {
|
||||
@Override
|
||||
@@ -60,12 +58,6 @@ 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.8.3</version>
|
||||
<version>3.8.0-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.8.3</version>
|
||||
<version>3.8.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-iff</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: IFF plugin</name>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user