mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2026-05-23 00:00:05 -04:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ab2f1f7e91 | |||
| 7110e89bda | |||
| a06cbfd6f4 | |||
| 3b68d676f3 |
@@ -1 +0,0 @@
|
|||||||
github: haraldk
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
---
|
|
||||||
name: Bug report
|
|
||||||
about: Create a report to help us improve
|
|
||||||
title: ''
|
|
||||||
labels: Reported bug
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Describe the bug**
|
|
||||||
A clear and concise description of what the bug is.
|
|
||||||
|
|
||||||
**Version information**
|
|
||||||
1. The version of the TwelveMonkeys ImageIO library in use.
|
|
||||||
For example: 4.0.0
|
|
||||||
|
|
||||||
2. The *exact* output of `java --version` (or `java -version` for older Java releases).
|
|
||||||
For example:
|
|
||||||
|
|
||||||
java version "1.8.0_271"
|
|
||||||
Java(TM) SE Runtime Environment (build 1.8.0_271-b09)
|
|
||||||
Java HotSpot(TM) 64-Bit Server VM (build 25.271-b09, mixed mode)
|
|
||||||
|
|
||||||
3. Extra information about OS version, server version, standalone program or web application packaging, executable wrapper, etc. Please state exact version numbers where applicable.
|
|
||||||
|
|
||||||
**To Reproduce**
|
|
||||||
Steps to reproduce the behavior:
|
|
||||||
|
|
||||||
1. Compile the below sample code
|
|
||||||
2. Download the sample image file
|
|
||||||
3. Run the code with the sample file
|
|
||||||
4. See error
|
|
||||||
|
|
||||||
**Expected behavior**
|
|
||||||
A clear and concise description of what you expected to happen.
|
|
||||||
|
|
||||||
**Example code**
|
|
||||||
Preferably as a failing JUnit test, or a standalone program with a `main` method that showcases the problem.
|
|
||||||
|
|
||||||
Less is more. Don't add your entire project, only the code required to reproduce the problem. 😀
|
|
||||||
|
|
||||||
**Sample file(s)**
|
|
||||||
Attach any sample files needed to reproduce the problem. Use a ZIP-file if the format is not directly supported by GitHub.
|
|
||||||
|
|
||||||
**Stak trace**
|
|
||||||
Always include the stack trace you experience.
|
|
||||||
|
|
||||||
**Screenshots**
|
|
||||||
If applicable, add screenshots to help explain your problem.
|
|
||||||
Do not add screenshots of code or stack traces. 😀
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context about the problem here.
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
---
|
|
||||||
name: Feature request
|
|
||||||
about: Suggest an idea for this project
|
|
||||||
title: ''
|
|
||||||
labels: New feature
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Is your feature request related to a use case or a problem you are working on? Please describe.**
|
|
||||||
A clear and concise description of what the problem or use case is. Understanding the rationale is key, to be able to implemeent the right solution.
|
|
||||||
|
|
||||||
**Describe the solution you'd like**
|
|
||||||
A clear and concise description of what you want to happen.
|
|
||||||
|
|
||||||
**Describe alternatives you've considered**
|
|
||||||
A clear and concise description of any alternative solutions or features you've already considered, and why they won't work.
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context or screenshots about the feature request here, like links to specifications or sample files.
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
---
|
|
||||||
name: Trouble shooting and programming help
|
|
||||||
about: "General programming issues will reach a wider audience at StackOverflow. Tag
|
|
||||||
questions with javax-imageio and/or twelvemonkeys \U0001F600 "
|
|
||||||
title: ''
|
|
||||||
labels: Trouble-shooting
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
General programming issues and problems will reach a much wider audience at StackOverflow, we suggest you ask them there. This will offload our work with maintaining the library, and make sure you get better help sooner.
|
|
||||||
|
|
||||||
Tag the question with `javax-imageio` and/or `twelvemonkeys` and we'll find them there.
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
**What is fixed** Add link to the issue this PR fixes.
|
|
||||||
|
|
||||||
Example: Fixes #42.
|
|
||||||
|
|
||||||
**Why is this change proposed** If this change does *not* fix an open issue, briefly describe the rationale for this PR.
|
|
||||||
|
|
||||||
**What is changed** Briefly describe the changes proposed in this pull request:
|
|
||||||
|
|
||||||
* Fixed rare exception happening in `x >= 42` case
|
|
||||||
* Small optimization of `decompress()` method
|
|
||||||
* Corrected API doc for `compress()` method to reflect current implementation
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
version: 2
|
|
||||||
updates:
|
|
||||||
# Maven/Java library updates
|
|
||||||
- package-ecosystem: "maven"
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: "daily"
|
|
||||||
cooldown:
|
|
||||||
default-days: 7
|
|
||||||
open-pull-requests-limit: 10
|
|
||||||
# GitHub actions updates
|
|
||||||
- package-ecosystem: "github-actions"
|
|
||||||
directory: "/.github/workflows"
|
|
||||||
schedule:
|
|
||||||
interval: "daily"
|
|
||||||
cooldown:
|
|
||||||
default-days: 7
|
|
||||||
@@ -1,143 +0,0 @@
|
|||||||
name: CI
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- '**'
|
|
||||||
- '!dependabot/**'
|
|
||||||
pull_request:
|
|
||||||
branches: [ 'master' ]
|
|
||||||
|
|
||||||
permissions: read-all
|
|
||||||
|
|
||||||
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, 21, 25 ]
|
|
||||||
exclude:
|
|
||||||
- os: macos-latest
|
|
||||||
java: 8
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
permissions:
|
|
||||||
checks: write
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
||||||
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
|
||||||
with:
|
|
||||||
distribution: 'temurin'
|
|
||||||
java-version: ${{ matrix.java }}
|
|
||||||
java-package: jdk
|
|
||||||
cache: 'maven'
|
|
||||||
- name: Run Tests
|
|
||||||
run: mvn --batch-mode --no-transfer-progress test
|
|
||||||
- name: Publish Test Report
|
|
||||||
uses: mikepenz/action-junit-report@bccf2e31636835cf0874589931c4116687171386 # v5
|
|
||||||
if: ${{ !cancelled() }}
|
|
||||||
with:
|
|
||||||
report_paths: "**/target/surefire-reports/TEST*.xml"
|
|
||||||
check_name: Unit Test Results for OpenJDK ${{ matrix.java }} on ${{ matrix.os }}
|
|
||||||
|
|
||||||
test-jdk8-macos:
|
|
||||||
name: Test OpenJDK 8 on macos-14
|
|
||||||
runs-on: macos-14
|
|
||||||
permissions:
|
|
||||||
checks: write
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
||||||
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
|
||||||
with:
|
|
||||||
distribution: 'zulu'
|
|
||||||
java-version: '8'
|
|
||||||
java-package: jdk
|
|
||||||
cache: 'maven'
|
|
||||||
- name: Run Tests
|
|
||||||
run: mvn --batch-mode --no-transfer-progress test
|
|
||||||
- name: Publish Test Report
|
|
||||||
uses: mikepenz/action-junit-report@bccf2e31636835cf0874589931c4116687171386 # v5
|
|
||||||
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
|
|
||||||
permissions:
|
|
||||||
checks: write
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
kcms: [ true, false ]
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
||||||
- 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@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
|
||||||
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 --batch-mode --no-transfer-progress test
|
|
||||||
- name: Publish Test Report
|
|
||||||
uses: mikepenz/action-junit-report@bccf2e31636835cf0874589931c4116687171386 # v5
|
|
||||||
if: ${{ !cancelled() }}
|
|
||||||
with:
|
|
||||||
report_paths: "**/target/surefire-reports/TEST*.xml"
|
|
||||||
check_name: Unit Test Results for Oracle JDK 8 with KCMS=${{ matrix.kcms }}
|
|
||||||
|
|
||||||
javadoc:
|
|
||||||
name: Build JavaDoc on OpenJDK ${{ matrix.java }}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
java: [8, 11, 25 ] # We only need a few versions here
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
||||||
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
|
||||||
with:
|
|
||||||
distribution: 'temurin'
|
|
||||||
java-version: ${{ matrix.java }}
|
|
||||||
java-package: jdk
|
|
||||||
cache: 'maven'
|
|
||||||
- name: Create JavaDoc
|
|
||||||
run: mvn --batch-mode --no-transfer-progress -DskipTests package javadoc:javadoc
|
|
||||||
|
|
||||||
release:
|
|
||||||
name: Deploy
|
|
||||||
needs: [ test, test-jdk8-macos, test-oracle, javadoc ]
|
|
||||||
if: github.ref == 'refs/heads/master' # only perform on latest master
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
||||||
- name: Set up Maven Central
|
|
||||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
|
||||||
with: # running setup-java again overwrites the settings.xml
|
|
||||||
distribution: 'temurin'
|
|
||||||
java-version: '8'
|
|
||||||
java-package: jdk
|
|
||||||
server-id: central # 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)
|
|
||||||
- name: Get Project Version
|
|
||||||
run: |
|
|
||||||
echo "PROJECT_VERSION=$(mvn --batch-mode help:evaluate -Dexpression=project.version -q -DforceStdout)" >> $GITHUB_ENV
|
|
||||||
- name: Publish to Maven Central
|
|
||||||
if: ${{ endsWith(env.PROJECT_VERSION, '-SNAPSHOT') }}
|
|
||||||
run: mvn --batch-mode --no-transfer-progress deploy -P release -DskipTests -Dgpg.signer=bc
|
|
||||||
env:
|
|
||||||
MAVEN_CENTRAL_USERNAME: ${{ secrets.CENTRAL_USERNAME }} # must be the same env variable name as (1)
|
|
||||||
MAVEN_CENTRAL_PASSWORD: ${{ secrets.CENTRAL_PASSWORD }} # must be the same env variable name as (2)
|
|
||||||
MAVEN_GPG_KEY: ${{ secrets.GPG_KEY }} # Value of the GPG private key to import
|
|
||||||
MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
name: "CodeQL"
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ "master" ]
|
|
||||||
pull_request:
|
|
||||||
# The branches below must be a subset of the branches above
|
|
||||||
branches: [ "master" ]
|
|
||||||
schedule:
|
|
||||||
- cron: '26 13 * * 6'
|
|
||||||
|
|
||||||
permissions: {}
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
analyze:
|
|
||||||
name: Analyze
|
|
||||||
# Runner size impacts CodeQL analysis time. To learn more, please see:
|
|
||||||
# - https://gh.io/recommended-hardware-resources-for-running-codeql
|
|
||||||
# - https://gh.io/supported-runners-and-hardware-resources
|
|
||||||
# - https://gh.io/using-larger-runners
|
|
||||||
# Consider using larger runners for possible analysis time improvements.
|
|
||||||
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
|
|
||||||
timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }}
|
|
||||||
permissions:
|
|
||||||
actions: read
|
|
||||||
contents: read
|
|
||||||
security-events: write
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
language: [ 'java' ]
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
|
||||||
- name: Initialize CodeQL
|
|
||||||
uses: github/codeql-action/init@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5
|
|
||||||
with:
|
|
||||||
languages: ${{ matrix.language }}
|
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
|
||||||
# By default, queries listed here will override any specified in a config file.
|
|
||||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
|
||||||
|
|
||||||
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
|
||||||
# queries: security-extended,security-and-quality
|
|
||||||
|
|
||||||
|
|
||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
|
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
|
||||||
- name: Autobuild
|
|
||||||
uses: github/codeql-action/autobuild@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5
|
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
|
||||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
|
||||||
|
|
||||||
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
|
||||||
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
|
||||||
|
|
||||||
# - run: |
|
|
||||||
# echo "Run, Build Application using script"
|
|
||||||
# ./location_of_script_within_repo/buildscript.sh
|
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
|
||||||
uses: github/codeql-action/analyze@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5
|
|
||||||
with:
|
|
||||||
category: "/language:${{matrix.language}}"
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
# This workflow uses actions that are not certified by GitHub. They are provided
|
|
||||||
# by a third-party and are governed by separate terms of service, privacy
|
|
||||||
# policy, and support documentation.
|
|
||||||
|
|
||||||
name: Scorecard supply-chain security
|
|
||||||
on:
|
|
||||||
# For Branch-Protection check. Only the default branch is supported. See
|
|
||||||
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
|
|
||||||
branch_protection_rule:
|
|
||||||
# To guarantee Maintained check is occasionally updated. See
|
|
||||||
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
|
|
||||||
schedule:
|
|
||||||
- cron: '38 8 * * 2'
|
|
||||||
push:
|
|
||||||
branches: [ "master" ]
|
|
||||||
|
|
||||||
permissions: read-all # Declare default permissions as read only.
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
analysis:
|
|
||||||
name: Scorecard analysis
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
security-events: write # to upload the results to code-scanning dashboard.
|
|
||||||
id-token: write # to publish results and get a badge
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: "Checkout code"
|
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: "Run analysis"
|
|
||||||
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
|
|
||||||
with:
|
|
||||||
results_file: results.sarif
|
|
||||||
results_format: sarif
|
|
||||||
# (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
|
|
||||||
# you want to enable the Branch-Protection check on the repository
|
|
||||||
# To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-fine-grained-pat-optional.
|
|
||||||
# repo_token: ${{ secrets.SCORECARD_TOKEN }}
|
|
||||||
|
|
||||||
# Publish Results:
|
|
||||||
# - Publish results to OpenSSF REST API for easy access by consumers
|
|
||||||
# - Allows the repository to include the Scorecard badge.
|
|
||||||
# - See https://github.com/ossf/scorecard-action#publishing-results.
|
|
||||||
publish_results: true
|
|
||||||
|
|
||||||
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
|
||||||
# format to the repository Actions tab.
|
|
||||||
- name: "Upload artifact"
|
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
||||||
with:
|
|
||||||
name: SARIF file
|
|
||||||
path: results.sarif
|
|
||||||
retention-days: 5
|
|
||||||
|
|
||||||
# Upload the results to GitHub's code scanning dashboard.
|
|
||||||
- name: "Upload to code-scanning"
|
|
||||||
uses: github/codeql-action/upload-sarif@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5
|
|
||||||
with:
|
|
||||||
sarif_file: results.sarif
|
|
||||||
@@ -15,4 +15,3 @@ private
|
|||||||
profiles.xml
|
profiles.xml
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
.DS_Store
|
.DS_Store
|
||||||
/.metadata/
|
|
||||||
|
|||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
language: java
|
||||||
|
jdk:
|
||||||
|
- oraclejdk8
|
||||||
|
# Oracle JDK 7 no longer supported, we use env matrix to test various CMM providers
|
||||||
|
# - oraclejdk7
|
||||||
|
# Some JPEGImageReader tests fail on OpenJDK, need to investigate/fix before enabling
|
||||||
|
# - openjdk7
|
||||||
|
env:
|
||||||
|
- MAVEN_OPTS=-Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider
|
||||||
|
- MAVEN_OPTS=""
|
||||||
|
cache:
|
||||||
|
directories:
|
||||||
|
- $HOME/.m2
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
BSD 3-Clause License
|
BSD 3-Clause License
|
||||||
|
|
||||||
Copyright (c) 2008-2020, Harald Kuhr
|
Copyright (c) 2017, Harald Kuhr
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
|||||||
@@ -1,85 +1,261 @@
|
|||||||
[](https://github.com/haraldk/TwelveMonkeys/actions/workflows/ci.yml)
|
## Latest
|
||||||
[](https://github.com/haraldk/TwelveMonkeys/actions/workflows/codeql.yml)
|
|
||||||
[](https://securityscorecards.dev/viewer/?uri=github.com/haraldk/TwelveMonkeys)
|
|
||||||
[](https://www.bestpractices.dev/projects/7900)
|
|
||||||
|
|
||||||
[](https://search.maven.org/search?q=g:com.twelvemonkeys.imageio)
|
Master branch build status: [](https://travis-ci.org/haraldk/TwelveMonkeys)
|
||||||
[](https://central.sonatype.com/repository/maven-snapshots/com/twelvemonkeys/imageio/imageio/maven-metadata.xml)
|
|
||||||
[](https://stackoverflow.com/questions/tagged/twelvemonkeys)
|
|
||||||
[](https://paypal.me/haraldk76/100)
|
|
||||||
|
|
||||||

|
Latest release is TwelveMonkeys ImageIO [3.3.2](http://search.maven.org/#search%7Cga%7C1%7Cg%3Acom.twelvemonkeys*%20AND%20v%3A%223.3.2%22) (Feb. 2nd, 2017).
|
||||||
|
[Release notes](https://github.com/haraldk/TwelveMonkeys/releases/latest).
|
||||||
|
|
||||||
## About
|
## 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 file formats not covered by the JDK.
|
These plugins extends the number of image file formats supported in Java, using the javax.imageio.* package.
|
||||||
Support for these formats is important, to be able to read data found
|
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.
|
"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
|
## Features
|
||||||
|
|
||||||
| Plugin | Format | Description | R | W | Metadata | Notes |
|
Mainstream format support
|
||||||
| ------ | -------- |---------------------------------------------------------|:---:|:---:| -------- | ----- |
|
|
||||||
| 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 | ✔ | ✔ | - |
|
|
||||||
| [DDS](https://github.com/haraldk/TwelveMonkeys/wiki/DDS-Plugin) | DDS | MS Direct Draw Surface Format | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
|
||||||
| [HDR](https://github.com/haraldk/TwelveMonkeys/wiki/HDR-Plugin) | HDR | Radiance High Dynamic Range RGBE Format | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
|
||||||
| [ICNS](https://github.com/haraldk/TwelveMonkeys/wiki/ICNS-Plugin) | ICNS | Apple Icon Image | ✔ | ✔ | - |
|
|
||||||
| [IFF](https://github.com/haraldk/TwelveMonkeys/wiki/IFF-Plugin) | IFF | Commodore Amiga/Electronic Arts Interchange File Format | ✔ | ✔ | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
|
||||||
| [JPEG](https://github.com/haraldk/TwelveMonkeys/wiki/JPEG-Plugin) | **JPEG** | Joint Photographers Expert Group | ✔ | ✔ | [Native](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/jpeg_metadata.html#image), [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
|
||||||
| | JPEG Lossless | | ✔ | - | [Native](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/jpeg_metadata.html#image), [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
|
||||||
| [PCX](https://github.com/haraldk/TwelveMonkeys/wiki/PCX-Plugin) | PCX | ZSoft Paintbrush Format | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
|
||||||
| | DCX | Multi-page PCX fax document | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
|
||||||
| [PICT](https://github.com/haraldk/TwelveMonkeys/wiki/PICT-Plugin) | PICT | Apple QuickTime Picture Format | ✔ | ✔ | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
|
||||||
| | PNTG | Apple MacPaint Picture Format | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
|
||||||
| [PNM](https://github.com/haraldk/TwelveMonkeys/wiki/PNM-Plugin) | PAM | NetPBM Portable Any Map | ✔ | ✔ | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
|
||||||
| | PBM | NetPBM Portable Bit Map | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
|
||||||
| | PGM | NetPBM Portable Grey Map | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
|
||||||
| | PPM | NetPBM Portable Pix Map | ✔ | ✔ | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
|
||||||
| | PFM | Portable Float Map | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
|
||||||
| [PSD](https://github.com/haraldk/TwelveMonkeys/wiki/PSD-Plugin) | **PSD** | Adobe Photoshop Document | ✔ | (✔) | Native, [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
|
||||||
| | PSB | Adobe Photoshop Large Document | ✔ | - | Native, [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
|
||||||
| [SGI](https://github.com/haraldk/TwelveMonkeys/wiki/SGI-Plugin) | SGI | Silicon Graphics Image Format | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
|
||||||
| [TGA](https://github.com/haraldk/TwelveMonkeys/wiki/TGA-Plugin) | TGA | Truevision TGA Image Format | ✔ | ✔ | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
|
||||||
|ThumbsDB| Thumbs.db| MS Windows Thumbs DB | ✔ | - | - | OLE2 Compound Document based format only |
|
|
||||||
| [TIFF](https://github.com/haraldk/TwelveMonkeys/wiki/TIFF-Plugin) | **TIFF** | Aldus/Adobe Tagged Image File Format | ✔ | ✔ | [Native](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/tiff_metadata.html#ImageMetadata), [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
|
||||||
| | BigTIFF | | ✔ | ✔ | [Native](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/tiff_metadata.html#ImageMetadata), [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
|
||||||
| [WebP](https://github.com/haraldk/TwelveMonkeys/wiki/WebP-Plugin) | **WebP** | Google WebP Format | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
|
||||||
| XWD | XWD | X11 Window Dump Format | ✔ | - | [Standard](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html) |
|
|
||||||
|
|
||||||
|
#### BMP - MS Windows/IBM OS/2 Device Independent Bitmap
|
||||||
|
|
||||||
**Important note on using Batik:** *Please read [The Apache™ XML Graphics Project - Security](https://xmlgraphics.apache.org/security.html),
|
* Read support for all known versions of the DIB/BMP format
|
||||||
and make sure you use an updated and secure version.*
|
* Indexed color, 1, 4 and 8 bit, including 4 and 8 bit RLE
|
||||||
|
* RGB, 16, 24 and 32 bit
|
||||||
|
* Embedded PNG and JPEG data
|
||||||
|
* Windows and OS/2 versions
|
||||||
|
* Native and standard metadata format
|
||||||
|
|
||||||
|
#### JPEG
|
||||||
|
|
||||||
|
* Read support for the following JPEG "flavors":
|
||||||
|
* All JFIF compliant JPEGs
|
||||||
|
* All Exif compliant JPEGs
|
||||||
|
* YCbCr JPEGs without JFIF segment (converted to RGB, using embedded ICC profile)
|
||||||
|
* CMYK JPEGs (converted to RGB by default or as CMYK, using embedded ICC profile)
|
||||||
|
* Adobe YCCK JPEGs (converted to RGB by default or as CMYK, using embedded ICC profile)
|
||||||
|
* JPEGs containing ICC profiles with interpretation other than 'Perceptual' or class other than 'Display'
|
||||||
|
* JPEGs containing ICC profiles that are incompatible with stream data, corrupted ICC profiles or corrupted `ICC_PROFILE` segments
|
||||||
|
* JPEGs using non-standard color spaces, unsupported by Java 2D
|
||||||
|
* JPEGs with APP14/Adobe segments with length other than 14 bytes
|
||||||
|
* 8 bit JPEGs with 16 bit DQT segments
|
||||||
|
* Issues warnings instead of throwing exceptions in cases of corrupted or non-conformant data where ever the image
|
||||||
|
data can still be read in a reasonable way
|
||||||
|
* Thumbnail support:
|
||||||
|
* JFIF thumbnails (even if stream contains "inconsistent metadata")
|
||||||
|
* JFXX thumbnails (JPEG, Indexed and RGB)
|
||||||
|
* EXIF thumbnails (JPEG, RGB and YCbCr)
|
||||||
|
* Metadata support:
|
||||||
|
* JPEG metadata in both standard and native formats (even if stream contains "inconsistent metadata")
|
||||||
|
* `javax_imageio_jpeg_image_1.0` format (currently as native format, may change in the future)
|
||||||
|
* Non-conforming combinations of JFIF, Exif and Adobe markers, using "unknown" segments in the
|
||||||
|
"MarkerSequence" tag for the unsupported segments (for `javax_imageio_jpeg_image_1.0` format)
|
||||||
|
* Extended write support in progress:
|
||||||
|
* CMYK JPEGs
|
||||||
|
* YCCK JPEGs
|
||||||
|
|
||||||
|
#### JPEG-2000
|
||||||
|
|
||||||
|
* Possibly coming in the future, pending some license issues.
|
||||||
|
|
||||||
|
If you are one of the authors, or know one of the authors and/or the current license holders of either the original
|
||||||
|
jj2000 package or the JAI ImageIO project, please contact me (I've tried to get in touch in various ways,
|
||||||
|
without success so far).
|
||||||
|
|
||||||
|
Alternatively, if you have or know of a JPEG-2000 implementation in Java with a suitable license, get in touch. :-)
|
||||||
|
|
||||||
|
#### PNM - NetPBM Portable Any Map
|
||||||
|
|
||||||
|
* Read support for the following file types:
|
||||||
|
* PBM in 'P1' (ASCII) and 'P4' (binary) formats, 1 bit per pixel
|
||||||
|
* PGM in 'P2' (ASCII) and 'P5' (binary) formats, up to 16/32 bits per pixel
|
||||||
|
* PPM in 'P3' (ASCII) and 'P6' (binary) formats, up to 16/32 bits per pixel component
|
||||||
|
* PAM in 'P7' (binary) format up to 32 bits per pixel component
|
||||||
|
* Limited support for PFM in 'Pf' (gray) and 'PF' (RGB) formats, 32 bits floating point
|
||||||
|
* Write support for the following formats:
|
||||||
|
* PPM in 'P6' (binary) format
|
||||||
|
* PAM in 'P7' (binary) format
|
||||||
|
* Standard metadata support
|
||||||
|
|
||||||
|
#### PSD - Adobe Photoshop Document
|
||||||
|
|
||||||
|
* Read support for the following file types:
|
||||||
|
* Monochrome, 1 channel, 1 bit
|
||||||
|
* Indexed, 1 channel, 8 bit
|
||||||
|
* Gray, 1 channel, 8, 16 and 32 bit
|
||||||
|
* Duotone, 1 channel, 8, 16 and 32 bit
|
||||||
|
* RGB, 3-4 channels, 8, 16 and 32 bit
|
||||||
|
* CMYK, 4-5 channels, 8, 16 and 32 bit
|
||||||
|
* Read support for the following compression types:
|
||||||
|
* Uncompressed
|
||||||
|
* RLE (PackBits)
|
||||||
|
* Layer support
|
||||||
|
* Image layers only, in all of the above types
|
||||||
|
* Thumbnail support
|
||||||
|
* JPEG
|
||||||
|
* RAW (RGB)
|
||||||
|
* Support for "Large Document Format" (PSB)
|
||||||
|
* Native and Standard metadata support
|
||||||
|
|
||||||
|
#### TIFF - Aldus/Adobe Tagged Image File Format
|
||||||
|
|
||||||
|
* Read support for the following "Baseline" TIFF file types:
|
||||||
|
* Class B (Bi-level), all relevant compression types, 1 bit per sample
|
||||||
|
* Class G (Gray), all relevant compression types, 2, 4, 8, 16 or 32 bits per sample, unsigned integer
|
||||||
|
* Class P (Palette/indexed color), all relevant compression types, 1, 2, 4, 8 or 16 bits per sample, unsigned integer
|
||||||
|
* Class R (RGB), all relevant compression types, 8 or 16 bits per sample, unsigned integer
|
||||||
|
* Read support for the following TIFF extensions:
|
||||||
|
* Tiling
|
||||||
|
* Class F (Facsimile), CCITT Modified Huffman RLE, T4 and T6 (type 2, 3 and 4) compressions.
|
||||||
|
* LZW Compression (type 5)
|
||||||
|
* "Old-style" JPEG Compression (type 6), as a best effort, as the spec is not well-defined
|
||||||
|
* JPEG Compression (type 7)
|
||||||
|
* ZLib (aka Adobe-style Deflate) Compression (type 8)
|
||||||
|
* Deflate Compression (type 32946)
|
||||||
|
* Horizontal differencing Predictor (type 2) for LZW, ZLib, Deflate and PackBits compression
|
||||||
|
* Alpha channel (ExtraSamples type 1/Associated Alpha and type 2/Unassociated Alpha)
|
||||||
|
* CMYK data (PhotometricInterpretation type 5/Separated)
|
||||||
|
* YCbCr data (PhotometricInterpretation type 6/YCbCr) for JPEG
|
||||||
|
* CIELab data in TIFF, ITU and ICC variants (PhotometricInterpretation type 9, 10 and 11)
|
||||||
|
* Planar data (PlanarConfiguration type 2/Planar)
|
||||||
|
* ICC profiles (ICCProfile)
|
||||||
|
* BitsPerSample values up to 16 for most PhotometricInterpretations
|
||||||
|
* Multiple images (pages) in one file
|
||||||
|
* Write support for most "Baseline" TIFF options
|
||||||
|
* Uncompressed, PackBits, ZLib and Deflate
|
||||||
|
* Additional support for CCITT T4 and and T6 compressions.
|
||||||
|
* Additional support for LZW and JPEG (type 7) compressions
|
||||||
|
* Horizontal differencing Predictor (type 2) for LZW, ZLib, Deflate
|
||||||
|
* Native and Standard metadata support
|
||||||
|
|
||||||
|
Legacy formats
|
||||||
|
|
||||||
|
#### HDR - Radiance High Dynamic Range RGBE Format
|
||||||
|
|
||||||
|
* Read support for the most common RGBE (.hdr) format
|
||||||
|
* Samples are converted to 32 bit floating point (`float`) and normalized using a global tone mapper by default.
|
||||||
|
* Support for custom global tone mappers
|
||||||
|
* Alternatively, use a "null-tone mapper", for unnormalized data (allows local tone mapping)
|
||||||
|
* Unconverted RGBE samples accessible using `readRaster`
|
||||||
|
* Standard metadata support
|
||||||
|
|
||||||
|
#### IFF - Commodore Amiga/Electronic Arts Interchange File Format
|
||||||
|
|
||||||
|
* Legacy format, allows reading popular image format from the Commodore Amiga computer.
|
||||||
|
* Read support for the following file types:
|
||||||
|
* ILBM Indexed color, 1-8 interleaved bit planes, including 6 bit EHB
|
||||||
|
* ILBM Gray, 8 bit interleaved bit planes
|
||||||
|
* ILBM RGB, 24 and 32 bit interleaved bit planes
|
||||||
|
* ILBM HAM6 and HAM8
|
||||||
|
* PBM Indexed color, 1-8 bit,
|
||||||
|
* PBM Gray, 8 bit
|
||||||
|
* PBM RGB, 24 and 32 bit
|
||||||
|
* PBM HAM6 and HAM8
|
||||||
|
* Write support
|
||||||
|
* ILBM Indexed color, 1-8 bits per sample, 8 bit gray, 24 and 32 bit true color.
|
||||||
|
* Support for the following compression types (read/write):
|
||||||
|
* Uncompressed
|
||||||
|
* RLE (PackBits)
|
||||||
|
|
||||||
|
#### PCX - ZSoft Paintbrush Format
|
||||||
|
|
||||||
|
* Read support for the following file types:
|
||||||
|
* Indexed color, 1, 2, 4 or 8 bits per pixel, bit planes or interleaved
|
||||||
|
* Grayscale, 8 bits per pixel
|
||||||
|
* Color (RGB), 8 bits per pixel component
|
||||||
|
* Read support for DCX (multi-page) fax format, containing any of the above types
|
||||||
|
* Support for the following compression types:
|
||||||
|
* Uncompressed (experimental)
|
||||||
|
* RLE compressed
|
||||||
|
* Standard metadata support
|
||||||
|
|
||||||
|
#### PICT - Apple Mac Paint Picture Format
|
||||||
|
|
||||||
|
* Legacy format, especially useful for reading OS X clipboard data.
|
||||||
|
* Read support for the following file types:
|
||||||
|
* QuickDraw (format support is not complete, but supports most OS X clipboard data as well as RGB pixel data)
|
||||||
|
* QuickDraw bitmap
|
||||||
|
* QuickDraw pixmap
|
||||||
|
* QuickTime stills
|
||||||
|
* Write support for RGB pixel data:
|
||||||
|
* QuickDraw pixmap
|
||||||
|
|
||||||
|
#### SGI - Silicon Graphics Image Format
|
||||||
|
|
||||||
|
* Read support for the following file types:
|
||||||
|
* 1, 2, 3 or 4 channel image data
|
||||||
|
* 8 or 16 bits per pixel component
|
||||||
|
* Support for the following compression types:
|
||||||
|
* Uncompressed
|
||||||
|
* RLE compressed
|
||||||
|
* Standard metadata support
|
||||||
|
|
||||||
|
#### TGA - Truevision TGA Image Format
|
||||||
|
|
||||||
|
* Read support for the following file types:
|
||||||
|
* ColorMapped
|
||||||
|
* Monochrome
|
||||||
|
* TrueColor
|
||||||
|
* Support for the following compression types:
|
||||||
|
* Uncompressed
|
||||||
|
* RLE compressed
|
||||||
|
* Standard metadata support
|
||||||
|
|
||||||
|
Icon/other formats
|
||||||
|
|
||||||
|
#### ICNS - Apple Icon Image
|
||||||
|
|
||||||
|
* Read support for the following icon types:
|
||||||
|
* All known "native" icon types
|
||||||
|
* Large PNG encoded icons
|
||||||
|
* Large JPEG 2000 encoded icons (requires JPEG 2000 ImageIO plugin or fallback to `sips` command line tool)
|
||||||
|
|
||||||
|
#### ICO & CUR - MS Windows Icon and Cursor Formats
|
||||||
|
|
||||||
|
* Read support for the following file types:
|
||||||
|
* ICO Indexed color, 1, 4 and 8 bit
|
||||||
|
* ICO RGB, 16, 24 and 32 bit
|
||||||
|
* CUR Indexed color, 1, 4 and 8 bit
|
||||||
|
* CUR RGB, 16, 24 and 32 bit
|
||||||
|
* *3.1* Note: These formats are now part of the BMP plugin
|
||||||
|
|
||||||
|
#### Thumbs.db - MS Windows Thumbs DB
|
||||||
|
|
||||||
|
* Read support
|
||||||
|
|
||||||
|
Other formats, using 3rd party libraries
|
||||||
|
|
||||||
|
#### SVG - Scalable Vector Graphics
|
||||||
|
|
||||||
|
* Read-only support using Batik
|
||||||
|
|
||||||
|
#### WMF - MS Windows MetaFile
|
||||||
|
|
||||||
|
* Limited read-only support using Batik
|
||||||
|
|
||||||
|
**Important note on using Batik:** *Please read [The Apache™ XML Graphics Project - Security](http://xmlgraphics.apache.org/security.html), and make sure you use
|
||||||
|
either version 1.6.1, 1.7.1 or 1.8+.*
|
||||||
|
|
||||||
Note that GIF, PNG and WBMP formats are already supported through the ImageIO API, using the
|
|
||||||
[JDK standard plugins](https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/package-summary.html).
|
|
||||||
For BMP, JPEG, and TIFF formats the TwelveMonkeys plugins provides extended format support and additional features.
|
|
||||||
|
|
||||||
## Basic usage
|
## Basic usage
|
||||||
|
|
||||||
Most of the time, all you need to do is simply include the plugins in your project and write:
|
Most of the time, all you need to do is simply include the plugins in your project and write:
|
||||||
|
|
||||||
```java
|
BufferedImage image = ImageIO.read(file);
|
||||||
BufferedImage image = ImageIO.read(file);
|
|
||||||
```
|
|
||||||
|
|
||||||
This will load the first image of the file, entirely into memory.
|
This will load the first image of the file, entirely into memory.
|
||||||
|
|
||||||
The basic and simplest form of writing is:
|
The basic and simplest form of writing is:
|
||||||
|
|
||||||
```java
|
if (!ImageIO.write(image, format, file)) {
|
||||||
if (!ImageIO.write(image, format, file)) {
|
// Handle image not written case
|
||||||
// Handle image not written case
|
}
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This will write the entire image into a single file, using the default settings for the given format.
|
This will write the entire image into a single file, using the default settings for the given format.
|
||||||
|
|
||||||
@@ -89,46 +265,50 @@ The plugins are discovered automatically at run time. See the [FAQ](#faq) for mo
|
|||||||
|
|
||||||
If you need more control of read parameters and the reading process, the common idiom for reading is something like:
|
If you need more control of read parameters and the reading process, the common idiom for reading is something like:
|
||||||
|
|
||||||
```java
|
// Create input stream
|
||||||
// Create input stream (in try-with-resource block to avoid leaks)
|
ImageInputStream input = ImageIO.createImageInputStream(file);
|
||||||
try (ImageInputStream input = ImageIO.createImageInputStream(file)) {
|
|
||||||
// Get the reader
|
|
||||||
Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
|
|
||||||
|
|
||||||
if (!readers.hasNext()) {
|
|
||||||
throw new IllegalArgumentException("No reader for: " + file);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageReader reader = readers.next();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
reader.setInput(input);
|
// Get the reader
|
||||||
|
Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
|
||||||
|
|
||||||
// Optionally, listen for read warnings, progress, etc.
|
if (!readers.hasNext()) {
|
||||||
reader.addIIOReadWarningListener(...);
|
throw new IllegalArgumentException("No reader for: " + file);
|
||||||
reader.addIIOReadProgressListener(...);
|
}
|
||||||
|
|
||||||
ImageReadParam param = reader.getDefaultReadParam();
|
ImageReader reader = readers.next();
|
||||||
|
|
||||||
// Optionally, control read settings like sub sampling, source region or destination etc.
|
try {
|
||||||
param.setSourceSubsampling(...);
|
reader.setInput(input);
|
||||||
param.setSourceRegion(...);
|
|
||||||
param.setDestination(...);
|
|
||||||
// ...
|
|
||||||
|
|
||||||
// Finally read the image, using settings from param
|
// Optionally, listen for read warnings, progress, etc.
|
||||||
BufferedImage image = reader.read(0, param);
|
reader.addIIOReadWarningListener(...);
|
||||||
|
reader.addIIOReadProgressListener(...);
|
||||||
|
|
||||||
// Optionally, read thumbnails, meta data, etc...
|
ImageReadParam param = reader.getDefaultReadParam();
|
||||||
int numThumbs = reader.getNumThumbnails(0);
|
|
||||||
// ...
|
// Optionally, control read settings like sub sampling, source region or destination etc.
|
||||||
|
param.setSourceSubsampling(...);
|
||||||
|
param.setSourceRegion(...);
|
||||||
|
param.setDestination(...);
|
||||||
|
// ...
|
||||||
|
|
||||||
|
// Finally read the image, using settings from param
|
||||||
|
BufferedImage image = reader.read(0, param);
|
||||||
|
|
||||||
|
// Optionally, read thumbnails, meta data, etc...
|
||||||
|
int numThumbs = reader.getNumThumbnails(0);
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
// Dispose reader in finally block to avoid memory leaks
|
||||||
|
reader.dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
// Dispose reader in finally block to avoid memory leaks
|
// Close stream in finally block to avoid resource leaks
|
||||||
reader.dispose();
|
input.close();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Query the reader for source image dimensions using `reader.getWidth(n)` and `reader.getHeight(n)` without reading the
|
Query the reader for source image dimensions using `reader.getWidth(n)` and `reader.getHeight(n)` without reading the
|
||||||
entire image into memory first.
|
entire image into memory first.
|
||||||
@@ -138,142 +318,129 @@ It's also possible to read multiple images from the same file in a loop, using `
|
|||||||
|
|
||||||
If you need more control of write parameters and the writing process, the common idiom for writing is something like:
|
If you need more control of write parameters and the writing process, the common idiom for writing is something like:
|
||||||
|
|
||||||
```java
|
// Get the writer
|
||||||
// Get the writer
|
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(format);
|
||||||
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(format);
|
|
||||||
|
|
||||||
if (!writers.hasNext()) {
|
if (!writers.hasNext()) {
|
||||||
throw new IllegalArgumentException("No writer for: " + format);
|
throw new IllegalArgumentException("No writer for: " + format);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageWriter writer = writers.next();
|
ImageWriter writer = writers.next();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Create output stream (in try-with-resource block to avoid leaks)
|
// Create output stream
|
||||||
try (ImageOutputStream output = ImageIO.createImageOutputStream(file)) {
|
ImageOutputStream output = ImageIO.createImageOutputStream(file);
|
||||||
writer.setOutput(output);
|
|
||||||
|
try {
|
||||||
// Optionally, listen to progress, warnings, etc.
|
writer.setOutput(output);
|
||||||
|
|
||||||
ImageWriteParam param = writer.getDefaultWriteParam();
|
// Optionally, listen to progress, warnings, etc.
|
||||||
|
|
||||||
// Optionally, control format specific settings of param (requires casting), or
|
ImageWriteParam param = writer.getDefaultWriteParam();
|
||||||
// control generic write settings like sub sampling, source region, output type etc.
|
|
||||||
|
// Optionally, control format specific settings of param (requires casting), or
|
||||||
// Optionally, provide thumbnails and image/stream metadata
|
// control generic write settings like sub sampling, source region, output type etc.
|
||||||
writer.write(..., new IIOImage(..., image, ...), param);
|
|
||||||
|
// Optionally, provide thumbnails and image/stream metadata
|
||||||
|
writer.write(..., new IIOImage(..., image, ...), param);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
// Close stream in finally block to avoid resource leaks
|
||||||
|
output.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
// Dispose writer in finally block to avoid memory leaks
|
||||||
|
writer.dispose();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
finally {
|
|
||||||
// Dispose writer in finally block to avoid memory leaks
|
|
||||||
writer.dispose();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
For more advanced usage, and information on how to use the ImageIO API, I suggest you read the
|
For more advanced usage, and information on how to use the ImageIO API, I suggest you read the
|
||||||
[Java Image I/O API Guide](https://docs.oracle.com/javase/7/docs/technotes/guides/imageio/spec/imageio_guideTOC.fm.html)
|
[Java Image I/O API Guide](http://docs.oracle.com/javase/7/docs/technotes/guides/imageio/spec/imageio_guideTOC.fm.html)
|
||||||
from Oracle.
|
from Oracle.
|
||||||
|
|
||||||
#### Adobe Clipping Path support
|
|
||||||
|
|
||||||
```java
|
#### Deploying the plugins in a web app
|
||||||
import com.twelvemonkeys.imageio.path.Paths;
|
|
||||||
|
|
||||||
...
|
Because the `ImageIO` plugin registry (the `IIORegistry`) is "VM global", it doesn't by default work well with
|
||||||
|
servlet contexts. This is especially evident if you load plugins from the `WEB-INF/lib` or `classes` folder.
|
||||||
|
Unless you add `ImageIO.scanForPlugins()` somewhere in your code, the plugins might never be available at all.
|
||||||
|
|
||||||
try (ImageInputStream stream = ImageIO.createImageInputStream(new File("image_with_path.jpg")) {
|
In addition, servlet contexts dynamically loads and unloads classes (using a new class loader per context).
|
||||||
BufferedImage image = Paths.readClipped(stream);
|
If you restart your application, old classes will by default remain in memory forever (because the next time
|
||||||
|
`scanForPlugins` is called, it's another `ClassLoader` that scans/loads classes, and thus they will be new instances
|
||||||
|
in the registry). If a read is attempted using one of the remaining "old" readers, weird exceptions
|
||||||
|
(like `NullPointerException`s when accessing `static final` initialized fields or `NoClassDefFoundError`s
|
||||||
|
for uninitialized inner classes) may occur.
|
||||||
|
|
||||||
// Do something with the clipped image...
|
To work around both the discovery problem and the resource leak,
|
||||||
}
|
it is *strongly recommended* to use the `IIOProviderContextListener` that implements
|
||||||
```
|
dynamic loading and unloading of ImageIO plugins for web applications.
|
||||||
See [Adobe Clipping Path support on the Wiki](https://github.com/haraldk/TwelveMonkeys/wiki/Photoshop-Clipping-Path-support) for more details and example code.
|
|
||||||
|
|
||||||
|
<web-app ...>
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
<listener>
|
||||||
|
<display-name>ImageIO service provider loader/unloader</display-name>
|
||||||
|
<listener-class>com.twelvemonkeys.servlet.image.IIOProviderContextListener</listener-class>
|
||||||
|
</listener>
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
</web-app>
|
||||||
|
|
||||||
|
Loading plugins from `WEB-INF/lib` without the context listener installed is unsupported and will not work correctly.
|
||||||
|
|
||||||
|
The context listener has no dependencies to the TwelveMonkeys ImageIO plugins, and may be used with JAI ImageIO
|
||||||
|
or other ImageIO plugins as well.
|
||||||
|
|
||||||
|
Another safe option, is to place the JAR files in the application server's shared or common lib folder.
|
||||||
|
|
||||||
#### Using the ResampleOp
|
#### Using the ResampleOp
|
||||||
|
|
||||||
The library comes with a resampling (image resizing) operation, that contains many different algorithms
|
The library comes with a resampling (image resizing) operation, that contains many different algorithms
|
||||||
to provide excellent results at reasonable speed.
|
to provide excellent results at reasonable speed.
|
||||||
|
|
||||||
```java
|
import com.twelvemonkeys.image.ResampleOp;
|
||||||
import com.twelvemonkeys.image.ResampleOp;
|
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|
||||||
BufferedImage input = ...; // Image to resample
|
BufferedImage input = ...; // Image to resample
|
||||||
int width, height = ...; // new width/height
|
int width, height = ...; // new width/height
|
||||||
|
|
||||||
|
BufferedImageOp resampler = new ResampleOp(width, height, ResampleOp.FILTER_LANCZOS); // A good default filter, see class documentation for more info
|
||||||
|
BufferedImage output = resampler.filter(input, null);
|
||||||
|
|
||||||
BufferedImageOp resampler = new ResampleOp(width, height, ResampleOp.FILTER_LANCZOS); // A good default filter, see class documentation for more info
|
|
||||||
BufferedImage output = resampler.filter(input, null);
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Using the DiffusionDither
|
#### Using the DiffusionDither
|
||||||
|
|
||||||
The library comes with a dithering operation, that can be used to convert `BufferedImage`s to `IndexColorModel` using
|
The library comes with a dithering operation, that can be used to convert `BufferedImage`s to `IndexColorModel` using
|
||||||
Floyd-Steinberg error-diffusion dither.
|
Floyd-Steinberg error-diffusion dither.
|
||||||
|
|
||||||
```java
|
import com.twelvemonkeys.image.DiffusionDither;
|
||||||
import com.twelvemonkeys.image.DiffusionDither;
|
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|
||||||
BufferedImage input = ...; // Image to dither
|
BufferedImage input = ...; // Image to dither
|
||||||
|
|
||||||
BufferedImageOp ditherer = new DiffusionDither();
|
BufferedImageOp ditherer = new DiffusionDither();
|
||||||
BufferedImage output = ditherer.filter(input, null);
|
BufferedImage output = ditherer.filter(input, null);
|
||||||
```
|
|
||||||
|
|
||||||
#### Working with damaged images
|
|
||||||
|
|
||||||
When using the normal patterns for loading images, trying to load a damaged image will result in an `IOException` being thrown.
|
|
||||||
|
|
||||||
```java
|
|
||||||
BufferedImage image = null;
|
|
||||||
try {
|
|
||||||
image = ImageIO.read(file);
|
|
||||||
} catch (IOException exception) {
|
|
||||||
// Handle, log a warning/error etc
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
In this scenario, if the image is damaged, and `ImageIO.read` throws an exception, `image` is still `null` - it's not possible for a function to both return a value and throw an exception.
|
|
||||||
|
|
||||||
However, in some cases it may be possible to get usable image data from a damaged image. The way to do this is use an `ImageReadParam` to set a `BufferedImage` as a destination.
|
|
||||||
|
|
||||||
```java
|
|
||||||
int width = reader.getWidth(0);
|
|
||||||
int height = reader.getHeight(0);
|
|
||||||
ImageTypeSpecifier imageType = reader.getRawImageType(0);
|
|
||||||
BufferedImage image = imageType.createBufferedImage(width, height);
|
|
||||||
|
|
||||||
ImageReadParam param = reader.getDefaultReadParam();
|
|
||||||
param.setDestination(image);
|
|
||||||
|
|
||||||
try {
|
|
||||||
reader.read(0, param);
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
// Handle, log a warning/error etc
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
In theory this should work for all plugins, but the result is very much plugin/implementation specific. With some formats and some forms of damaged file, you may get an image that is mostly useful.
|
|
||||||
However, you should be prepared for the possibility this just gives a blank or empty image.
|
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
Download the project (using [Git](https://git-scm.com/downloads)):
|
Download the project (using [Git](http://git-scm.com/downloads)):
|
||||||
|
|
||||||
$ git clone git@github.com:haraldk/TwelveMonkeys.git
|
$ git clone git@github.com:haraldk/TwelveMonkeys.git
|
||||||
|
|
||||||
This should create a folder named `TwelveMonkeys` in your current directory. Change directory to the `TwelveMonkeys`
|
This should create a folder named `TwelveMonkeys` in your current directory. Change directory to the `TwelveMonkeys`
|
||||||
folder, and issue the command below to build.
|
folder, and issue the command below to build.
|
||||||
|
|
||||||
Build the project (using [Maven](https://maven.apache.org/download.cgi)):
|
Build the project (using [Maven](http://maven.apache.org/download.cgi)):
|
||||||
|
|
||||||
$ mvn package
|
$ mvn package
|
||||||
|
|
||||||
Currently, the recommended JDK for making a build is Oracle JDK 8.x.
|
Currently, the recommended JDK for making a build is Oracle JDK 7.x or 8.x.
|
||||||
|
|
||||||
It's possible to build using OpenJDK, but some tests might fail due to some minor differences between the color management systems used. You will need to either disable the tests in question, or build without tests altogether.
|
It's possible to build using OpenJDK, but some tests might fail due to some minor differences between the color management systems used. You will need to either disable the tests in question, or build without tests altogether.
|
||||||
|
|
||||||
@@ -294,12 +461,10 @@ The ImageIO registry and service lookup mechanism will make sure the plugins are
|
|||||||
|
|
||||||
To verify that the JPEG plugin is installed and used at run-time, you could use the following code:
|
To verify that the JPEG plugin is installed and used at run-time, you could use the following code:
|
||||||
|
|
||||||
```java
|
Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName("JPEG");
|
||||||
Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName("JPEG");
|
while (readers.hasNext()) {
|
||||||
while (readers.hasNext()) {
|
System.out.println("reader: " + readers.next());
|
||||||
System.out.println("reader: " + readers.next());
|
}
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The first line should print:
|
The first line should print:
|
||||||
|
|
||||||
@@ -309,209 +474,143 @@ The first line should print:
|
|||||||
|
|
||||||
To depend on the JPEG and TIFF plugin using Maven, add the following to your POM:
|
To depend on the JPEG and TIFF plugin using Maven, add the following to your POM:
|
||||||
|
|
||||||
```xml
|
|
||||||
...
|
|
||||||
<dependencies>
|
|
||||||
...
|
...
|
||||||
<dependency>
|
<dependencies>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
...
|
||||||
<artifactId>imageio-jpeg</artifactId>
|
<dependency>
|
||||||
<version>3.13.1</version>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
</dependency>
|
<artifactId>imageio-jpeg</artifactId>
|
||||||
<dependency>
|
<version>3.3.2</version> <!-- Alternatively, build your own version -->
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
</dependency>
|
||||||
<artifactId>imageio-tiff</artifactId>
|
<dependency>
|
||||||
<version>3.13.1</version>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
</dependency>
|
<artifactId>imageio-tiff</artifactId>
|
||||||
|
<version>3.3.2</version> <!-- Alternatively, build your own version -->
|
||||||
<!--
|
</dependency>
|
||||||
Optional dependency. Needed only if you deploy ImageIO plugins as part of a web app.
|
</dependencies>
|
||||||
Make sure you add the IIOProviderContextListener to your web.xml, see above.
|
|
||||||
-->
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.twelvemonkeys.servlet</groupId>
|
|
||||||
<artifactId>servlet</artifactId>
|
|
||||||
<version>3.13.1</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Or Jakarta version, for Servlet API 5.0
|
|
||||||
-->
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.twelvemonkeys.servlet</groupId>
|
|
||||||
<artifactId>servlet</artifactId>
|
|
||||||
<version>3.13.1</version>
|
|
||||||
<classifier>jakarta</classifier>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Manual dependency example
|
#### Manual dependency example
|
||||||
|
|
||||||
To depend on the JPEG and TIFF plugin in your IDE or program, add all of the following JARs to your class path:
|
To depend on the JPEG and TIFF plugin in your IDE or program, add all of the following JARs to your class path:
|
||||||
|
|
||||||
twelvemonkeys-common-lang-3.13.1.jar
|
twelvemonkeys-common-lang-3.3.2.jar
|
||||||
twelvemonkeys-common-io-3.13.1.jar
|
twelvemonkeys-common-io-3.3.2.jar
|
||||||
twelvemonkeys-common-image-3.13.1.jar
|
twelvemonkeys-common-image-3.3.2.jar
|
||||||
twelvemonkeys-imageio-core-3.13.1.jar
|
twelvemonkeys-imageio-core-3.3.2.jar
|
||||||
twelvemonkeys-imageio-metadata-3.13.1.jar
|
twelvemonkeys-imageio-metadata-3.3.2.jar
|
||||||
twelvemonkeys-imageio-jpeg-3.13.1.jar
|
twelvemonkeys-imageio-jpeg-3.3.2.jar
|
||||||
twelvemonkeys-imageio-tiff-3.13.1.jar
|
twelvemonkeys-imageio-tiff-3.3.2.jar
|
||||||
|
|
||||||
#### Deploying the plugins in a web app
|
|
||||||
|
|
||||||
Because the `ImageIO` plugin registry (the `IIORegistry`) is "VM global", it does not work well with
|
|
||||||
servlet contexts as-is. This is especially evident if you load plugins from the `WEB-INF/lib` or `classes` folder.
|
|
||||||
Unless you add `ImageIO.scanForPlugins()` somewhere in your code, the plugins might never be available at all.
|
|
||||||
|
|
||||||
In addition, servlet contexts dynamically loads and unloads classes (using a new class loader per context).
|
|
||||||
If you restart your application, old classes will by default remain in memory forever (because the next time
|
|
||||||
`scanForPlugins` is called, it's another `ClassLoader` that scans/loads classes, and thus they will be new instances
|
|
||||||
in the registry). If a read is attempted using one of the remaining "old" readers, weird exceptions
|
|
||||||
(like `NullPointerException`s when accessing `static final` initialized fields or `NoClassDefFoundError`s
|
|
||||||
for uninitialized inner classes) may occur.
|
|
||||||
|
|
||||||
To work around both the discovery problem and the resource leak,
|
|
||||||
it is *strongly recommended* to use the `IIOProviderContextListener` that implements
|
|
||||||
dynamic loading and unloading of ImageIO plugins for web applications.
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<web-app ...>
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
<listener>
|
|
||||||
<display-name>ImageIO service provider loader/unloader</display-name>
|
|
||||||
<listener-class>com.twelvemonkeys.servlet.image.IIOProviderContextListener</listener-class>
|
|
||||||
</listener>
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
</web-app>
|
|
||||||
```
|
|
||||||
|
|
||||||
Loading plugins from `WEB-INF/lib` without the context listener installed is unsupported and will not work correctly.
|
|
||||||
|
|
||||||
The context listener has no dependencies to the TwelveMonkeys ImageIO plugins, and may be used with JAI ImageIO
|
|
||||||
or other ImageIO plugins as well.
|
|
||||||
|
|
||||||
Another safe option, is to place the JAR files in the application server's shared or common lib folder.
|
|
||||||
|
|
||||||
##### Jakarta Servlet Support
|
|
||||||
|
|
||||||
For those transitioning from the old `javax.servlet` to the new `jakarta.servlet` package, there is a separate
|
|
||||||
dependency available. It contains exactly the same servlet classes as mentioned above, but built against the new Jakarta EE
|
|
||||||
packages. The dependency has the same group name and identifier as before, but a `jakarta` *classifier* appended, to
|
|
||||||
distinguish it from the non-Jakarta package.
|
|
||||||
|
|
||||||
See the [Maven dependency example](#maven-dependency-example) for how to enable it with Maven.
|
|
||||||
Gradle or other build tools will have similar options.
|
|
||||||
|
|
||||||
#### Including the plugins in a "fat" JAR
|
|
||||||
|
|
||||||
The recommended way to use the plugins, is just to include the JARs as-is in your project, through a Maven dependency or similar.
|
|
||||||
Re-packaging is not necessary to use the library, and not recommended.
|
|
||||||
|
|
||||||
However, if you like to create a "fat"
|
|
||||||
JAR, or otherwise like to re-package the JARs for some reason, it's important to remember that automatic discovery of
|
|
||||||
the plugins by ImageIO depends on the
|
|
||||||
[Service Provider Interface (SPI)](https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html) mechanism.
|
|
||||||
In short, each JAR contains a special folder, named `META-INF/services` containing one or more files,
|
|
||||||
typically `javax.imageio.spi.ImageReaderSpi` and `javax.imageio.spi.ImageWriterSpi`.
|
|
||||||
These files exist *with the same name in every JAR*,
|
|
||||||
so if you simply unpack everything to a single folder or create a JAR, files will be overwritten and behavior be
|
|
||||||
unspecified (most likely you will end up with a single plugin being installed).
|
|
||||||
|
|
||||||
The solution is to make sure all files with the same name, are merged to a single file,
|
|
||||||
containing all the SPI information of each type. If using the Maven Shade plugin, you should use the
|
|
||||||
[ServicesResourceTransformer](https://maven.apache.org/plugins/maven-shade-plugin/examples/resource-transformers.html#ServicesResourceTransformer)
|
|
||||||
to properly merge these files. You may also want to use the
|
|
||||||
[ManifestResourceTransforme](https://maven.apache.org/plugins/maven-shade-plugin/examples/resource-transformers.html#ManifestResourceTransformer)
|
|
||||||
to get the correct vendor name, version info etc.
|
|
||||||
Other "fat" JAR bundlers will probably have similar mechanisms to merge entries with the same name.
|
|
||||||
|
|
||||||
### Links to prebuilt binaries
|
### Links to prebuilt binaries
|
||||||
|
|
||||||
##### Latest version (3.13.1)
|
##### Latest version (3.2.x)
|
||||||
|
|
||||||
The latest version that will run on Java 7 is 3.9.4. Later versions will require Java 8 or later.
|
Requires Java 7 or later.
|
||||||
|
|
||||||
Common dependencies
|
Common dependencies
|
||||||
* [common-lang-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-lang/3.13.1/common-lang-3.13.1.jar)
|
* [common-lang-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-lang/3.3.2/common-lang-3.3.2.jar)
|
||||||
* [common-io-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-io/3.13.1/common-io-3.13.1.jar)
|
* [common-io-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-io/3.3.2/common-io-3.3.2.jar)
|
||||||
* [common-image-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-image/3.13.1/common-image-3.13.1.jar)
|
* [common-image-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-image/3.3.2/common-image-3.3.2.jar)
|
||||||
|
|
||||||
ImageIO dependencies
|
ImageIO dependencies
|
||||||
* [imageio-core-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-core/3.13.1/imageio-core-3.13.1.jar)
|
* [imageio-core-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-core/3.3.2/imageio-core-3.3.2.jar)
|
||||||
* [imageio-metadata-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-metadata/3.13.1/imageio-metadata-3.13.1.jar)
|
* [imageio-metadata-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-metadata/3.3.2/imageio-metadata-3.3.2.jar)
|
||||||
|
|
||||||
ImageIO plugins
|
ImageIO plugins
|
||||||
* [imageio-bmp-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-bmp/3.13.1/imageio-bmp-3.13.1.jar)
|
* [imageio-bmp-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-bmp/3.3.2/imageio-bmp-3.3.2.jar)
|
||||||
* [imageio-dds-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-dds/3.13.1/imageio-dds-3.13.1.jar)
|
* [imageio-jpeg-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jpeg/3.3.2/imageio-jpeg-3.3.2.jar)
|
||||||
* [imageio-hdr-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-hdr/3.13.1/imageio-hdr-3.13.1.jar)
|
* [imageio-tiff-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tiff/3.3.2/imageio-tiff-3.3.2.jar)
|
||||||
* [imageio-icns-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-icns/3.13.1/imageio-icns-3.13.1.jar)
|
* [imageio-pnm-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pnm/3.3.2/imageio-pnm-3.3.2.jar)
|
||||||
* [imageio-iff-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-iff/3.13.1/imageio-iff-3.13.1.jar)
|
* [imageio-psd-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-psd/3.3.2/imageio-psd-3.3.2.jar)
|
||||||
* [imageio-jpeg-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jpeg/3.13.1/imageio-jpeg-3.13.1.jar)
|
* [imageio-hdr-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-hdr/3.3.2/imageio-hdr-3.3.2.jar)
|
||||||
* [imageio-pcx-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pcx/3.13.1/imageio-pcx-3.13.1.jar)
|
* [imageio-iff-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-iff/3.3.2/imageio-iff-3.3.2.jar)
|
||||||
* [imageio-pict-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pict/3.13.1/imageio-pict-3.13.1.jar)
|
* [imageio-pcx-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pcx/3.3.2/imageio-pcx-3.3.2.jar)
|
||||||
* [imageio-pnm-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pnm/3.13.1/imageio-pnm-3.13.1.jar)
|
* [imageio-pict-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pict/3.3.2/imageio-pict-3.3.2.jar)
|
||||||
* [imageio-psd-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-psd/3.13.1/imageio-psd-3.13.1.jar)
|
* [imageio-sgi-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-sgi/3.3.2/imageio-sgi-3.3.2.jar)
|
||||||
* [imageio-sgi-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-sgi/3.13.1/imageio-sgi-3.13.1.jar)
|
* [imageio-tga-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tga/3.3.2/imageio-tga-3.3.2.jar)
|
||||||
* [imageio-tga-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tga/3.13.1/imageio-tga-3.13.1.jar)
|
* [imageio-icns-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-icns/3.3.2/imageio-icns-3.3.2.jar)
|
||||||
* [imageio-thumbsdb-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-thumbsdb/3.13.1/imageio-thumbsdb-3.13.1.jar)
|
* [imageio-thumbsdb-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-thumbsdb/3.3.2/imageio-thumbsdb-3.3.2.jar)
|
||||||
* [imageio-tiff-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tiff/3.13.1/imageio-tiff-3.13.1.jar)
|
|
||||||
* [imageio-webp-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-webp/3.13.1/imageio-webp-3.13.1.jar)
|
|
||||||
* [imageio-xwd-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-xwd/3.13.1/imageio-xwd-3.13.1.jar)
|
|
||||||
|
|
||||||
ImageIO plugins requiring 3rd party libs
|
ImageIO plugins requiring 3rd party libs
|
||||||
* [imageio-batik-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-batik/3.13.1/imageio-batik-3.13.1.jar)
|
* [imageio-batik-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-batik/3.3.2/imageio-batik-3.3.2.jar)
|
||||||
|
|
||||||
Photoshop Path support for ImageIO
|
Photoshop Path support for ImageIO
|
||||||
* [imageio-clippath-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-clippath/3.13.1/imageio-clippath-3.13.1.jar)
|
* [imageio-clippath-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-clippath/3.3.2/imageio-clippath-3.3.2.jar)
|
||||||
|
|
||||||
Servlet support
|
Servlet support
|
||||||
* [servlet-3.13.1.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/servlet/servlet/3.13.1/servlet-3.13.1.jar) for legacy Java EE (javax.servlet)
|
* [servlet-3.3.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/servlet/servlet/3.3.2/servlet-3.3.2.jar)
|
||||||
* [servlet-3.13.1-jakarta.jar](https://search.maven.org/remotecontent?filepath=com/twelvemonkeys/servlet/servlet/3.13.1/servlet-3.13.1-jakrta.jar) for Jakarta EE (jakarta.servlet)
|
|
||||||
|
##### Old version (3.0.x)
|
||||||
|
|
||||||
|
Use this version for projects that requires Java 6 or need the JMagick support. *Does not support Java 8 or later*.
|
||||||
|
|
||||||
|
Common dependencies
|
||||||
|
* [common-lang-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-lang/3.0.2/common-lang-3.0.2.jar)
|
||||||
|
* [common-io-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-io/3.0.2/common-io-3.0.2.jar)
|
||||||
|
* [common-image-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/common/common-image/3.0.2/common-image-3.0.2.jar)
|
||||||
|
|
||||||
|
ImageIO dependencies
|
||||||
|
* [imageio-core-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-core/3.0.2/imageio-core-3.0.2.jar)
|
||||||
|
* [imageio-metadata-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-metadata/3.0.2/imageio-metadata-3.0.2.jar)
|
||||||
|
|
||||||
|
ImageIO plugins
|
||||||
|
* [imageio-jpeg-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jpeg/3.0.2/imageio-jpeg-3.0.2.jar)
|
||||||
|
* [imageio-tiff-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-tiff/3.0.2/imageio-tiff-3.0.2.jar)
|
||||||
|
* [imageio-psd-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-psd/3.0.2/imageio-psd-3.0.2.jar)
|
||||||
|
* [imageio-pict-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-pict/3.0.2/imageio-pict-3.0.2.jar)
|
||||||
|
* [imageio-iff-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-iff/3.0.2/imageio-iff-3.0.2.jar)
|
||||||
|
* [imageio-icns-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-icns/3.0.2/imageio-icns-3.0.2.jar)
|
||||||
|
* [imageio-ico-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-ico/3.0.2/imageio-ico-3.0.2.jar)
|
||||||
|
* [imageio-thumbsdb-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-thumbsdb/3.0.2/imageio-thumbsdb-3.0.2.jar)
|
||||||
|
|
||||||
|
ImageIO plugins requiring 3rd party libs
|
||||||
|
* [imageio-batik-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-batik/3.0.2/imageio-batik-3.0.2.jar)
|
||||||
|
* [imageio-jmagick-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/imageio/imageio-jmagick/3.0.2/imageio-jmagick-3.0.2.jar)
|
||||||
|
|
||||||
|
Servlet support
|
||||||
|
* [servlet-3.0.2.jar](http://search.maven.org/remotecontent?filepath=com/twelvemonkeys/servlet/servlet/3.0.2/servlet-3.0.2.jar)
|
||||||
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This project is provided under the OSI approved [BSD license](https://opensource.org/licenses/BSD-3-Clause):
|
The project is distributed under the OSI approved [BSD license](http://opensource.org/licenses/BSD-3-Clause):
|
||||||
|
|
||||||
Copyright (c) 2008-2022, Harald Kuhr
|
Copyright (c) 2008-2015, Harald Kuhr
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
modification, are permitted provided that the following conditions are met:
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
o Redistributions of source code must retain the above copyright notice, this
|
o Redistributions of source code must retain the above copyright
|
||||||
list of conditions and the following disclaimer.
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
o Redistributions in binary form must reproduce the above copyright notice,
|
o Redistributions in binary form must reproduce the above copyright
|
||||||
this list of conditions and the following disclaimer in the documentation
|
notice, this list of conditions and the following disclaimer in the
|
||||||
and/or other materials provided with the distribution.
|
documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
o Neither the name of the copyright holder nor the names of its
|
o Neither the name "TwelveMonkeys" nor the
|
||||||
contributors may be used to endorse or promote products derived from
|
names of its contributors may be used to endorse or promote products
|
||||||
this software without specific prior written permission.
|
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.
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
|
|
||||||
q: How do I use it?
|
q: How do I use it?
|
||||||
|
|
||||||
a: The easiest way is to build your own project using Maven, Gradle or other build tool with dependency management,
|
a: The easiest way is to build your own project using Maven, and just add dependencies to the specific plug-ins you need.
|
||||||
and just add dependencies to the specific plug-ins you need.
|
If you don't use Maven, make sure you have all the necessary JARs in classpath. See the Install section above.
|
||||||
If you don't use such a build tool, make sure you have all the necessary JARs in classpath. See the Install section above.
|
|
||||||
|
|
||||||
|
|
||||||
q: What changes do I have to make to my code in order to use the plug-ins?
|
q: What changes do I have to make to my code in order to use the plug-ins?
|
||||||
@@ -529,47 +628,28 @@ q: How does it work?
|
|||||||
|
|
||||||
a: The TwelveMonkeys ImageIO project contains plug-ins for ImageIO. ImageIO uses a service lookup mechanism, to discover plug-ins at runtime.
|
a: The TwelveMonkeys ImageIO project contains plug-ins for ImageIO. ImageIO uses a service lookup mechanism, to discover plug-ins at runtime.
|
||||||
|
|
||||||
All you have to do, is to make sure you have the TwelveMonkeys ImageIO JARs in your classpath.
|
All you have have to do, is to make sure you have the TwelveMonkeys JARs in your classpath.
|
||||||
|
|
||||||
You can read more about the registry and the lookup mechanism in the [IIORegistry API doc](https://docs.oracle.com/javase/7/docs/api/javax/imageio/spi/IIORegistry.html).
|
You can read more about the registry and the lookup mechanism in the [IIORegistry API doc](http://docs.oracle.com/javase/7/docs/api/javax/imageio/spi/IIORegistry.html).
|
||||||
|
|
||||||
The fine print: The TwelveMonkeys service providers for JPEG, BMP and TIFF, overrides the onRegistration method, and
|
The fine print: The TwelveMonkeys service providers for JPEG, BMP and TIFF, overrides the onRegistration method, and
|
||||||
utilizes the pairwise partial ordering mechanism of the `IIOServiceRegistry` to make sure it is installed before
|
utilizes the pairwise partial ordering mechanism of the `IIOServiceRegistry` to make sure it is installed before
|
||||||
the Sun/Oracle provided `JPEGImageReader`, `BMPImageReader` `TIFFImageReader`, and the Apple provided `TIFFImageReader` on OS X,
|
the Sun/Oracle provided `JPEGImageReader` and `BMPImageReader`, and the Apple provided `TIFFImageReader` on OS X,
|
||||||
respectively. Using the pairwise ordering will not remove any functionality form these implementations, but in most
|
respectively. Using the pairwise ordering will not remove any functionality form these implementations, but in most
|
||||||
cases you'll end up using the TwelveMonkeys plug-ins instead.
|
cases you'll end up using the TwelveMonkeys plug-ins instead.
|
||||||
|
|
||||||
|
|
||||||
q: Why is there no support for common formats like GIF or PNG?
|
|
||||||
|
|
||||||
a: The short answer is simply that the built-in support in ImageIO for these formats are considered good enough as-is.
|
|
||||||
If you are looking for better PNG write performance on Java 7 and 8, see [JDK9 PNG Writer Backport](https://github.com/gredler/jdk9-png-writer-backport).
|
|
||||||
|
|
||||||
|
|
||||||
q: When is the next release? What is the current release schedule?
|
|
||||||
|
|
||||||
a: The goal is to make monthly releases, containing bug fixes and minor new features.
|
|
||||||
And quarterly releases with more "major" features.
|
|
||||||
|
|
||||||
|
|
||||||
q: I love this project! How can I help?
|
|
||||||
|
|
||||||
a: Have a look at the open issues, and see if there are any issues you can help fix, or provide sample file or create test cases for.
|
|
||||||
It is also possible for you or your organization to become a sponsor, through GitHub Sponsors.
|
|
||||||
Providing funding will allow us to spend more time on fixing bugs and implementing new features.
|
|
||||||
|
|
||||||
|
|
||||||
q: What about JAI? Several of the formats are already supported by JAI.
|
q: What about JAI? Several of the formats are already supported by JAI.
|
||||||
|
|
||||||
a: While JAI (and jai-imageio in particular) have support for some of the same formats, JAI has some major issues.
|
a: While JAI (and jai-imageio in particular) have support for some of the formats, JAI has some major issues.
|
||||||
The most obvious being:
|
The most obvious being:
|
||||||
- It's not actively developed. No issue has been fixed for years.
|
- It's not actively developed. No issues has been fixed for years.
|
||||||
- To get full format support, you need native libs.
|
- To get full format support, you need native libs.
|
||||||
Native libs does not exist for several popular platforms/architectures, and further the native libs are not open source.
|
Native libs does not exist for several popular platforms/architectures, and further the native libs are not open source.
|
||||||
Some environments may also prevent deployment of native libs, which brings us back to square one.
|
Some environments may also prevent deployment of native libs, which brings us back to square one.
|
||||||
|
|
||||||
|
|
||||||
q: What about JMagick or IM4Java? Can't you just use what's already available?
|
q: What about JMagick or IM4Java? Can't you just use what´s already available?
|
||||||
|
|
||||||
a: While great libraries with a wide range of formats support, the ImageMagick-based libraries has some disadvantages
|
a: While great libraries with a wide range of formats support, the ImageMagick-based libraries has some disadvantages
|
||||||
compared to ImageIO.
|
compared to ImageIO.
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
# Security Policy
|
|
||||||
|
|
||||||
To report a security issue, please disclose it at [security advisory](https://github.com/haraldk/TwelveMonkeys/security/advisories/new).
|
|
||||||
|
|
||||||
Vulnerabilities will be disclosed in a best effort base.
|
|
||||||
+2
-17
@@ -5,13 +5,13 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys</groupId>
|
<groupId>com.twelvemonkeys</groupId>
|
||||||
<artifactId>twelvemonkeys</artifactId>
|
<artifactId>twelvemonkeys</artifactId>
|
||||||
<version>3.13.2-SNAPSHOT</version>
|
<version>3.4.1</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<groupId>com.twelvemonkeys.bom</groupId>
|
<groupId>com.twelvemonkeys.bom</groupId>
|
||||||
<artifactId>bom</artifactId>
|
<artifactId>bom</artifactId>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
<name>TwelveMonkeys » BOM</name>
|
<name>TwelveMonkeys :: BOM</name>
|
||||||
<description>
|
<description>
|
||||||
TwelveMonkeys "Bill of Materials" (BOM).
|
TwelveMonkeys "Bill of Materials" (BOM).
|
||||||
</description>
|
</description>
|
||||||
@@ -63,11 +63,6 @@
|
|||||||
<artifactId>imageio-hdr</artifactId>
|
<artifactId>imageio-hdr</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
|
||||||
<artifactId>imageio-dds</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio-icns</artifactId>
|
<artifactId>imageio-icns</artifactId>
|
||||||
@@ -128,16 +123,6 @@
|
|||||||
<artifactId>imageio-tiff</artifactId>
|
<artifactId>imageio-tiff</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
|
||||||
<artifactId>imageio-webp</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
|
||||||
<artifactId>imageio-xwd</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- ImageIO 3rd party dependent plugins -->
|
<!-- ImageIO 3rd party dependent plugins -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|||||||
+10
-16
@@ -4,19 +4,15 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.common</groupId>
|
<groupId>com.twelvemonkeys.common</groupId>
|
||||||
<artifactId>common</artifactId>
|
<artifactId>common</artifactId>
|
||||||
<version>3.13.2-SNAPSHOT</version>
|
<version>3.4.1</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>common-image</artifactId>
|
<artifactId>common-image</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
<name>TwelveMonkeys » Common » Image</name>
|
<name>TwelveMonkeys :: Common :: Image</name>
|
||||||
<description>
|
<description>
|
||||||
TwelveMonkeys Common image support classes.
|
The TwelveMonkeys Common Image support
|
||||||
</description>
|
</description>
|
||||||
|
|
||||||
<properties>
|
|
||||||
<project.jpms.module.name>com.twelvemonkeys.common.image</project.jpms.module.name>
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>${project.groupId}</groupId>
|
<groupId>${project.groupId}</groupId>
|
||||||
@@ -28,14 +24,12 @@
|
|||||||
<artifactId>common-io</artifactId>
|
<artifactId>common-io</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>jmagick</groupId>
|
||||||
|
<artifactId>jmagick</artifactId>
|
||||||
|
<version>6.2.4</version>
|
||||||
|
<optional>true</optional>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.felix</groupId>
|
|
||||||
<artifactId>maven-bundle-plugin</artifactId>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import java.util.List;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* AbstractImageSource
|
* AbstractImageSource
|
||||||
|
* <p/>
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/AbstractImageSource.java#1 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/AbstractImageSource.java#1 $
|
||||||
|
|||||||
@@ -34,13 +34,7 @@ import java.awt.*;
|
|||||||
import java.awt.geom.AffineTransform;
|
import java.awt.geom.AffineTransform;
|
||||||
import java.awt.geom.Point2D;
|
import java.awt.geom.Point2D;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.*;
|
||||||
import java.awt.image.BufferedImageOp;
|
|
||||||
import java.awt.image.ColorModel;
|
|
||||||
import java.awt.image.ImagingOpException;
|
|
||||||
import java.awt.image.Raster;
|
|
||||||
import java.awt.image.RasterOp;
|
|
||||||
import java.awt.image.WritableRaster;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a drop-in replacement for {@link java.awt.image.AffineTransformOp}.
|
* 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);
|
delegate = new java.awt.image.AffineTransformOp(xform, interpolationType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("ConstantConditions")
|
|
||||||
@Override
|
@Override
|
||||||
public BufferedImage filter(final BufferedImage src, BufferedImage dst) {
|
public BufferedImage filter(final BufferedImage src, BufferedImage dst) {
|
||||||
try {
|
try {
|
||||||
@@ -87,9 +80,10 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp {
|
|||||||
dst = createCompatibleDestImage(src, src.getColorModel());
|
dst = createCompatibleDestImage(src, src.getColorModel());
|
||||||
}
|
}
|
||||||
|
|
||||||
Graphics2D g2d = dst.createGraphics();
|
Graphics2D g2d = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
g2d = dst.createGraphics();
|
||||||
int interpolationType = delegate.getInterpolationType();
|
int interpolationType = delegate.getInterpolationType();
|
||||||
|
|
||||||
if (interpolationType > 0) {
|
if (interpolationType > 0) {
|
||||||
@@ -115,7 +109,9 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp {
|
|||||||
return dst;
|
return dst;
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
g2d.dispose();
|
if (g2d != null) {
|
||||||
|
g2d.dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-11
@@ -35,15 +35,14 @@ import java.awt.image.RGBImageFilter;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Adjusts the contrast and brightness of an image.
|
* Adjusts the contrast and brightness of an image.
|
||||||
* <p>
|
* <p/>
|
||||||
* For brightness, the valid range is {@code -2.0,..,0.0,..,2.0}.
|
* For brightness, the valid range is {@code -2.0,..,0.0,..,2.0}.
|
||||||
* A value of {@code 0.0} means no change.
|
* A value of {@code 0.0} means no change.
|
||||||
* Negative values will make the pixels darker.
|
* Negative values will make the pixels darker.
|
||||||
* Maximum negative value ({@code -2}) will make all filtered pixels black.
|
* Maximum negative value ({@code -2}) will make all filtered pixels black.
|
||||||
* Positive values will make the pixels brighter.
|
* Positive values will make the pixels brighter.
|
||||||
* Maximum positive value ({@code 2}) will make all filtered pixels white.
|
* Maximum positive value ({@code 2}) will make all filtered pixels white.
|
||||||
* </p>
|
* <p/>
|
||||||
* <p>
|
|
||||||
* For contrast, the valid range is {@code -1.0,..,0.0,..,1.0}.
|
* For contrast, the valid range is {@code -1.0,..,0.0,..,1.0}.
|
||||||
* A value of {@code 0.0} means no change.
|
* A value of {@code 0.0} means no change.
|
||||||
* Negative values will reduce contrast.
|
* Negative values will reduce contrast.
|
||||||
@@ -52,14 +51,15 @@ import java.awt.image.RGBImageFilter;
|
|||||||
* Positive values will increase contrast.
|
* Positive values will increase contrast.
|
||||||
* Maximum positive value ({@code 1}) will make all filtered pixels primary
|
* Maximum positive value ({@code 1}) will make all filtered pixels primary
|
||||||
* colors (either black, white, cyan, magenta, yellow, red, blue or green).
|
* colors (either black, white, cyan, magenta, yellow, red, blue or green).
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @author last modified by $Author: haku $
|
* @author last modified by $Author: haku $
|
||||||
*
|
*
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/BrightnessContrastFilter.java#1 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/BrightnessContrastFilter.java#1 $
|
||||||
|
*
|
||||||
|
* @todo consider doing something similar to http://archives.java.sun.com/cgi-bin/wa?A2=ind0302&L=jai-interest&F=&S=&P=15947
|
||||||
*/
|
*/
|
||||||
// TODO: consider doing something similar to http://archives.java.sun.com/cgi-bin/wa?A2=ind0302&L=jai-interest&F=&S=&P=15947
|
|
||||||
public class BrightnessContrastFilter extends RGBImageFilter {
|
public class BrightnessContrastFilter extends RGBImageFilter {
|
||||||
|
|
||||||
// TODO: Replace with RescaleOp?
|
// TODO: Replace with RescaleOp?
|
||||||
@@ -76,9 +76,8 @@ public class BrightnessContrastFilter extends RGBImageFilter {
|
|||||||
/**
|
/**
|
||||||
* Creates a BrightnessContrastFilter with default values
|
* Creates a BrightnessContrastFilter with default values
|
||||||
* ({@code brightness=0.3, contrast=0.3}).
|
* ({@code brightness=0.3, contrast=0.3}).
|
||||||
* <p>
|
* <p/>
|
||||||
* This will slightly increase both brightness and contrast.
|
* This will slightly increase both brightness and contrast.
|
||||||
* </p>
|
|
||||||
*/
|
*/
|
||||||
public BrightnessContrastFilter() {
|
public BrightnessContrastFilter() {
|
||||||
this(0.3f, 0.3f);
|
this(0.3f, 0.3f);
|
||||||
@@ -87,15 +86,14 @@ public class BrightnessContrastFilter extends RGBImageFilter {
|
|||||||
/**
|
/**
|
||||||
* Creates a BrightnessContrastFilter with the given values for brightness
|
* Creates a BrightnessContrastFilter with the given values for brightness
|
||||||
* and contrast.
|
* and contrast.
|
||||||
* <p>
|
* <p/>
|
||||||
* For brightness, the valid range is {@code -2.0,..,0.0,..,2.0}.
|
* For brightness, the valid range is {@code -2.0,..,0.0,..,2.0}.
|
||||||
* A value of {@code 0.0} means no change.
|
* A value of {@code 0.0} means no change.
|
||||||
* Negative values will make the pixels darker.
|
* Negative values will make the pixels darker.
|
||||||
* Maximum negative value ({@code -2}) will make all filtered pixels black.
|
* Maximum negative value ({@code -2}) will make all filtered pixels black.
|
||||||
* Positive values will make the pixels brighter.
|
* Positive values will make the pixels brighter.
|
||||||
* Maximum positive value ({@code 2}) will make all filtered pixels white.
|
* Maximum positive value ({@code 2}) will make all filtered pixels white.
|
||||||
* </p>
|
* <p/>
|
||||||
* <p>
|
|
||||||
* For contrast, the valid range is {@code -1.0,..,0.0,..,1.0}.
|
* For contrast, the valid range is {@code -1.0,..,0.0,..,1.0}.
|
||||||
* A value of {@code 0.0} means no change.
|
* A value of {@code 0.0} means no change.
|
||||||
* Negative values will reduce contrast.
|
* Negative values will reduce contrast.
|
||||||
@@ -104,7 +102,6 @@ public class BrightnessContrastFilter extends RGBImageFilter {
|
|||||||
* Positive values will increase contrast.
|
* Positive values will increase contrast.
|
||||||
* Maximum positive value ({@code 1}) will make all filtered pixels primary
|
* Maximum positive value ({@code 1}) will make all filtered pixels primary
|
||||||
* colors (either black, white, cyan, magenta, yellow, red, blue or green).
|
* colors (either black, white, cyan, magenta, yellow, red, blue or green).
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @param pBrightness adjust the brightness of the image, in the range
|
* @param pBrightness adjust the brightness of the image, in the range
|
||||||
* {@code -2.0,..,0.0,..,2.0}.
|
* {@code -2.0,..,0.0,..,2.0}.
|
||||||
|
|||||||
+89
-101
@@ -44,16 +44,14 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
|||||||
* A faster, lighter and easier way to convert an {@code Image} to a
|
* A faster, lighter and easier way to convert an {@code Image} to a
|
||||||
* {@code BufferedImage} than using a {@code PixelGrabber}.
|
* {@code BufferedImage} than using a {@code PixelGrabber}.
|
||||||
* Clients may provide progress listeners to monitor conversion progress.
|
* Clients may provide progress listeners to monitor conversion progress.
|
||||||
* <p>
|
* <p/>
|
||||||
* Supports source image subsampling and source region extraction.
|
* Supports source image subsampling and source region extraction.
|
||||||
* Supports source images with 16 bit {@link ColorModel} and
|
* Supports source images with 16 bit {@link ColorModel} and
|
||||||
* {@link DataBuffer#TYPE_USHORT} transfer type, without converting to
|
* {@link DataBuffer#TYPE_USHORT} transfer type, without converting to
|
||||||
* 32 bit/TYPE_INT.
|
* 32 bit/TYPE_INT.
|
||||||
* </p>
|
* <p/>
|
||||||
* <p>
|
|
||||||
* NOTE: Does not support images with more than one {@code ColorModel} or
|
* NOTE: Does not support images with more than one {@code ColorModel} or
|
||||||
* different types of pixel data. This is not very common.
|
* different types of pixel data. This is not very common.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/BufferedImageFactory.java#1 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/BufferedImageFactory.java#1 $
|
||||||
@@ -79,7 +77,7 @@ public final class BufferedImageFactory {
|
|||||||
private int scanSize;
|
private int scanSize;
|
||||||
|
|
||||||
private ColorModel sourceColorModel;
|
private ColorModel sourceColorModel;
|
||||||
private Hashtable<?, ?> sourceProperties; // ImageConsumer API dictates Hashtable
|
private Hashtable sourceProperties; // ImageConsumer API dictates Hashtable
|
||||||
|
|
||||||
private Object sourcePixels;
|
private Object sourcePixels;
|
||||||
|
|
||||||
@@ -91,21 +89,21 @@ public final class BufferedImageFactory {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@code BufferedImageFactory}.
|
* Creates a {@code BufferedImageFactory}.
|
||||||
* @param source the source image
|
* @param pSource the source image
|
||||||
* @throws IllegalArgumentException if {@code source == null}
|
* @throws IllegalArgumentException if {@code pSource == null}
|
||||||
*/
|
*/
|
||||||
public BufferedImageFactory(final Image source) {
|
public BufferedImageFactory(final Image pSource) {
|
||||||
this(source != null ? source.getSource() : null);
|
this(pSource != null ? pSource.getSource() : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@code BufferedImageFactory}.
|
* Creates a {@code BufferedImageFactory}.
|
||||||
* @param source the source image producer
|
* @param pSource the source image producer
|
||||||
* @throws IllegalArgumentException if {@code source == null}
|
* @throws IllegalArgumentException if {@code pSource == null}
|
||||||
*/
|
*/
|
||||||
public BufferedImageFactory(final ImageProducer source) {
|
public BufferedImageFactory(final ImageProducer pSource) {
|
||||||
Validate.notNull(source, "source");
|
Validate.notNull(pSource, "source");
|
||||||
producer = source;
|
producer = pSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -155,44 +153,44 @@ public final class BufferedImageFactory {
|
|||||||
/**
|
/**
|
||||||
* Sets the source region (AOI) for the new image.
|
* Sets the source region (AOI) for the new image.
|
||||||
*
|
*
|
||||||
* @param region the source region
|
* @param pRegion the source region
|
||||||
*/
|
*/
|
||||||
public void setSourceRegion(final Rectangle region) {
|
public void setSourceRegion(final Rectangle pRegion) {
|
||||||
// Re-fetch everything, if region changed
|
// Re-fetch everything, if region changed
|
||||||
if (x != region.x || y != region.y || width != region.width || height != region.height) {
|
if (x != pRegion.x || y != pRegion.y || width != pRegion.width || height != pRegion.height) {
|
||||||
dispose();
|
dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
x = region.x;
|
x = pRegion.x;
|
||||||
y = region.y;
|
y = pRegion.y;
|
||||||
width = region.width;
|
width = pRegion.width;
|
||||||
height = region.height;
|
height = pRegion.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the source subsampling for the new image.
|
* Sets the source subsampling for the new image.
|
||||||
*
|
*
|
||||||
* @param xSubsampling horizontal subsampling factor
|
* @param pXSub horizontal subsampling factor
|
||||||
* @param ySubsampling vertical subsampling factor
|
* @param pYSub vertical subsampling factor
|
||||||
*/
|
*/
|
||||||
public void setSourceSubsampling(int xSubsampling, int ySubsampling) {
|
public void setSourceSubsampling(int pXSub, int pYSub) {
|
||||||
// Re-fetch everything, if subsampling changed
|
// Re-fetch everything, if subsampling changed
|
||||||
if (xSub != xSubsampling || ySub != ySubsampling) {
|
if (xSub != pXSub || ySub != pYSub) {
|
||||||
dispose();
|
dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (xSubsampling > 1) {
|
if (pXSub > 1) {
|
||||||
xSub = xSubsampling;
|
xSub = pXSub;
|
||||||
}
|
}
|
||||||
if (ySubsampling > 1) {
|
if (pYSub > 1) {
|
||||||
ySub = ySubsampling;
|
ySub = pYSub;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void doFetch(final boolean colorModelOnly) throws ImageConversionException {
|
private synchronized void doFetch(boolean pColorModelOnly) throws ImageConversionException {
|
||||||
if (!fetching && (!colorModelOnly && buffered == null || buffered == null && sourceColorModel == null)) {
|
if (!fetching && (!pColorModelOnly && buffered == null || buffered == null && sourceColorModel == null)) {
|
||||||
// NOTE: Subsampling is only applied if extracting full image
|
// NOTE: Subsampling is only applied if extracting full image
|
||||||
if (!colorModelOnly && (xSub > 1 || ySub > 1)) {
|
if (!pColorModelOnly && (xSub > 1 || ySub > 1)) {
|
||||||
// If only sampling a region, the region must be scaled too
|
// If only sampling a region, the region must be scaled too
|
||||||
if (width > 0 && height > 0) {
|
if (width > 0 && height > 0) {
|
||||||
width = (width + xSub - 1) / xSub;
|
width = (width + xSub - 1) / xSub;
|
||||||
@@ -207,41 +205,38 @@ public final class BufferedImageFactory {
|
|||||||
|
|
||||||
// Start fetching
|
// Start fetching
|
||||||
fetching = true;
|
fetching = true;
|
||||||
readColorModelOnly = colorModelOnly;
|
readColorModelOnly = pColorModelOnly;
|
||||||
|
|
||||||
producer.startProduction(consumer); // Note: If single-thread (synchronous), this call will block
|
producer.startProduction(consumer); // Note: If single-thread (synchronous), this call will block
|
||||||
|
|
||||||
// Wait until the producer wakes us up, by calling imageComplete
|
// Wait until the producer wakes us up, by calling imageComplete
|
||||||
while (fetching) {
|
while (fetching) {
|
||||||
try {
|
try {
|
||||||
wait(200L);
|
wait(200l);
|
||||||
}
|
}
|
||||||
catch (InterruptedException e) {
|
catch (InterruptedException e) {
|
||||||
throw new ImageConversionException("Image conversion aborted: " + e.getMessage(), e);
|
throw new ImageConversionException("Image conversion aborted: " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
if (consumerException != null) {
|
||||||
if (consumerException != null) {
|
throw new ImageConversionException("Image conversion failed: " + consumerException.getMessage(), consumerException);
|
||||||
throw new ImageConversionException("Image conversion failed: " + consumerException.getMessage(), consumerException);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (colorModelOnly) {
|
|
||||||
createColorModel();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
createBuffered();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
// Clean up, in case any objects are copied/cloned, so we can free resources
|
if (pColorModelOnly) {
|
||||||
freeResources();
|
createColorModel();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
createBuffered();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createColorModel() {
|
private void createColorModel() {
|
||||||
colorModel = sourceColorModel;
|
colorModel = sourceColorModel;
|
||||||
|
|
||||||
|
// Clean up, in case any objects are copied/cloned, so we can free resources
|
||||||
|
freeResources();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createBuffered() {
|
private void createBuffered() {
|
||||||
@@ -256,9 +251,8 @@ public final class BufferedImageFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buffered == null) {
|
// Clean up, in case any objects are copied/cloned, so we can free resources
|
||||||
throw new ImageConversionException("Could not create BufferedImage");
|
freeResources();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void freeResources() {
|
private void freeResources() {
|
||||||
@@ -284,27 +278,27 @@ public final class BufferedImageFactory {
|
|||||||
/**
|
/**
|
||||||
* Adds a progress listener to this factory.
|
* Adds a progress listener to this factory.
|
||||||
*
|
*
|
||||||
* @param listener the progress listener
|
* @param pListener the progress listener
|
||||||
*/
|
*/
|
||||||
public void addProgressListener(ProgressListener listener) {
|
public void addProgressListener(ProgressListener pListener) {
|
||||||
if (listener == null) {
|
if (pListener == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (listeners == null) {
|
if (listeners == null) {
|
||||||
listeners = new CopyOnWriteArrayList<>();
|
listeners = new CopyOnWriteArrayList<ProgressListener>();
|
||||||
}
|
}
|
||||||
|
|
||||||
listeners.add(listener);
|
listeners.add(pListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes a progress listener from this factory.
|
* Removes a progress listener from this factory.
|
||||||
*
|
*
|
||||||
* @param listener the progress listener
|
* @param pListener the progress listener
|
||||||
*/
|
*/
|
||||||
public void removeProgressListener(ProgressListener listener) {
|
public void removeProgressListener(ProgressListener pListener) {
|
||||||
if (listener == null) {
|
if (pListener == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,7 +306,7 @@ public final class BufferedImageFactory {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
listeners.remove(listener);
|
listeners.remove(pListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -328,22 +322,21 @@ public final class BufferedImageFactory {
|
|||||||
* Converts an array of {@code int} pixels to an array of {@code short}
|
* Converts an array of {@code int} pixels to an array of {@code short}
|
||||||
* pixels. The conversion is done, by masking out the
|
* pixels. The conversion is done, by masking out the
|
||||||
* <em>higher 16 bits</em> of the {@code int}.
|
* <em>higher 16 bits</em> of the {@code int}.
|
||||||
* <p>
|
*
|
||||||
* For any given {@code int}, the {@code short} value is computed as
|
* For any given {@code int}, the {@code short} value is computed as
|
||||||
* follows:
|
* follows:
|
||||||
* <blockquote>{@code
|
* <blockquote>{@code
|
||||||
* short value = (short) (intValue & 0x0000ffff);
|
* short value = (short) (intValue & 0x0000ffff);
|
||||||
* }</blockquote>
|
* }</blockquote>
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @param inputPixels the pixel data to convert
|
* @param pPixels the pixel data to convert
|
||||||
* @return an array of {@code short}s, same length as {@code inputPixels}
|
* @return an array of {@code short}s, same lenght as {@code pPixels}
|
||||||
*/
|
*/
|
||||||
private static short[] toShortPixels(int[] inputPixels) {
|
private static short[] toShortPixels(int[] pPixels) {
|
||||||
short[] pixels = new short[inputPixels.length];
|
short[] pixels = new short[pPixels.length];
|
||||||
|
|
||||||
for (int i = 0; i < pixels.length; i++) {
|
for (int i = 0; i < pixels.length; i++) {
|
||||||
pixels[i] = (short) (inputPixels[i] & 0xffff);
|
pixels[i] = (short) (pPixels[i] & 0xffff);
|
||||||
}
|
}
|
||||||
|
|
||||||
return pixels;
|
return pixels;
|
||||||
@@ -356,17 +349,17 @@ public final class BufferedImageFactory {
|
|||||||
* @see BufferedImageFactory#addProgressListener
|
* @see BufferedImageFactory#addProgressListener
|
||||||
* @see BufferedImageFactory#removeProgressListener
|
* @see BufferedImageFactory#removeProgressListener
|
||||||
*/
|
*/
|
||||||
public interface ProgressListener extends EventListener {
|
public static interface ProgressListener extends EventListener {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reports progress to this listener.
|
* Reports progress to this listener.
|
||||||
* Invoked by the {@code BufferedImageFactory} to report progress in
|
* Invoked by the {@code BufferedImageFactory} to report progress in
|
||||||
* the image decoding.
|
* the image decoding.
|
||||||
*
|
*
|
||||||
* @param factory the factory reporting the progress
|
* @param pFactory the factory reporting the progress
|
||||||
* @param percentage the percentage of progress
|
* @param pPercentage the percentage of progress
|
||||||
*/
|
*/
|
||||||
void progress(BufferedImageFactory factory, float percentage);
|
void progress(BufferedImageFactory pFactory, float pPercentage);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Consumer implements ImageConsumer {
|
private class Consumer implements ImageConsumer {
|
||||||
@@ -451,18 +444,18 @@ public final class BufferedImageFactory {
|
|||||||
processProgress(pY + pHeight);
|
processProgress(pY + pHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPixels(int x, int y, int width, int height, ColorModel colorModel, short[] pixels, int offset, int scanSize) {
|
public void setPixels(int pX, int pY, int pWidth, int pHeight, ColorModel pModel, short[] pPixels, int pOffset, int pScanSize) {
|
||||||
setPixelsImpl(x, y, width, height, colorModel, pixels, offset, scanSize);
|
setPixelsImpl(pX, pY, pWidth, pHeight, pModel, pPixels, pOffset, pScanSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setColorModelOnce(final ColorModel colorModel) {
|
private void setColorModelOnce(final ColorModel pModel) {
|
||||||
// NOTE: There seems to be a "bug" in AreaAveragingScaleFilter, as it
|
// NOTE: There seems to be a "bug" in AreaAveragingScaleFilter, as it
|
||||||
// first passes the original color model through in setColorModel, then
|
// first passes the original color model through in setColorModel, then
|
||||||
// later replaces it with the default RGB in the first setPixels call
|
// later replaces it with the default RGB in the first setPixels call
|
||||||
// (this is probably allowed according to the spec, but it's a waste of time and space).
|
// (this is probably allowed according to the spec, but it's a waste of time and space).
|
||||||
if (sourceColorModel != colorModel) {
|
if (sourceColorModel != pModel) {
|
||||||
if (sourcePixels == null) {
|
if (/*sourceColorModel == null ||*/ sourcePixels == null) {
|
||||||
sourceColorModel = colorModel;
|
sourceColorModel = pModel;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new IllegalStateException("Change of ColorModel after pixel delivery not supported");
|
throw new IllegalStateException("Change of ColorModel after pixel delivery not supported");
|
||||||
@@ -475,16 +468,17 @@ public final class BufferedImageFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void imageComplete(int pStatus) {
|
||||||
public void imageComplete(int status) {
|
|
||||||
fetching = false;
|
fetching = false;
|
||||||
|
|
||||||
if (producer != null) {
|
if (producer != null) {
|
||||||
producer.removeConsumer(this);
|
producer.removeConsumer(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status == ImageConsumer.IMAGEERROR) {
|
switch (pStatus) {
|
||||||
consumerException = new ImageConversionException("ImageConsumer.IMAGEERROR");
|
case ImageConsumer.IMAGEERROR:
|
||||||
|
consumerException = new ImageConversionException("ImageConsumer.IMAGEERROR");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized (BufferedImageFactory.this) {
|
synchronized (BufferedImageFactory.this) {
|
||||||
@@ -492,18 +486,16 @@ public final class BufferedImageFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void setColorModel(ColorModel pModel) {
|
||||||
public void setColorModel(ColorModel colorModel) {
|
setColorModelOnce(pModel);
|
||||||
setColorModelOnce(colorModel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void setDimensions(int pWidth, int pHeight) {
|
||||||
public void setDimensions(int w, int h) {
|
|
||||||
if (width < 0) {
|
if (width < 0) {
|
||||||
width = w - x;
|
width = pWidth - x;
|
||||||
}
|
}
|
||||||
if (height < 0) {
|
if (height < 0) {
|
||||||
height = h - y;
|
height = pHeight - y;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hmm.. Special case, but is it a good idea?
|
// Hmm.. Special case, but is it a good idea?
|
||||||
@@ -512,31 +504,27 @@ public final class BufferedImageFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void setHints(int pHintflags) {
|
||||||
public void setHints(int hintFlags) {
|
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void setPixels(int pX, int pY, int pWidth, int pHeight, ColorModel pModel, byte[] pPixels, int pOffset, int pScanSize) {
|
||||||
public void setPixels(int x, int y, int width, int height, ColorModel colorModel, byte[] pixels, int offset, int scanSize) {
|
setPixelsImpl(pX, pY, pWidth, pHeight, pModel, pPixels, pOffset, pScanSize);
|
||||||
setPixelsImpl(x, y, width, height, colorModel, pixels, offset, scanSize);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void setPixels(int pX, int pY, int pWeigth, int pHeight, ColorModel pModel, int[] pPixels, int pOffset, int pScanSize) {
|
||||||
public void setPixels(int x, int y, int width, int height, ColorModel colorModel, int[] pixels, int offset, int scanSize) {
|
if (pModel.getTransferType() == DataBuffer.TYPE_USHORT) {
|
||||||
if (colorModel.getTransferType() == DataBuffer.TYPE_USHORT) {
|
|
||||||
// NOTE: Workaround for limitation in ImageConsumer API
|
// NOTE: Workaround for limitation in ImageConsumer API
|
||||||
// Convert int[] to short[], to be compatible with the ColorModel
|
// Convert int[] to short[], to be compatible with the ColorModel
|
||||||
setPixelsImpl(x, y, width, height, colorModel, toShortPixels(pixels), offset, scanSize);
|
setPixelsImpl(pX, pY, pWeigth, pHeight, pModel, toShortPixels(pPixels), pOffset, pScanSize);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
setPixelsImpl(x, y, width, height, colorModel, pixels, offset, scanSize);
|
setPixelsImpl(pX, pY, pWeigth, pHeight, pModel, pPixels, pOffset, pScanSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void setProperties(Hashtable pProperties) {
|
||||||
public void setProperties(Hashtable properties) {
|
sourceProperties = pProperties;
|
||||||
sourceProperties = properties;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,14 +39,15 @@ import java.awt.image.BufferedImage;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@code Icon} implementation backed by a {@code BufferedImage}.
|
* An {@code Icon} implementation backed by a {@code BufferedImage}.
|
||||||
|
* <p/>
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/BufferedImageIcon.java#2 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/BufferedImageIcon.java#2 $
|
||||||
*/
|
*/
|
||||||
public class BufferedImageIcon implements Icon {
|
public class BufferedImageIcon implements Icon {
|
||||||
private final BufferedImage image;
|
private final BufferedImage image;
|
||||||
private final int width;
|
private int width;
|
||||||
private final int height;
|
private int height;
|
||||||
private final boolean fast;
|
private final boolean fast;
|
||||||
|
|
||||||
public BufferedImageIcon(BufferedImage pImage) {
|
public BufferedImageIcon(BufferedImage pImage) {
|
||||||
@@ -81,10 +82,11 @@ public class BufferedImageIcon implements Icon {
|
|||||||
else {
|
else {
|
||||||
//System.out.println("Scaling using interpolation");
|
//System.out.println("Scaling using interpolation");
|
||||||
Graphics2D g2 = (Graphics2D) g;
|
Graphics2D g2 = (Graphics2D) g;
|
||||||
AffineTransform transform = AffineTransform.getTranslateInstance(x, y);
|
AffineTransform xform = AffineTransform.getTranslateInstance(x, y);
|
||||||
transform.scale(width / (double) image.getWidth(), height / (double) image.getHeight());
|
xform.scale(width / (double) image.getWidth(), height / (double) image.getHeight());
|
||||||
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
|
||||||
g2.drawImage(image, transform, null);
|
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
||||||
|
g2.drawImage(image, xform, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,24 +39,22 @@ import java.util.Random;
|
|||||||
/**
|
/**
|
||||||
* This {@code BufferedImageOp/RasterOp} implements basic
|
* This {@code BufferedImageOp/RasterOp} implements basic
|
||||||
* Floyd-Steinberg error-diffusion algorithm for dithering.
|
* Floyd-Steinberg error-diffusion algorithm for dithering.
|
||||||
* <p>
|
* <P/>
|
||||||
* The weights used are 7/16, 3/16, 5/16 and 1/16, distributed like this:
|
* The weights used are 7/16, 3/16, 5/16 and 1/16, distributed like this:
|
||||||
* <!-- - -
|
* <!-- - -
|
||||||
* | |x|7|
|
* | |x|7|
|
||||||
* - - - -
|
* - - - -
|
||||||
* |3|5|1|
|
* |3|5|1|
|
||||||
* - - -->
|
* - - -->
|
||||||
* </p>
|
* <P/>
|
||||||
* <table border="1">
|
* <TABLE border="1" cellpadding="4" cellspacing="0">
|
||||||
* <caption>Floyd-Steinberg error-diffusion weights</caption>
|
* <TR><TD bgcolor="#000000"> </TD><TD class="TableHeadingColor"
|
||||||
* <tr><td style="background:#000000"> </td><td class="TableHeadingColor"
|
* align="center">X</TD><TD>7/16</TD></TR>
|
||||||
* style="text-align:center">x</td><td>7/16</td></tr>
|
* <TR><TD>3/16</TD><TD>5/16</TD><TD>1/16</TD></TR>
|
||||||
* <tr><td>3/16</td><td>5/16</td><td>1/16</td></tr>
|
* </TABLE>
|
||||||
* </table>
|
* <P/>
|
||||||
* <p>
|
|
||||||
* See <A href="http://www.awprofessional.com/bookstore/product.asp?isbn=0201848406&rl=1">Computer Graphics (Foley et al.)</a>
|
* See <A href="http://www.awprofessional.com/bookstore/product.asp?isbn=0201848406&rl=1">Computer Graphics (Foley et al.)</a>
|
||||||
* for more information.
|
* for more information.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @author last modified by $Author: haku $
|
* @author last modified by $Author: haku $
|
||||||
|
|||||||
@@ -43,11 +43,10 @@ public final class GraphicsUtil {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Enables anti-aliasing in the {@code Graphics} object.
|
* Enables anti-aliasing in the {@code Graphics} object.
|
||||||
* <p>
|
* <p/>
|
||||||
* Anti-aliasing is enabled by casting to {@code Graphics2D} and setting
|
* Anti-aliasing is enabled by casting to {@code Graphics2D} and setting
|
||||||
* the rendering hint {@code RenderingHints.KEY_ANTIALIASING} to
|
* the rendering hint {@code RenderingHints.KEY_ANTIALIASING} to
|
||||||
* {@code RenderingHints.VALUE_ANTIALIAS_ON}.
|
* {@code RenderingHints.VALUE_ANTIALIAS_ON}.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @param pGraphics the graphics object
|
* @param pGraphics the graphics object
|
||||||
* @throws ClassCastException if {@code pGraphics} is not an instance of
|
* @throws ClassCastException if {@code pGraphics} is not an instance of
|
||||||
@@ -63,11 +62,10 @@ public final class GraphicsUtil {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the alpha in the {@code Graphics} object.
|
* Sets the alpha in the {@code Graphics} object.
|
||||||
* <p>
|
* <p/>
|
||||||
* Alpha is set by casting to {@code Graphics2D} and setting the composite
|
* Alpha is set by casting to {@code Graphics2D} and setting the composite
|
||||||
* to the rule {@code AlphaComposite.SRC_OVER} multiplied by the given
|
* to the rule {@code AlphaComposite.SRC_OVER} multiplied by the given
|
||||||
* alpha.
|
* alpha.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @param pGraphics the graphics object
|
* @param pGraphics the graphics object
|
||||||
* @param pAlpha the alpha level, {@code alpha} must be a floating point
|
* @param pAlpha the alpha level, {@code alpha} must be a floating point
|
||||||
|
|||||||
@@ -34,9 +34,8 @@ import java.awt.image.RGBImageFilter;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This class can convert a color image to grayscale.
|
* This class can convert a color image to grayscale.
|
||||||
* <p>
|
* <P/>
|
||||||
* Uses ITU standard conversion: (222 * Red + 707 * Green + 71 * Blue) / 1000.
|
* Uses ITU standard conversion: (222 * Red + 707 * Green + 71 * Blue) / 1000.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @author last modified by $Author: haku $
|
* @author last modified by $Author: haku $
|
||||||
@@ -65,7 +64,7 @@ public class GrayFilter extends RGBImageFilter {
|
|||||||
* pLow and pHigh.
|
* pLow and pHigh.
|
||||||
*
|
*
|
||||||
* @param pLow float in the range 0..1
|
* @param pLow float in the range 0..1
|
||||||
* @param pHigh float in the range 0..1 and >= pLow
|
* @param pHigh float in the range 0..1 and >= pLow
|
||||||
*/
|
*/
|
||||||
public GrayFilter(float pLow, float pHigh) {
|
public GrayFilter(float pLow, float pHigh) {
|
||||||
if (pLow > pHigh) {
|
if (pLow > pHigh) {
|
||||||
@@ -95,7 +94,7 @@ public class GrayFilter extends RGBImageFilter {
|
|||||||
* range between pLow and pHigh.
|
* range between pLow and pHigh.
|
||||||
*
|
*
|
||||||
* @param pLow integer in the range 0..255
|
* @param pLow integer in the range 0..255
|
||||||
* @param pHigh integer in the range 0..255 and >= pLow
|
* @param pHigh inteeger in the range 0..255 and >= pLow
|
||||||
*/
|
*/
|
||||||
public GrayFilter(int pLow, int pHigh) {
|
public GrayFilter(int pLow, int pHigh) {
|
||||||
this(pLow / 255f, pHigh / 255f);
|
this(pLow / 255f, pHigh / 255f);
|
||||||
|
|||||||
@@ -162,12 +162,11 @@ public final class ImageUtil {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The sharpen kernel. Uses the following 3 by 3 matrix:
|
* The sharpen kernel. Uses the following 3 by 3 matrix:
|
||||||
* <table border="1">
|
* <TABLE border="1" cellspacing="0">
|
||||||
* <caption>Sharpen Kernel Matrix</caption>
|
* <TR><TD>0.0</TD><TD>-0.3</TD><TD>0.0</TD></TR>
|
||||||
* <tr><td>0.0</td><td>-0.3</td><td>0.0</td></tr>
|
* <TR><TD>-0.3</TD><TD>2.2</TD><TD>-0.3</TD></TR>
|
||||||
* <tr><td>-0.3</td><td>2.2</td><td>-0.3</td></tr>
|
* <TR><TD>0.0</TD><TD>-0.3</TD><TD>0.0</TD></TR>
|
||||||
* <tr><td>0.0</td><td>-0.3</td><td>0.0</td></tr>
|
* </TABLE>
|
||||||
* </table>
|
|
||||||
*/
|
*/
|
||||||
private static final Kernel SHARPEN_KERNEL = new Kernel(3, 3, SHARPEN_MATRIX);
|
private static final Kernel SHARPEN_KERNEL = new Kernel(3, 3, SHARPEN_MATRIX);
|
||||||
|
|
||||||
@@ -209,10 +208,9 @@ public final class ImageUtil {
|
|||||||
* Converts the {@code RenderedImage} to a {@code BufferedImage}.
|
* Converts the {@code RenderedImage} to a {@code BufferedImage}.
|
||||||
* The new image will have the <em>same</em> {@code ColorModel},
|
* The new image will have the <em>same</em> {@code ColorModel},
|
||||||
* {@code Raster} and properties as the original image, if possible.
|
* {@code Raster} and properties as the original image, if possible.
|
||||||
* <p>
|
* <p/>
|
||||||
* If the image is already a {@code BufferedImage}, it is simply returned
|
* If the image is already a {@code BufferedImage}, it is simply returned
|
||||||
* and no conversion takes place.
|
* and no conversion takes place.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @param pOriginal the image to convert.
|
* @param pOriginal the image to convert.
|
||||||
*
|
*
|
||||||
@@ -264,10 +262,9 @@ public final class ImageUtil {
|
|||||||
/**
|
/**
|
||||||
* Converts the {@code RenderedImage} to a {@code BufferedImage} of the
|
* Converts the {@code RenderedImage} to a {@code BufferedImage} of the
|
||||||
* given type.
|
* given type.
|
||||||
* <p>
|
* <p/>
|
||||||
* If the image is already a {@code BufferedImage} of the given type, it
|
* If the image is already a {@code BufferedImage} of the given type, it
|
||||||
* is simply returned and no conversion takes place.
|
* is simply returned and no conversion takes place.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @param pOriginal the image to convert.
|
* @param pOriginal the image to convert.
|
||||||
* @param pType the type of buffered image
|
* @param pType the type of buffered image
|
||||||
@@ -311,14 +308,12 @@ public final class ImageUtil {
|
|||||||
* Converts the {@code BufferedImage} to a {@code BufferedImage} of the
|
* Converts the {@code BufferedImage} to a {@code BufferedImage} of the
|
||||||
* given type. The new image will have the same {@code ColorModel},
|
* given type. The new image will have the same {@code ColorModel},
|
||||||
* {@code Raster} and properties as the original image, if possible.
|
* {@code Raster} and properties as the original image, if possible.
|
||||||
* <p>
|
* <p/>
|
||||||
* If the image is already a {@code BufferedImage} of the given type, it
|
* If the image is already a {@code BufferedImage} of the given type, it
|
||||||
* is simply returned and no conversion takes place.
|
* is simply returned and no conversion takes place.
|
||||||
* </p>
|
* <p/>
|
||||||
* <p>
|
|
||||||
* This method simply invokes
|
* This method simply invokes
|
||||||
* {@link #toBuffered(RenderedImage,int) toBuffered((RenderedImage) pOriginal, pType)}.
|
* {@link #toBuffered(RenderedImage,int) toBuffered((RenderedImage) pOriginal, pType)}.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @param pOriginal the image to convert.
|
* @param pOriginal the image to convert.
|
||||||
* @param pType the type of buffered image
|
* @param pType the type of buffered image
|
||||||
@@ -338,10 +333,9 @@ public final class ImageUtil {
|
|||||||
* Converts the {@code Image} to a {@code BufferedImage}.
|
* Converts the {@code Image} to a {@code BufferedImage}.
|
||||||
* The new image will have the same {@code ColorModel}, {@code Raster} and
|
* The new image will have the same {@code ColorModel}, {@code Raster} and
|
||||||
* properties as the original image, if possible.
|
* properties as the original image, if possible.
|
||||||
* <p>
|
* <p/>
|
||||||
* If the image is already a {@code BufferedImage}, it is simply returned
|
* If the image is already a {@code BufferedImage}, it is simply returned
|
||||||
* and no conversion takes place.
|
* and no conversion takes place.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @param pOriginal the image to convert.
|
* @param pOriginal the image to convert.
|
||||||
*
|
*
|
||||||
@@ -395,22 +389,19 @@ public final class ImageUtil {
|
|||||||
/**
|
/**
|
||||||
* Creates a {@code WritableRaster} for the given {@code ColorModel} and
|
* Creates a {@code WritableRaster} for the given {@code ColorModel} and
|
||||||
* pixel data.
|
* pixel data.
|
||||||
* <p>
|
* <p/>
|
||||||
* This method is optimized for the most common cases of {@code ColorModel}
|
* This method is optimized for the most common cases of {@code ColorModel}
|
||||||
* and pixel data combinations. The raster's backing {@code DataBuffer} is
|
* and pixel data combinations. The raster's backing {@code DataBuffer} is
|
||||||
* created directly from the pixel data, as this is faster and more
|
* created directly from the pixel data, as this is faster and more
|
||||||
* resource-friendly than using
|
* resource-friendly than using
|
||||||
* {@code ColorModel.createCompatibleWritableRaster(w, h)}.
|
* {@code ColorModel.createCompatibleWritableRaster(w, h)}.
|
||||||
* </p>
|
* <p/>
|
||||||
* <p>
|
|
||||||
* For uncommon combinations, the method will fallback to using
|
* For uncommon combinations, the method will fallback to using
|
||||||
* {@code ColorModel.createCompatibleWritableRaster(w, h)} and
|
* {@code ColorModel.createCompatibleWritableRaster(w, h)} and
|
||||||
* {@code WritableRaster.setDataElements(w, h, pixels)}
|
* {@code WritableRaster.setDataElements(w, h, pixels)}
|
||||||
* </p>
|
* <p/>
|
||||||
* <p>
|
|
||||||
* Note that the {@code ColorModel} and pixel data are <em>not</em> cloned
|
* Note that the {@code ColorModel} and pixel data are <em>not</em> cloned
|
||||||
* (in most cases).
|
* (in most cases).
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @param pWidth the requested raster width
|
* @param pWidth the requested raster width
|
||||||
* @param pHeight the requested raster height
|
* @param pHeight the requested raster height
|
||||||
@@ -552,10 +543,9 @@ public final class ImageUtil {
|
|||||||
* Converts the {@code Image} to a {@code BufferedImage} of the given type.
|
* Converts the {@code Image} to a {@code BufferedImage} of the given type.
|
||||||
* The new image will have the same {@code ColorModel}, {@code Raster} and
|
* The new image will have the same {@code ColorModel}, {@code Raster} and
|
||||||
* properties as the original image, if possible.
|
* properties as the original image, if possible.
|
||||||
* <p>
|
* <p/>
|
||||||
* If the image is already a {@code BufferedImage} of the given type, it
|
* If the image is already a {@code BufferedImage} of the given type, it
|
||||||
* is simply returned and no conversion takes place.
|
* is simply returned and no conversion takes place.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @param pOriginal the image to convert.
|
* @param pOriginal the image to convert.
|
||||||
* @param pType the type of buffered image
|
* @param pType the type of buffered image
|
||||||
@@ -666,10 +656,9 @@ public final class ImageUtil {
|
|||||||
* Rotates the image 90 degrees, clockwise (aka "rotate right"),
|
* Rotates the image 90 degrees, clockwise (aka "rotate right"),
|
||||||
* counter-clockwise (aka "rotate left") or 180 degrees, depending on the
|
* counter-clockwise (aka "rotate left") or 180 degrees, depending on the
|
||||||
* {@code pDirection} argument.
|
* {@code pDirection} argument.
|
||||||
* <p>
|
* <p/>
|
||||||
* The new image will be completely covered with pixels from the source
|
* The new image will be completely covered with pixels from the source
|
||||||
* image.
|
* image.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @param pImage the source image.
|
* @param pImage the source image.
|
||||||
* @param pDirection the direction, must be either {@link #ROTATE_90_CW},
|
* @param pDirection the direction, must be either {@link #ROTATE_90_CW},
|
||||||
@@ -844,7 +833,7 @@ public final class ImageUtil {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < mapSize1; i++) {
|
for (int i = 0; i > mapSize1; i++) {
|
||||||
if (icm1.getRGB(i) != icm2.getRGB(i)) {
|
if (icm1.getRGB(i) != icm2.getRGB(i)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1078,16 +1067,14 @@ public final class ImageUtil {
|
|||||||
/**
|
/**
|
||||||
* Sharpens an image using a convolution matrix.
|
* Sharpens an image using a convolution matrix.
|
||||||
* The sharpen kernel used, is defined by the following 3 by 3 matrix:
|
* The sharpen kernel used, is defined by the following 3 by 3 matrix:
|
||||||
* <table border="1">
|
* <TABLE border="1" cellspacing="0">
|
||||||
* <caption>Sharpen Kernel Matrix</caption>
|
* <TR><TD>0.0</TD><TD>-0.3</TD><TD>0.0</TD></TR>
|
||||||
* <tr><td>0.0</td><td>-0.3</td><td>0.0</td></tr>
|
* <TR><TD>-0.3</TD><TD>2.2</TD><TD>-0.3</TD></TR>
|
||||||
* <tr><td>-0.3</td><td>2.2</td><td>-0.3</td></tr>
|
* <TR><TD>0.0</TD><TD>-0.3</TD><TD>0.0</TD></TR>
|
||||||
* <tr><td>0.0</td><td>-0.3</td><td>0.0</td></tr>
|
* </TABLE>
|
||||||
* </table>
|
* <P/>
|
||||||
* <p>
|
|
||||||
* This is the same result returned as
|
* This is the same result returned as
|
||||||
* {@code sharpen(pOriginal, 0.3f)}.
|
* {@code sharpen(pOriginal, 0.3f)}.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @param pOriginal the BufferedImage to sharpen
|
* @param pOriginal the BufferedImage to sharpen
|
||||||
*
|
*
|
||||||
@@ -1100,14 +1087,13 @@ public final class ImageUtil {
|
|||||||
/**
|
/**
|
||||||
* Sharpens an image using a convolution matrix.
|
* Sharpens an image using a convolution matrix.
|
||||||
* The sharpen kernel used, is defined by the following 3 by 3 matrix:
|
* The sharpen kernel used, is defined by the following 3 by 3 matrix:
|
||||||
* <table border="1">
|
* <TABLE border="1" cellspacing="0">
|
||||||
* <caption>Sharpen Kernel Matrix</caption>
|
* <TR><TD>0.0</TD><TD>-{@code pAmount}</TD><TD>0.0</TD></TR>
|
||||||
* <tr><td>0.0</td><td>-{@code pAmount}</td><td>0.0</td></tr>
|
* <TR><TD>-{@code pAmount}</TD>
|
||||||
* <tr><td>-{@code pAmount}</td>
|
* <TD>4.0 * {@code pAmount} + 1.0</TD>
|
||||||
* <td>4.0 * {@code pAmount} + 1.0</td>
|
* <TD>-{@code pAmount}</TD></TR>
|
||||||
* <td>-{@code pAmount}</td></tr>
|
* <TR><TD>0.0</TD><TD>-{@code pAmount}</TD><TD>0.0</TD></TR>
|
||||||
* <tr><td>0.0</td><td>-{@code pAmount}</td><td>0.0</td></tr>
|
* </TABLE>
|
||||||
* </table>
|
|
||||||
*
|
*
|
||||||
* @param pOriginal the BufferedImage to sharpen
|
* @param pOriginal the BufferedImage to sharpen
|
||||||
* @param pAmount the amount of sharpening
|
* @param pAmount the amount of sharpening
|
||||||
|
|||||||
@@ -101,35 +101,32 @@ import java.util.List;
|
|||||||
* This class implements an adaptive palette generator to reduce images
|
* This class implements an adaptive palette generator to reduce images
|
||||||
* to a variable number of colors.
|
* to a variable number of colors.
|
||||||
* It can also render images into fixed color pallettes.
|
* It can also render images into fixed color pallettes.
|
||||||
* <p>
|
* <p/>
|
||||||
* Support for the default JVM (ordered/pattern) dither, Floyd-Steinberg like
|
* Support for the default JVM (ordered/pattern) dither, Floyd-Steinberg like
|
||||||
* error-diffusion and no dither, controlled by the hints
|
* error-diffusion and no dither, controlled by the hints
|
||||||
* {@link #DITHER_DIFFUSION},
|
* {@link #DITHER_DIFFUSION},
|
||||||
* {@link #DITHER_NONE} and
|
* {@link #DITHER_NONE} and
|
||||||
* {@link #DITHER_DEFAULT}.
|
* {@link #DITHER_DEFAULT}.
|
||||||
* </p>
|
* <p/>
|
||||||
* <p>
|
|
||||||
* Color selection speed/accuracy can be controlled using the hints
|
* Color selection speed/accuracy can be controlled using the hints
|
||||||
* {@link #COLOR_SELECTION_FAST},
|
* {@link #COLOR_SELECTION_FAST},
|
||||||
* {@link #COLOR_SELECTION_QUALITY} and
|
* {@link #COLOR_SELECTION_QUALITY} and
|
||||||
* {@link #COLOR_SELECTION_DEFAULT}.
|
* {@link #COLOR_SELECTION_DEFAULT}.
|
||||||
* </p>
|
* <p/>
|
||||||
* <p>
|
|
||||||
* Transparency support can be controlled using the hints
|
* Transparency support can be controlled using the hints
|
||||||
* {@link #TRANSPARENCY_OPAQUE},
|
* {@link #TRANSPARENCY_OPAQUE},
|
||||||
* {@link #TRANSPARENCY_BITMASK} and
|
* {@link #TRANSPARENCY_BITMASK} and
|
||||||
* {@link #TRANSPARENCY_TRANSLUCENT}.
|
* {@link #TRANSPARENCY_TRANSLUCENT}.
|
||||||
* </p>
|
* <p/>
|
||||||
* <hr>
|
* <HR/>
|
||||||
* <p>
|
* <p/>
|
||||||
* <pre>
|
* <PRE>
|
||||||
* This product includes software developed by the Apache Software Foundation.
|
* This product includes software developed by the Apache Software Foundation.
|
||||||
*
|
* <p/>
|
||||||
* This software consists of voluntary contributions made by many individuals
|
* This software consists of voluntary contributions made by many individuals
|
||||||
* on behalf of the Apache Software Foundation. For more information on the
|
* on behalf of the Apache Software Foundation. For more information on the
|
||||||
* Apache Software Foundation, please see <A href="http://www.apache.org/">http://www.apache.org/</A>
|
* Apache Software Foundation, please see <A href="http://www.apache.org/">http://www.apache.org/</A>
|
||||||
* </pre>
|
* </PRE>
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @author <A href="mailto:deweese@apache.org">Thomas DeWeese</A>
|
* @author <A href="mailto:deweese@apache.org">Thomas DeWeese</A>
|
||||||
* @author <A href="mailto:jun@oop-reserch.com">Jun Inamori</A>
|
* @author <A href="mailto:jun@oop-reserch.com">Jun Inamori</A>
|
||||||
@@ -587,7 +584,6 @@ class IndexImage {
|
|||||||
* @deprecated Use {@link #getIndexColorModel(Image,int,int)} instead!
|
* @deprecated Use {@link #getIndexColorModel(Image,int,int)} instead!
|
||||||
* This version will be removed in a later version of the API.
|
* This version will be removed in a later version of the API.
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
|
||||||
public static IndexColorModel getIndexColorModel(Image pImage, int pNumberOfColors, boolean pFast) {
|
public static IndexColorModel getIndexColorModel(Image pImage, int pNumberOfColors, boolean pFast) {
|
||||||
return getIndexColorModel(pImage, pNumberOfColors, pFast ? COLOR_SELECTION_FAST : COLOR_SELECTION_QUALITY);
|
return getIndexColorModel(pImage, pNumberOfColors, pFast ? COLOR_SELECTION_FAST : COLOR_SELECTION_QUALITY);
|
||||||
}
|
}
|
||||||
@@ -829,9 +825,8 @@ class IndexImage {
|
|||||||
* {@code TYPE_INT_ARGB}) to an indexed image. Generating an adaptive
|
* {@code TYPE_INT_ARGB}) to an indexed image. Generating an adaptive
|
||||||
* palette (8 bit) from the color data in the image, and uses default
|
* palette (8 bit) from the color data in the image, and uses default
|
||||||
* dither.
|
* dither.
|
||||||
* <p>
|
* <p/>
|
||||||
* The image returned is a new image, the input image is not modified.
|
* The image returned is a new image, the input image is not modified.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @param pImage the BufferedImage to index and get color information from.
|
* @param pImage the BufferedImage to index and get color information from.
|
||||||
* @return the indexed BufferedImage. The image will be of type
|
* @return the indexed BufferedImage. The image will be of type
|
||||||
@@ -875,9 +870,8 @@ class IndexImage {
|
|||||||
* adaptive palette (8 bit) from the given palette image.
|
* adaptive palette (8 bit) from the given palette image.
|
||||||
* Dithering, transparency and color selection is controlled with the
|
* Dithering, transparency and color selection is controlled with the
|
||||||
* {@code pHints}parameter.
|
* {@code pHints}parameter.
|
||||||
* <p>
|
* <p/>
|
||||||
* The image returned is a new image, the input image is not modified.
|
* The image returned is a new image, the input image is not modified.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @param pImage the BufferedImage to index
|
* @param pImage the BufferedImage to index
|
||||||
* @param pPalette the Image to read color information from
|
* @param pPalette the Image to read color information from
|
||||||
@@ -910,10 +904,9 @@ class IndexImage {
|
|||||||
* {@code TYPE_INT_ARGB}) to an indexed image. Generating an adaptive
|
* {@code TYPE_INT_ARGB}) to an indexed image. Generating an adaptive
|
||||||
* palette with the given number of colors.
|
* palette with the given number of colors.
|
||||||
* Dithering, transparency and color selection is controlled with the
|
* Dithering, transparency and color selection is controlled with the
|
||||||
* {@code pHints} parameter.
|
* {@code pHints}parameter.
|
||||||
* <p>
|
* <p/>
|
||||||
* The image returned is a new image, the input image is not modified.
|
* The image returned is a new image, the input image is not modified.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @param pImage the BufferedImage to index
|
* @param pImage the BufferedImage to index
|
||||||
* @param pNumberOfColors the number of colors for the image
|
* @param pNumberOfColors the number of colors for the image
|
||||||
@@ -959,9 +952,8 @@ class IndexImage {
|
|||||||
* {@code IndexColorModel}'s palette.
|
* {@code IndexColorModel}'s palette.
|
||||||
* Dithering, transparency and color selection is controlled with the
|
* Dithering, transparency and color selection is controlled with the
|
||||||
* {@code pHints} parameter.
|
* {@code pHints} parameter.
|
||||||
* <p>
|
* <p/>
|
||||||
* The image returned is a new image, the input image is not modified.
|
* The image returned is a new image, the input image is not modified.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @param pImage the BufferedImage to index
|
* @param pImage the BufferedImage to index
|
||||||
* @param pColors an {@code IndexColorModel} containing the color information
|
* @param pColors an {@code IndexColorModel} containing the color information
|
||||||
@@ -1077,9 +1069,8 @@ class IndexImage {
|
|||||||
* palette with the given number of colors.
|
* palette with the given number of colors.
|
||||||
* Dithering, transparency and color selection is controlled with the
|
* Dithering, transparency and color selection is controlled with the
|
||||||
* {@code pHints}parameter.
|
* {@code pHints}parameter.
|
||||||
* <p>
|
* <p/>
|
||||||
* The image returned is a new image, the input image is not modified.
|
* The image returned is a new image, the input image is not modified.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @param pImage the BufferedImage to index
|
* @param pImage the BufferedImage to index
|
||||||
* @param pNumberOfColors the number of colors for the image
|
* @param pNumberOfColors the number of colors for the image
|
||||||
@@ -1108,9 +1099,8 @@ class IndexImage {
|
|||||||
* {@code IndexColorModel}'s palette.
|
* {@code IndexColorModel}'s palette.
|
||||||
* Dithering, transparency and color selection is controlled with the
|
* Dithering, transparency and color selection is controlled with the
|
||||||
* {@code pHints}parameter.
|
* {@code pHints}parameter.
|
||||||
* <p>
|
* <p/>
|
||||||
* The image returned is a new image, the input image is not modified.
|
* The image returned is a new image, the input image is not modified.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @param pImage the BufferedImage to index
|
* @param pImage the BufferedImage to index
|
||||||
* @param pColors an {@code IndexColorModel} containing the color information
|
* @param pColors an {@code IndexColorModel} containing the color information
|
||||||
@@ -1140,9 +1130,8 @@ class IndexImage {
|
|||||||
* adaptive palette (8 bit) from the given palette image.
|
* adaptive palette (8 bit) from the given palette image.
|
||||||
* Dithering, transparency and color selection is controlled with the
|
* Dithering, transparency and color selection is controlled with the
|
||||||
* {@code pHints}parameter.
|
* {@code pHints}parameter.
|
||||||
* <p>
|
* <p/>
|
||||||
* The image returned is a new image, the input image is not modified.
|
* The image returned is a new image, the input image is not modified.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @param pImage the BufferedImage to index
|
* @param pImage the BufferedImage to index
|
||||||
* @param pPalette the Image to read color information from
|
* @param pPalette the Image to read color information from
|
||||||
|
|||||||
@@ -33,10 +33,9 @@ package com.twelvemonkeys.image;
|
|||||||
/**
|
/**
|
||||||
* Inverse Colormap to provide efficient lookup of any given input color
|
* Inverse Colormap to provide efficient lookup of any given input color
|
||||||
* to the closest match to the given color map.
|
* to the closest match to the given color map.
|
||||||
* <p>
|
* <p/>
|
||||||
* Based on "Efficient Inverse Color Map Computation" by Spencer W. Thomas
|
* Based on "Efficient Inverse Color Map Computation" by Spencer W. Thomas
|
||||||
* in "Graphics Gems Volume II".
|
* in "Graphics Gems Volume II"
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @author Robin Luiten (Java port)
|
* @author Robin Luiten (Java port)
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* 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.image;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Magick
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
|
* @author last modified by $Author: haku $
|
||||||
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/Magick.java#1 $
|
||||||
|
*/
|
||||||
|
final class Magick {
|
||||||
|
static final boolean DEBUG = useDebug();
|
||||||
|
|
||||||
|
private static boolean useDebug() {
|
||||||
|
try {
|
||||||
|
return "TRUE".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.image.magick.debug"));
|
||||||
|
}
|
||||||
|
catch (Throwable t) {
|
||||||
|
// Most probably in case of a SecurityManager
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Magick() {}
|
||||||
|
}
|
||||||
+187
@@ -0,0 +1,187 @@
|
|||||||
|
/*
|
||||||
|
* 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.image;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.lang.SystemUtil;
|
||||||
|
import magick.MagickImage;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.awt.image.BufferedImageOp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class accelerates certain graphics operations, using
|
||||||
|
* JMagick and ImageMagick, if available.
|
||||||
|
* If those libraries are not installed, this class silently does nothing.
|
||||||
|
* <p/>
|
||||||
|
* Set the system property {@code "com.twelvemonkeys.image.accel"} to
|
||||||
|
* {@code false}, to disable, even if JMagick is installed.
|
||||||
|
* Set the system property {@code "com.twelvemonkeys.image.magick.debug"} to
|
||||||
|
* <p/>
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/MagickAccelerator.java#3 $
|
||||||
|
*/
|
||||||
|
final class MagickAccelerator {
|
||||||
|
|
||||||
|
private static final boolean DEBUG = Magick.DEBUG;
|
||||||
|
private static final boolean USE_MAGICK = useMagick();
|
||||||
|
|
||||||
|
private static final int RESAMPLE_OP = 0;
|
||||||
|
|
||||||
|
private static Class[] nativeOp = new Class[1];
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
nativeOp[RESAMPLE_OP] = Class.forName("com.twelvemonkeys.image.ResampleOp");
|
||||||
|
}
|
||||||
|
catch (ClassNotFoundException e) {
|
||||||
|
System.err.println("Could not find class: " + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean useMagick() {
|
||||||
|
try {
|
||||||
|
boolean available = SystemUtil.isClassAvailable("magick.MagickImage");
|
||||||
|
|
||||||
|
if (DEBUG && !available) {
|
||||||
|
System.err.print("ImageMagick bindings not available.");
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean useMagick =
|
||||||
|
available && !"FALSE".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.image.accel"));
|
||||||
|
|
||||||
|
if (DEBUG) {
|
||||||
|
System.err.println(
|
||||||
|
useMagick
|
||||||
|
? "Will use ImageMagick bindings to accelerate image resampling operations."
|
||||||
|
: "Will not use ImageMagick to accelerate image resampling operations."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return useMagick;
|
||||||
|
}
|
||||||
|
catch (Throwable t) {
|
||||||
|
// Most probably in case of a SecurityManager
|
||||||
|
System.err.println("Could not enable ImageMagick bindings: " + t);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getNativeOpIndex(Class pOpClass) {
|
||||||
|
for (int i = 0; i < nativeOp.length; i++) {
|
||||||
|
if (pOpClass == nativeOp[i]) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BufferedImage filter(BufferedImageOp pOperation, BufferedImage pInput, BufferedImage pOutput) {
|
||||||
|
if (!USE_MAGICK) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferedImage result = null;
|
||||||
|
switch (getNativeOpIndex(pOperation.getClass())) {
|
||||||
|
case RESAMPLE_OP:
|
||||||
|
ResampleOp resample = (ResampleOp) pOperation;
|
||||||
|
result = resampleMagick(pInput, resample.width, resample.height, resample.filterType);
|
||||||
|
|
||||||
|
// NOTE: If output parameter is non-null, we have to return that
|
||||||
|
// image, instead of result
|
||||||
|
if (pOutput != null) {
|
||||||
|
//pOutput.setData(result.getRaster()); // Fast, but less compatible
|
||||||
|
// NOTE: For some reason, this is sometimes super-slow...?
|
||||||
|
ImageUtil.drawOnto(pOutput, result);
|
||||||
|
result = pOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Simply fall through, allowing acceleration to be added later
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BufferedImage resampleMagick(BufferedImage pSrc, int pWidth, int pHeight, int pFilterType) {
|
||||||
|
// Convert to Magick, scale and convert back
|
||||||
|
MagickImage image = null;
|
||||||
|
MagickImage scaled = null;
|
||||||
|
try {
|
||||||
|
image = MagickUtil.toMagick(pSrc);
|
||||||
|
|
||||||
|
long start = 0;
|
||||||
|
if (DEBUG) {
|
||||||
|
start = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: setFilter affects zoomImage, NOT scaleImage
|
||||||
|
image.setFilter(pFilterType);
|
||||||
|
scaled = image.zoomImage(pWidth, pHeight);
|
||||||
|
//scaled = image.scaleImage(pWidth, pHeight); // AREA_AVERAGING
|
||||||
|
|
||||||
|
if (DEBUG) {
|
||||||
|
long time = System.currentTimeMillis() - start;
|
||||||
|
System.out.println("Filtered: " + time + " ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
return MagickUtil.toBuffered(scaled);
|
||||||
|
}
|
||||||
|
//catch (MagickException e) {
|
||||||
|
catch (Exception e) {
|
||||||
|
// NOTE: Stupid workaround: If MagickException is caught, a
|
||||||
|
// NoClassDefFoundError is thrown, when MagickException class is
|
||||||
|
// unavailable...
|
||||||
|
if (e instanceof RuntimeException) {
|
||||||
|
throw (RuntimeException) e;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ImageConversionException(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
// NOTE: ImageMagick might be unstable after a while, if image data
|
||||||
|
// is not deallocated. The GC/finalize method handles this, but in
|
||||||
|
// special circumstances, it's not triggered often enough.
|
||||||
|
if (image != null) {
|
||||||
|
image.destroyImages();
|
||||||
|
}
|
||||||
|
if (scaled != null) {
|
||||||
|
scaled.destroyImages();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,616 @@
|
|||||||
|
/*
|
||||||
|
* 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.image;
|
||||||
|
|
||||||
|
import magick.ImageType;
|
||||||
|
import magick.MagickException;
|
||||||
|
import magick.MagickImage;
|
||||||
|
import magick.PixelPacket;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.color.ColorSpace;
|
||||||
|
import java.awt.color.ICC_ColorSpace;
|
||||||
|
import java.awt.color.ICC_Profile;
|
||||||
|
import java.awt.image.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility for converting JMagick {@code MagickImage}s to standard Java
|
||||||
|
* {@code BufferedImage}s and back.
|
||||||
|
* <p/>
|
||||||
|
* <em>NOTE: This class is considered an implementation detail and not part of
|
||||||
|
* the public API. This class is subject to change without further notice.
|
||||||
|
* You have been warned. :-)</em>
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/MagickUtil.java#4 $
|
||||||
|
*/
|
||||||
|
public final class MagickUtil {
|
||||||
|
// IMPORTANT NOTE: Disaster happens if any of these constants are used outside this class
|
||||||
|
// because you then have a dependency on MagickException (this is due to Java class loading
|
||||||
|
// and initialization magic).
|
||||||
|
// Do not use outside this class. If the constants need to be shared, move to Magick or ImageUtil.
|
||||||
|
|
||||||
|
/** Color Model usesd for bilevel (B/W) */
|
||||||
|
private static final IndexColorModel CM_MONOCHROME = MonochromeColorModel.getInstance();
|
||||||
|
|
||||||
|
/** Color Model usesd for raw ABGR */
|
||||||
|
private static final ColorModel CM_COLOR_ALPHA =
|
||||||
|
new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[] {8, 8, 8, 8},
|
||||||
|
true, true, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);
|
||||||
|
|
||||||
|
/** Color Model usesd for raw BGR */
|
||||||
|
private static final ColorModel CM_COLOR_OPAQUE =
|
||||||
|
new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[] {8, 8, 8},
|
||||||
|
false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
|
||||||
|
|
||||||
|
/** Color Model usesd for raw RGB */
|
||||||
|
//private static final ColorModel CM_COLOR_RGB = new DirectColorModel(24, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x0);
|
||||||
|
|
||||||
|
/** Color Model usesd for raw GRAY + ALPHA */
|
||||||
|
private static final ColorModel CM_GRAY_ALPHA =
|
||||||
|
new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY),
|
||||||
|
true, true, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);
|
||||||
|
|
||||||
|
/** Color Model usesd for raw GRAY */
|
||||||
|
private static final ColorModel CM_GRAY_OPAQUE =
|
||||||
|
new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY),
|
||||||
|
false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
|
||||||
|
|
||||||
|
/** Band offsets for raw ABGR */
|
||||||
|
private static final int[] BAND_OFF_TRANS = new int[] {3, 2, 1, 0};
|
||||||
|
|
||||||
|
/** Band offsets for raw BGR */
|
||||||
|
private static final int[] BAND_OFF_OPAQUE = new int[] {2, 1, 0};
|
||||||
|
|
||||||
|
/** The point at {@code 0, 0} */
|
||||||
|
private static final Point LOCATION_UPPER_LEFT = new Point(0, 0);
|
||||||
|
|
||||||
|
private static final boolean DEBUG = Magick.DEBUG;
|
||||||
|
|
||||||
|
// Only static members and methods
|
||||||
|
private MagickUtil() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a {@code MagickImage} to a {@code BufferedImage}.
|
||||||
|
* <p/>
|
||||||
|
* The conversion depends on {@code pImage}'s {@code ImageType}:
|
||||||
|
* <dl>
|
||||||
|
* <dt>{@code ImageType.BilevelType}</dt>
|
||||||
|
* <dd>{@code BufferedImage} of type {@code TYPE_BYTE_BINARY}</dd>
|
||||||
|
*
|
||||||
|
* <dt>{@code ImageType.GrayscaleType}</dt>
|
||||||
|
* <dd>{@code BufferedImage} of type {@code TYPE_BYTE_GRAY}</dd>
|
||||||
|
* <dt>{@code ImageType.GrayscaleMatteType}</dt>
|
||||||
|
* <dd>{@code BufferedImage} of type {@code TYPE_USHORT_GRAY}</dd>
|
||||||
|
*
|
||||||
|
* <dt>{@code ImageType.PaletteType}</dt>
|
||||||
|
* <dd>{@code BufferedImage} of type {@code TYPE_BYTE_BINARY} (for images
|
||||||
|
* with a palette of <= 16 colors) or {@code TYPE_BYTE_INDEXED}</dd>
|
||||||
|
* <dt>{@code ImageType.PaletteMatteType}</dt>
|
||||||
|
* <dd>{@code BufferedImage} of type {@code TYPE_BYTE_BINARY} (for images
|
||||||
|
* with a palette of <= 16 colors) or {@code TYPE_BYTE_INDEXED}</dd>
|
||||||
|
*
|
||||||
|
* <dt>{@code ImageType.TrueColorType}</dt>
|
||||||
|
* <dd>{@code BufferedImage} of type {@code TYPE_3BYTE_BGR}</dd>
|
||||||
|
* <dt>{@code ImageType.TrueColorPaletteType}</dt>
|
||||||
|
* <dd>{@code BufferedImage} of type {@code TYPE_4BYTE_ABGR}</dd>
|
||||||
|
*
|
||||||
|
* @param pImage the original {@code MagickImage}
|
||||||
|
* @return a new {@code BufferedImage}
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if {@code pImage} is {@code null}
|
||||||
|
* or if the {@code ImageType} is not one mentioned above.
|
||||||
|
* @throws MagickException if an exception occurs during conversion
|
||||||
|
*
|
||||||
|
* @see BufferedImage
|
||||||
|
*/
|
||||||
|
public static BufferedImage toBuffered(MagickImage pImage) throws MagickException {
|
||||||
|
if (pImage == null) {
|
||||||
|
throw new IllegalArgumentException("image == null");
|
||||||
|
}
|
||||||
|
|
||||||
|
long start = 0L;
|
||||||
|
if (DEBUG) {
|
||||||
|
start = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferedImage image = null;
|
||||||
|
try {
|
||||||
|
switch (pImage.getImageType()) {
|
||||||
|
case ImageType.BilevelType:
|
||||||
|
image = bilevelToBuffered(pImage);
|
||||||
|
break;
|
||||||
|
case ImageType.GrayscaleType:
|
||||||
|
image = grayToBuffered(pImage, false);
|
||||||
|
break;
|
||||||
|
case ImageType.GrayscaleMatteType:
|
||||||
|
image = grayToBuffered(pImage, true);
|
||||||
|
break;
|
||||||
|
case ImageType.PaletteType:
|
||||||
|
image = paletteToBuffered(pImage, false);
|
||||||
|
break;
|
||||||
|
case ImageType.PaletteMatteType:
|
||||||
|
image = paletteToBuffered(pImage, true);
|
||||||
|
break;
|
||||||
|
case ImageType.TrueColorType:
|
||||||
|
image = rgbToBuffered(pImage, false);
|
||||||
|
break;
|
||||||
|
case ImageType.TrueColorMatteType:
|
||||||
|
image = rgbToBuffered(pImage, true);
|
||||||
|
break;
|
||||||
|
case ImageType.ColorSeparationType:
|
||||||
|
image = cmykToBuffered(pImage, false);
|
||||||
|
break;
|
||||||
|
case ImageType.ColorSeparationMatteType:
|
||||||
|
image = cmykToBuffered(pImage, true);
|
||||||
|
break;
|
||||||
|
case ImageType.OptimizeType:
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Unknown JMagick image type: " + pImage.getImageType());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if (DEBUG) {
|
||||||
|
long time = System.currentTimeMillis() - start;
|
||||||
|
System.out.println("Converted JMagick image type: " + pImage.getImageType() + " to BufferedImage: " + image);
|
||||||
|
System.out.println("Conversion to BufferedImage: " + time + " ms");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a {@code BufferedImage} to a {@code MagickImage}.
|
||||||
|
* <p/>
|
||||||
|
* The conversion depends on {@code pImage}'s {@code ColorModel}:
|
||||||
|
* <dl>
|
||||||
|
* <dt>{@code IndexColorModel} with 1 bit b/w</dt>
|
||||||
|
* <dd>{@code MagickImage} of type {@code ImageType.BilevelType}</dd>
|
||||||
|
* <dt>{@code IndexColorModel} > 1 bit,</dt>
|
||||||
|
* <dd>{@code MagickImage} of type {@code ImageType.PaletteType}
|
||||||
|
* or {@code MagickImage} of type {@code ImageType.PaletteMatteType}
|
||||||
|
* depending on <tt>ColorModel.getAlpha()</dd>
|
||||||
|
*
|
||||||
|
* <dt>{@code ColorModel.getColorSpace().getType() == ColorSpace.TYPE_GRAY}</dt>
|
||||||
|
* <dd>{@code MagickImage} of type {@code ImageType.GrayscaleType}
|
||||||
|
* or {@code MagickImage} of type {@code ImageType.GrayscaleMatteType}
|
||||||
|
* depending on <tt>ColorModel.getAlpha()</dd>
|
||||||
|
*
|
||||||
|
* <dt>{@code ColorModel.getColorSpace().getType() == ColorSpace.TYPE_RGB}</dt>
|
||||||
|
* <dd>{@code MagickImage} of type {@code ImageType.TrueColorType}
|
||||||
|
* or {@code MagickImage} of type {@code ImageType.TrueColorPaletteType}</dd>
|
||||||
|
*
|
||||||
|
* @param pImage the original {@code BufferedImage}
|
||||||
|
* @return a new {@code MagickImage}
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if {@code pImage} is {@code null}
|
||||||
|
* or if the {@code ColorModel} is not one mentioned above.
|
||||||
|
* @throws MagickException if an exception occurs during conversion
|
||||||
|
*
|
||||||
|
* @see BufferedImage
|
||||||
|
*/
|
||||||
|
public static MagickImage toMagick(BufferedImage pImage) throws MagickException {
|
||||||
|
if (pImage == null) {
|
||||||
|
throw new IllegalArgumentException("image == null");
|
||||||
|
}
|
||||||
|
|
||||||
|
long start = 0L;
|
||||||
|
if (DEBUG) {
|
||||||
|
start = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
ColorModel cm = pImage.getColorModel();
|
||||||
|
if (cm instanceof IndexColorModel) {
|
||||||
|
// Handles both BilevelType, PaletteType and PaletteMatteType
|
||||||
|
return indexedToMagick(pImage, (IndexColorModel) cm, cm.hasAlpha());
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (cm.getColorSpace().getType()) {
|
||||||
|
case ColorSpace.TYPE_GRAY:
|
||||||
|
// Handles GrayType and GrayMatteType
|
||||||
|
return grayToMagick(pImage, cm.hasAlpha());
|
||||||
|
case ColorSpace.TYPE_RGB:
|
||||||
|
// Handles TrueColorType and TrueColorMatteType
|
||||||
|
return rgbToMagic(pImage, cm.hasAlpha());
|
||||||
|
case ColorSpace.TYPE_CMY:
|
||||||
|
case ColorSpace.TYPE_CMYK:
|
||||||
|
case ColorSpace.TYPE_HLS:
|
||||||
|
case ColorSpace.TYPE_HSV:
|
||||||
|
// Other types not supported yet
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Unknown buffered image type: " + pImage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if (DEBUG) {
|
||||||
|
long time = System.currentTimeMillis() - start;
|
||||||
|
System.out.println("Conversion to MagickImage: " + time + " ms");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MagickImage rgbToMagic(BufferedImage pImage, boolean pAlpha) throws MagickException {
|
||||||
|
MagickImage image = new MagickImage();
|
||||||
|
|
||||||
|
BufferedImage buffered = ImageUtil.toBuffered(pImage, pAlpha ? BufferedImage.TYPE_4BYTE_ABGR : BufferedImage.TYPE_3BYTE_BGR);
|
||||||
|
|
||||||
|
// Need to get data of sub raster, not the full data array, this is
|
||||||
|
// just a convenient way
|
||||||
|
Raster raster;
|
||||||
|
if (buffered.getRaster().getParent() != null) {
|
||||||
|
raster = buffered.getData(new Rectangle(buffered.getWidth(), buffered.getHeight()));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
raster = buffered.getRaster();
|
||||||
|
}
|
||||||
|
|
||||||
|
image.constituteImage(buffered.getWidth(), buffered.getHeight(), pAlpha ? "ABGR" : "BGR",
|
||||||
|
((DataBufferByte) raster.getDataBuffer()).getData());
|
||||||
|
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MagickImage grayToMagick(BufferedImage pImage, boolean pAlpha) throws MagickException {
|
||||||
|
MagickImage image = new MagickImage();
|
||||||
|
|
||||||
|
// TODO: Make a fix for TYPE_USHORT_GRAY
|
||||||
|
// The code below does not seem to work (JMagick issues?)...
|
||||||
|
/*
|
||||||
|
if (pImage.getType() == BufferedImage.TYPE_USHORT_GRAY) {
|
||||||
|
short[] data = ((DataBufferUShort) pImage.getRaster().getDataBuffer()).getData();
|
||||||
|
int[] intData = new int[data.length];
|
||||||
|
for (int i = 0; i < data.length; i++) {
|
||||||
|
intData[i] = (data[i] & 0xffff) * 0xffff;
|
||||||
|
}
|
||||||
|
image.constituteImage(pImage.getWidth(), pImage.getHeight(), "I", intData);
|
||||||
|
|
||||||
|
System.out.println("storageClass: " + image.getStorageClass());
|
||||||
|
System.out.println("depth: " + image.getDepth());
|
||||||
|
System.out.println("imageType: " + image.getImageType());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
*/
|
||||||
|
BufferedImage buffered = ImageUtil.toBuffered(pImage, pAlpha ? BufferedImage.TYPE_4BYTE_ABGR : BufferedImage.TYPE_BYTE_GRAY);
|
||||||
|
|
||||||
|
// Need to get data of sub raster, not the full data array, this is
|
||||||
|
// just a convenient way
|
||||||
|
Raster raster;
|
||||||
|
if (buffered.getRaster().getParent() != null) {
|
||||||
|
raster = buffered.getData(new Rectangle(buffered.getWidth(), buffered.getHeight()));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
raster = buffered.getRaster();
|
||||||
|
}
|
||||||
|
|
||||||
|
image.constituteImage(buffered.getWidth(), buffered.getHeight(), pAlpha ? "ABGR" : "I", ((DataBufferByte) raster.getDataBuffer()).getData());
|
||||||
|
//}
|
||||||
|
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MagickImage indexedToMagick(BufferedImage pImage, IndexColorModel pColorModel, boolean pAlpha) throws MagickException {
|
||||||
|
MagickImage image = rgbToMagic(pImage, pAlpha);
|
||||||
|
|
||||||
|
int mapSize = pColorModel.getMapSize();
|
||||||
|
image.setNumberColors(mapSize);
|
||||||
|
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
public static MagickImage toMagick(BufferedImage pImage) throws MagickException {
|
||||||
|
if (pImage == null) {
|
||||||
|
throw new IllegalArgumentException("image == null");
|
||||||
|
}
|
||||||
|
|
||||||
|
final int width = pImage.getWidth();
|
||||||
|
final int height = pImage.getHeight();
|
||||||
|
|
||||||
|
// int ARGB -> byte RGBA conversion
|
||||||
|
// NOTE: This is ImageMagick Q16 compatible raw RGBA format with 16 bits/sample...
|
||||||
|
// For a Q8 build, we could probably go with half the space...
|
||||||
|
// NOTE: This is close to insanity, as it wastes extreme ammounts of memory
|
||||||
|
final int[] argb = new int[width];
|
||||||
|
final byte[] raw16 = new byte[width * height * 8];
|
||||||
|
for (int y = 0; y < height; y++) {
|
||||||
|
// Fetch one line of ARGB data
|
||||||
|
pImage.getRGB(0, y, width, 1, argb, 0, width);
|
||||||
|
|
||||||
|
for (int x = 0; x < width; x++) {
|
||||||
|
int pixel = (x + (y * width)) * 8;
|
||||||
|
raw16[pixel ] = (byte) ((argb[x] >> 16) & 0xff); // R
|
||||||
|
raw16[pixel + 2] = (byte) ((argb[x] >> 8) & 0xff); // G
|
||||||
|
raw16[pixel + 4] = (byte) ((argb[x] ) & 0xff); // B
|
||||||
|
raw16[pixel + 6] = (byte) ((argb[x] >> 24) & 0xff); // A
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create magick image
|
||||||
|
ImageInfo info = new ImageInfo();
|
||||||
|
info.setMagick("RGBA"); // Raw RGBA samples
|
||||||
|
info.setSize(width + "x" + height); // String?!?
|
||||||
|
|
||||||
|
MagickImage image = new MagickImage(info);
|
||||||
|
image.setImageAttribute("depth", "8");
|
||||||
|
|
||||||
|
// Set pixel data in 16 bit raw RGBA format
|
||||||
|
image.blobToImage(info, raw16);
|
||||||
|
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a bi-level {@code MagickImage} to a {@code BufferedImage}, of
|
||||||
|
* type {@code TYPE_BYTE_BINARY}.
|
||||||
|
*
|
||||||
|
* @param pImage the original {@code MagickImage}
|
||||||
|
* @return a new {@code BufferedImage}
|
||||||
|
*
|
||||||
|
* @throws MagickException if an exception occurs during conversion
|
||||||
|
*
|
||||||
|
* @see BufferedImage
|
||||||
|
*/
|
||||||
|
private static BufferedImage bilevelToBuffered(MagickImage pImage) throws MagickException {
|
||||||
|
// As there is no way to get the binary representation of the image,
|
||||||
|
// convert to gray, and the create a binary image from it
|
||||||
|
BufferedImage temp = grayToBuffered(pImage, false);
|
||||||
|
|
||||||
|
BufferedImage image = new BufferedImage(temp.getWidth(), temp.getHeight(), BufferedImage.TYPE_BYTE_BINARY, CM_MONOCHROME);
|
||||||
|
|
||||||
|
ImageUtil.drawOnto(image, temp);
|
||||||
|
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a gray {@code MagickImage} to a {@code BufferedImage}, of
|
||||||
|
* type {@code TYPE_USHORT_GRAY} or {@code TYPE_BYTE_GRAY}.
|
||||||
|
*
|
||||||
|
* @param pImage the original {@code MagickImage}
|
||||||
|
* @param pAlpha keep alpha channel
|
||||||
|
* @return a new {@code BufferedImage}
|
||||||
|
*
|
||||||
|
* @throws MagickException if an exception occurs during conversion
|
||||||
|
*
|
||||||
|
* @see BufferedImage
|
||||||
|
*/
|
||||||
|
private static BufferedImage grayToBuffered(MagickImage pImage, boolean pAlpha) throws MagickException {
|
||||||
|
Dimension size = pImage.getDimension();
|
||||||
|
int length = size.width * size.height;
|
||||||
|
int bands = pAlpha ? 2 : 1;
|
||||||
|
byte[] pixels = new byte[length * bands];
|
||||||
|
|
||||||
|
// TODO: Make a fix for 16 bit TYPE_USHORT_GRAY?!
|
||||||
|
// Note: The ordering AI or I corresponds to BufferedImage
|
||||||
|
// TYPE_CUSTOM and TYPE_BYTE_GRAY respectively
|
||||||
|
pImage.dispatchImage(0, 0, size.width, size.height, pAlpha ? "AI" : "I", pixels);
|
||||||
|
|
||||||
|
// Init databuffer with array, to avoid allocation of empty array
|
||||||
|
DataBuffer buffer = new DataBufferByte(pixels, pixels.length);
|
||||||
|
|
||||||
|
int[] bandOffsets = pAlpha ? new int[] {1, 0} : new int[] {0};
|
||||||
|
|
||||||
|
WritableRaster raster =
|
||||||
|
Raster.createInterleavedRaster(buffer, size.width, size.height,
|
||||||
|
size.width * bands, bands, bandOffsets, LOCATION_UPPER_LEFT);
|
||||||
|
|
||||||
|
return new BufferedImage(pAlpha ? CM_GRAY_ALPHA : CM_GRAY_OPAQUE, raster, pAlpha, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a palette-based {@code MagickImage} to a
|
||||||
|
* {@code BufferedImage}, of type {@code TYPE_BYTE_BINARY} (for images
|
||||||
|
* with a palette of <= 16 colors) or {@code TYPE_BYTE_INDEXED}.
|
||||||
|
*
|
||||||
|
* @param pImage the original {@code MagickImage}
|
||||||
|
* @param pAlpha keep alpha channel
|
||||||
|
* @return a new {@code BufferedImage}
|
||||||
|
*
|
||||||
|
* @throws MagickException if an exception occurs during conversion
|
||||||
|
*
|
||||||
|
* @see BufferedImage
|
||||||
|
*/
|
||||||
|
private static BufferedImage paletteToBuffered(MagickImage pImage, boolean pAlpha) throws MagickException {
|
||||||
|
// Create indexcolormodel for the image
|
||||||
|
IndexColorModel cm;
|
||||||
|
|
||||||
|
try {
|
||||||
|
cm = createIndexColorModel(pImage.getColormap(), pAlpha);
|
||||||
|
}
|
||||||
|
catch (MagickException e) {
|
||||||
|
// NOTE: Some MagickImages incorrecly (?) reports to be paletteType,
|
||||||
|
// but does not have a colormap, this is a workaround.
|
||||||
|
return rgbToBuffered(pImage, pAlpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
// As there is no way to get the indexes of an indexed image, convert to
|
||||||
|
// RGB, and the create an indexed image from it
|
||||||
|
BufferedImage temp = rgbToBuffered(pImage, pAlpha);
|
||||||
|
|
||||||
|
BufferedImage image;
|
||||||
|
if (cm.getMapSize() <= 16) {
|
||||||
|
image = new BufferedImage(temp.getWidth(), temp.getHeight(), BufferedImage.TYPE_BYTE_BINARY, cm);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
image = new BufferedImage(temp.getWidth(), temp.getHeight(), BufferedImage.TYPE_BYTE_INDEXED, cm);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create transparent background for images containing alpha
|
||||||
|
if (pAlpha) {
|
||||||
|
Graphics2D g = image.createGraphics();
|
||||||
|
try {
|
||||||
|
g.setComposite(AlphaComposite.Clear);
|
||||||
|
g.fillRect(0, 0, temp.getWidth(), temp.getHeight());
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
g.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: This is (surprisingly) much faster than using g2d.drawImage()..
|
||||||
|
// (Tests shows 20-30ms, vs. 600-700ms on the same image)
|
||||||
|
BufferedImageOp op = new CopyDither(cm);
|
||||||
|
op.filter(temp, image);
|
||||||
|
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an {@code IndexColorModel} from an array of
|
||||||
|
* {@code PixelPacket}s.
|
||||||
|
*
|
||||||
|
* @param pColormap the original colormap as a {@code PixelPacket} array
|
||||||
|
* @param pAlpha keep alpha channel
|
||||||
|
*
|
||||||
|
* @return a new {@code IndexColorModel}
|
||||||
|
*/
|
||||||
|
public static IndexColorModel createIndexColorModel(PixelPacket[] pColormap, boolean pAlpha) {
|
||||||
|
int[] colors = new int[pColormap.length];
|
||||||
|
|
||||||
|
// TODO: Verify if this is correct for alpha...?
|
||||||
|
int trans = pAlpha ? colors.length - 1 : -1;
|
||||||
|
|
||||||
|
//for (int i = 0; i < pColormap.length; i++) {
|
||||||
|
for (int i = pColormap.length - 1; i != 0; i--) {
|
||||||
|
PixelPacket color = pColormap[i];
|
||||||
|
if (pAlpha) {
|
||||||
|
colors[i] = (0xff - (color.getOpacity() & 0xff)) << 24 |
|
||||||
|
(color.getRed() & 0xff) << 16 |
|
||||||
|
(color.getGreen() & 0xff) << 8 |
|
||||||
|
(color.getBlue() & 0xff);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
colors[i] = (color.getRed() & 0xff) << 16 |
|
||||||
|
(color.getGreen() & 0xff) << 8 |
|
||||||
|
(color.getBlue() & 0xff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new InverseColorMapIndexColorModel(8, colors.length, colors, 0, pAlpha, trans, DataBuffer.TYPE_BYTE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an (A)RGB {@code MagickImage} to a {@code BufferedImage}, of
|
||||||
|
* type {@code TYPE_4BYTE_ABGR} or {@code TYPE_3BYTE_BGR}.
|
||||||
|
*
|
||||||
|
* @param pImage the original {@code MagickImage}
|
||||||
|
* @param pAlpha keep alpha channel
|
||||||
|
* @return a new {@code BufferedImage}
|
||||||
|
*
|
||||||
|
* @throws MagickException if an exception occurs during conversion
|
||||||
|
*
|
||||||
|
* @see BufferedImage
|
||||||
|
*/
|
||||||
|
private static BufferedImage rgbToBuffered(MagickImage pImage, boolean pAlpha) throws MagickException {
|
||||||
|
Dimension size = pImage.getDimension();
|
||||||
|
int length = size.width * size.height;
|
||||||
|
int bands = pAlpha ? 4 : 3;
|
||||||
|
byte[] pixels = new byte[length * bands];
|
||||||
|
|
||||||
|
// TODO: If we do multiple dispatches (one per line, typically), we could provide listener
|
||||||
|
// feedback. But it's currently a lot slower than fetching all the pixels in one go.
|
||||||
|
|
||||||
|
// Note: The ordering ABGR or BGR corresponds to BufferedImage
|
||||||
|
// TYPE_4BYTE_ABGR and TYPE_3BYTE_BGR respectively
|
||||||
|
pImage.dispatchImage(0, 0, size.width, size.height, pAlpha ? "ABGR" : "BGR", pixels);
|
||||||
|
|
||||||
|
// Init databuffer with array, to avoid allocation of empty array
|
||||||
|
DataBuffer buffer = new DataBufferByte(pixels, pixels.length);
|
||||||
|
|
||||||
|
int[] bandOffsets = pAlpha ? BAND_OFF_TRANS : BAND_OFF_OPAQUE;
|
||||||
|
|
||||||
|
WritableRaster raster =
|
||||||
|
Raster.createInterleavedRaster(buffer, size.width, size.height,
|
||||||
|
size.width * bands, bands, bandOffsets, LOCATION_UPPER_LEFT);
|
||||||
|
|
||||||
|
return new BufferedImage(pAlpha ? CM_COLOR_ALPHA : CM_COLOR_OPAQUE, raster, pAlpha, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an {@code MagickImage} to a {@code BufferedImage} which holds an CMYK ICC profile
|
||||||
|
*
|
||||||
|
* @param pImage the original {@code MagickImage}
|
||||||
|
* @param pAlpha keep alpha channel
|
||||||
|
* @return a new {@code BufferedImage}
|
||||||
|
*
|
||||||
|
* @throws MagickException if an exception occurs during conversion
|
||||||
|
*
|
||||||
|
* @see BufferedImage
|
||||||
|
*/
|
||||||
|
private static BufferedImage cmykToBuffered(MagickImage pImage, boolean pAlpha) throws MagickException {
|
||||||
|
Dimension size = pImage.getDimension();
|
||||||
|
int length = size.width * size.height;
|
||||||
|
|
||||||
|
// Retreive the ICC profile
|
||||||
|
ICC_Profile profile = ICC_Profile.getInstance(pImage.getColorProfile().getInfo());
|
||||||
|
ColorSpace cs = new ICC_ColorSpace(profile);
|
||||||
|
|
||||||
|
int bands = cs.getNumComponents() + (pAlpha ? 1 : 0);
|
||||||
|
|
||||||
|
int[] bits = new int[bands];
|
||||||
|
for (int i = 0; i < bands; i++) {
|
||||||
|
bits[i] = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
ColorModel cm = pAlpha ?
|
||||||
|
new ComponentColorModel(cs, bits, true, true, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE) :
|
||||||
|
new ComponentColorModel(cs, bits, false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
|
||||||
|
|
||||||
|
byte[] pixels = new byte[length * bands];
|
||||||
|
|
||||||
|
// TODO: If we do multiple dispatches (one per line, typically), we could provide listener
|
||||||
|
// feedback. But it's currently a lot slower than fetching all the pixels in one go.
|
||||||
|
// TODO: handle more generic cases if profile is not CMYK
|
||||||
|
// TODO: Test "ACMYK"
|
||||||
|
pImage.dispatchImage(0, 0, size.width, size.height, pAlpha ? "ACMYK" : "CMYK", pixels);
|
||||||
|
|
||||||
|
// Init databuffer with array, to avoid allocation of empty array
|
||||||
|
DataBuffer buffer = new DataBufferByte(pixels, pixels.length);
|
||||||
|
|
||||||
|
// TODO: build array from bands variable, here it just works for CMYK
|
||||||
|
// The values has not been tested with an alpha picture actually...
|
||||||
|
int[] bandOffsets = pAlpha ? new int[] {0, 1, 2, 3, 4} : new int[] {0, 1, 2, 3};
|
||||||
|
|
||||||
|
WritableRaster raster =
|
||||||
|
Raster.createInterleavedRaster(buffer, size.width, size.height,
|
||||||
|
size.width * bands, bands, bandOffsets, LOCATION_UPPER_LEFT);
|
||||||
|
|
||||||
|
return new BufferedImage(cm, raster, pAlpha, null);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -55,7 +55,9 @@
|
|||||||
package com.twelvemonkeys.image;
|
package com.twelvemonkeys.image;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.geom.*;
|
import java.awt.geom.AffineTransform;
|
||||||
|
import java.awt.geom.Point2D;
|
||||||
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.awt.image.*;
|
import java.awt.image.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -65,42 +67,47 @@ import java.awt.image.*;
|
|||||||
* constructor, either using the
|
* constructor, either using the
|
||||||
* <a href="#field_summary">filter type constants</a>, or one of the
|
* <a href="#field_summary">filter type constants</a>, or one of the
|
||||||
* {@code RendereingHints}.
|
* {@code RendereingHints}.
|
||||||
* <p>
|
* <p/>
|
||||||
* For fastest results, use {@link #FILTER_POINT} or {@link #FILTER_BOX}.
|
* For fastest results, use {@link #FILTER_POINT} or {@link #FILTER_BOX}.
|
||||||
* In most cases, {@link #FILTER_TRIANGLE} will produce acceptable results, while
|
* In most cases, {@link #FILTER_TRIANGLE} will produce acceptable results, while
|
||||||
* being relatively fast.
|
* being relatively fast.
|
||||||
* For higher quality output, use more sophisticated interpolation algorithms,
|
* For higher quality output, use more sophisticated interpolation algorithms,
|
||||||
* like {@link #FILTER_MITCHELL} or {@link #FILTER_LANCZOS}.
|
* like {@link #FILTER_MITCHELL} or {@link #FILTER_LANCZOS}.
|
||||||
* </p>
|
* <p/>
|
||||||
* <p>
|
|
||||||
* Example:
|
* Example:
|
||||||
* </p>
|
|
||||||
* <blockquote><pre>
|
* <blockquote><pre>
|
||||||
* BufferedImage image;
|
* BufferedImage image;
|
||||||
*
|
* <p/>
|
||||||
* //...
|
* //...
|
||||||
*
|
* <p/>
|
||||||
* ResampleOp resampler = new ResampleOp(100, 100, ResampleOp.FILTER_TRIANGLE);
|
* ResampleOp resampler = new ResampleOp(100, 100, ResampleOp.FILTER_TRIANGLE);
|
||||||
* BufferedImage thumbnail = resampler.filter(image, null);
|
* BufferedImage thumbnail = resampler.filter(image, null);
|
||||||
* </pre></blockquote>
|
* </pre></blockquote>
|
||||||
* <p>
|
* <p/>
|
||||||
* If your input image is very large, it's possible to first resample using the
|
* If your imput image is very large, it's possible to first resample using the
|
||||||
* very fast {@code FILTER_POINT} algorithm, then resample to the wanted size,
|
* very fast {@code FILTER_POINT} algorithm, then resample to the wanted size,
|
||||||
* using a higher quality algorithm:
|
* using a higher quality algorithm:
|
||||||
* </p>
|
|
||||||
* <blockquote><pre>
|
* <blockquote><pre>
|
||||||
* BufferedImage verylLarge;
|
* BufferedImage verylLarge;
|
||||||
*
|
* <p/>
|
||||||
* //...
|
* //...
|
||||||
*
|
* <p/>
|
||||||
* int w = 300;
|
* int w = 300;
|
||||||
* int h = 200;
|
* int h = 200;
|
||||||
*
|
* <p/>
|
||||||
* BufferedImage temp = new ResampleOp(w * 2, h * 2, FILTER_POINT).filter(verylLarge, null);
|
* BufferedImage temp = new ResampleOp(w * 2, h * 2, FILTER_POINT).filter(verylLarge, null);
|
||||||
*
|
* <p/>
|
||||||
* BufferedImage scaled = new ResampleOp(w, h).filter(temp, null);
|
* BufferedImage scaled = new ResampleOp(w, h).filter(temp, null);
|
||||||
* </pre></blockquote>
|
* </pre></blockquote>
|
||||||
* <p>
|
* <p/>
|
||||||
|
* For maximum performance, this class will use native code, through
|
||||||
|
* <a href="http://www.yeo.id.au/jmagick/">JMagick</a>, when available.
|
||||||
|
* Otherwise, the class will silently fall back to pure Java mode.
|
||||||
|
* Native code may be disabled globally, by setting the system property
|
||||||
|
* {@code com.twelvemonkeys.image.accel} to {@code false}.
|
||||||
|
* To allow debug of the native code, set the system property
|
||||||
|
* {@code com.twelvemonkeys.image.magick.debug} to {@code true}.
|
||||||
|
* <p/>
|
||||||
* This {@code BufferedImageOp} is based on C example code found in
|
* This {@code BufferedImageOp} is based on C example code found in
|
||||||
* <a href="http://www.acm.org/tog/GraphicsGems/">Graphics Gems III</a>,
|
* <a href="http://www.acm.org/tog/GraphicsGems/">Graphics Gems III</a>,
|
||||||
* Filtered Image Rescaling, by Dale Schumacher (with additional improvments by
|
* Filtered Image Rescaling, by Dale Schumacher (with additional improvments by
|
||||||
@@ -109,12 +116,10 @@ import java.awt.image.*;
|
|||||||
* <a href="http://www.imagemagick.org/">ImageMagick</a> and
|
* <a href="http://www.imagemagick.org/">ImageMagick</a> and
|
||||||
* Marco Schmidt's <a href="http://schmidt.devlib.org/jiu/">Java Imaging Utilities</a>
|
* Marco Schmidt's <a href="http://schmidt.devlib.org/jiu/">Java Imaging Utilities</a>
|
||||||
* (which are also adaptions of the same original code from Graphics Gems III).
|
* (which are also adaptions of the same original code from Graphics Gems III).
|
||||||
* </p>
|
* <p/>
|
||||||
* <p>
|
|
||||||
* For a description of the various interpolation algorithms, see
|
* For a description of the various interpolation algorithms, see
|
||||||
* <em>General Filtered Image Rescaling</em> in <em>Graphics Gems III</em>,
|
* <em>General Filtered Image Rescaling</em> in <em>Graphics Gems III</em>,
|
||||||
* Academic Press, 1994.
|
* Academic Press, 1994.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @author last modified by $Author: haku $
|
* @author last modified by $Author: haku $
|
||||||
@@ -128,6 +133,9 @@ import java.awt.image.*;
|
|||||||
// TODO: Consider using AffineTransformOp for more operations!?
|
// TODO: Consider using AffineTransformOp for more operations!?
|
||||||
public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
|
public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
|
||||||
|
|
||||||
|
// NOTE: These MUST correspond to ImageMagick filter types, for the
|
||||||
|
// MagickAccelerator to work consistently (see magick.FilterType).
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Undefined interpolation, filter method will use default filter.
|
* Undefined interpolation, filter method will use default filter.
|
||||||
*/
|
*/
|
||||||
@@ -281,10 +289,11 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
|
|||||||
new Value(KEY_RESAMPLE_INTERPOLATION, "Blackman-Sinc", FILTER_BLACKMAN_SINC);
|
new Value(KEY_RESAMPLE_INTERPOLATION, "Blackman-Sinc", FILTER_BLACKMAN_SINC);
|
||||||
|
|
||||||
// Member variables
|
// Member variables
|
||||||
private final int width;
|
// Package access, to allow access from MagickAccelerator
|
||||||
private final int height;
|
int width;
|
||||||
|
int height;
|
||||||
|
|
||||||
private final int filterType;
|
int filterType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RendereingHints.Key implementation, works only with Value values.
|
* RendereingHints.Key implementation, works only with Value values.
|
||||||
@@ -352,14 +361,13 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
|
|||||||
* Creates a {@code ResampleOp} that will resample input images to the
|
* Creates a {@code ResampleOp} that will resample input images to the
|
||||||
* given width and height, using the interpolation filter specified by
|
* given width and height, using the interpolation filter specified by
|
||||||
* the given hints.
|
* the given hints.
|
||||||
* <p>
|
|
||||||
* If using {@code RenderingHints}, the hints are mapped as follows:
|
* If using {@code RenderingHints}, the hints are mapped as follows:
|
||||||
* </p>
|
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>{@code KEY_RESAMPLE_INTERPOLATION} takes precedence over any
|
* <li>{@code KEY_RESAMPLE_INTERPOLATION} takes precedence over any
|
||||||
* standard {@code java.awt} hints, and dictates interpolation
|
* standard {@code java.awt} hints, and dictates interpolation
|
||||||
* directly, see
|
* directly, see
|
||||||
* <a href="#field_summary">{@code RenderingHints} constants</a>.</li>
|
* <a href="#field_summary">{@code RenderingHints} constants</a>.</li>
|
||||||
|
* <p/>
|
||||||
* <li>{@code KEY_INTERPOLATION} takes precedence over other hints.
|
* <li>{@code KEY_INTERPOLATION} takes precedence over other hints.
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>{@link RenderingHints#VALUE_INTERPOLATION_NEAREST_NEIGHBOR} specifies
|
* <li>{@link RenderingHints#VALUE_INTERPOLATION_NEAREST_NEIGHBOR} specifies
|
||||||
@@ -370,6 +378,7 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
|
|||||||
* {@code FILTER_QUADRATIC}</li>
|
* {@code FILTER_QUADRATIC}</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* </li>
|
* </li>
|
||||||
|
* <p/>
|
||||||
* <li>{@code KEY_RENDERING} or {@code KEY_COLOR_RENDERING}
|
* <li>{@code KEY_RENDERING} or {@code KEY_COLOR_RENDERING}
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>{@link RenderingHints#VALUE_RENDER_SPEED} specifies
|
* <li>{@link RenderingHints#VALUE_RENDER_SPEED} specifies
|
||||||
@@ -379,9 +388,7 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
|
|||||||
* </ul>
|
* </ul>
|
||||||
* </li>
|
* </li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* <p>
|
|
||||||
* Other hints have no effect on this filter.
|
* Other hints have no effect on this filter.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @param width width of the re-sampled image
|
* @param width width of the re-sampled image
|
||||||
* @param height height of the re-sampled image
|
* @param height height of the re-sampled image
|
||||||
@@ -532,6 +539,16 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
|
|||||||
// Fall through
|
// Fall through
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Try to use native ImageMagick code
|
||||||
|
BufferedImage result = MagickAccelerator.filter(this, input, output);
|
||||||
|
if (result != null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, continue in pure Java mode
|
||||||
|
|
||||||
|
// TODO: What if output != null and wrong size? Create new? Render on only a part? Document?
|
||||||
|
|
||||||
// If filter type != POINT or BOX and input has IndexColorModel, convert
|
// If filter type != POINT or BOX and input has IndexColorModel, convert
|
||||||
// to true color, with alpha reflecting that of the original color model.
|
// to true color, with alpha reflecting that of the original color model.
|
||||||
BufferedImage temp;
|
BufferedImage temp;
|
||||||
@@ -546,7 +563,7 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
|
|||||||
|
|
||||||
// Create or convert output to a suitable image
|
// Create or convert output to a suitable image
|
||||||
// TODO: OPTIMIZE: Don't really need to convert all types to same as input
|
// TODO: OPTIMIZE: Don't really need to convert all types to same as input
|
||||||
BufferedImage result = output != null && temp.getType() != BufferedImage.TYPE_CUSTOM ? /*output*/ ImageUtil.toBuffered(output, temp.getType()) : createCompatibleDestImage(temp, null);
|
result = output != null && temp.getType() != BufferedImage.TYPE_CUSTOM ? /*output*/ ImageUtil.toBuffered(output, temp.getType()) : createCompatibleDestImage(temp, null);
|
||||||
|
|
||||||
resample(temp, result, filter);
|
resample(temp, result, filter);
|
||||||
|
|
||||||
@@ -1255,12 +1272,12 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
|
|||||||
/*
|
/*
|
||||||
* image rescaling routine
|
* image rescaling routine
|
||||||
*/
|
*/
|
||||||
static class Contributor {
|
class Contributor {
|
||||||
int pixel;
|
int pixel;
|
||||||
double weight;
|
double weight;
|
||||||
}
|
}
|
||||||
|
|
||||||
static class ContributorList {
|
class ContributorList {
|
||||||
int n;/* number of contributors (may be < p.length) */
|
int n;/* number of contributors (may be < p.length) */
|
||||||
Contributor[] p;/* pointer to list of contributions */
|
Contributor[] p;/* pointer to list of contributions */
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,10 +34,9 @@ import java.awt.image.ReplicateScaleFilter;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@code ImageFilter} class for subsampling images.
|
* An {@code ImageFilter} class for subsampling images.
|
||||||
* <p>
|
* <p/>
|
||||||
* It is meant to be used in conjunction with a {@code FilteredImageSource}
|
* It is meant to be used in conjunction with a {@code FilteredImageSource}
|
||||||
* object to produce subsampled versions of existing images.
|
* object to produce subsampled versions of existing images.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @see java.awt.image.FilteredImageSource
|
* @see java.awt.image.FilteredImageSource
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -30,9 +30,8 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Classes for image manipulation.
|
* Classes for image manipulation.
|
||||||
* <p>
|
* <p/>
|
||||||
* See the class {@link com.twelvemonkeys.image.ImageUtil}.
|
* See the class {@link com.twelvemonkeys.image.ImageUtil}.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @version 1.0
|
* @version 1.0
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
|
|||||||
+38
-42
@@ -30,24 +30,17 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.image;
|
package com.twelvemonkeys.image;
|
||||||
|
|
||||||
import static java.lang.Math.min;
|
import org.junit.Test;
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
|
import javax.imageio.ImageTypeSpecifier;
|
||||||
import java.awt.color.ColorSpace;
|
import java.awt.color.ColorSpace;
|
||||||
import java.awt.geom.AffineTransform;
|
import java.awt.geom.AffineTransform;
|
||||||
import java.awt.geom.Point2D;
|
import java.awt.geom.Point2D;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.*;
|
||||||
import java.awt.image.BufferedImageOp;
|
|
||||||
import java.awt.image.DataBuffer;
|
|
||||||
import java.awt.image.ImagingOpException;
|
|
||||||
import java.awt.image.Raster;
|
|
||||||
import java.awt.image.RasterOp;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.imageio.ImageTypeSpecifier;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AffineTransformOpTest.
|
* AffineTransformOpTest.
|
||||||
@@ -108,7 +101,6 @@ public class AffineTransformOpTest {
|
|||||||
|
|
||||||
private final int width = 30;
|
private final int width = 30;
|
||||||
private final int height = 20;
|
private final int height = 20;
|
||||||
private final double anchor = min(width, height) / 2.0;
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetPoint2D() {
|
public void testGetPoint2D() {
|
||||||
@@ -136,33 +128,33 @@ public class AffineTransformOpTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFilterRotateBIStandard() {
|
public void testFilterRotateBIStandard() {
|
||||||
BufferedImageOp jreOp = new java.awt.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, anchor, anchor), 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) {
|
for (Integer type : TYPES) {
|
||||||
BufferedImage image = new BufferedImage(width, height, type);
|
BufferedImage image = new BufferedImage(width, height, type);
|
||||||
BufferedImage jreResult = jreOp.filter(image, null);
|
BufferedImage jreResult = jreOp.filter(image, null);
|
||||||
BufferedImage tmResult = tmOp.filter(image, null);
|
BufferedImage tmResult = tmOp.filter(image, null);
|
||||||
|
|
||||||
assertNotNull(tmResult, "No result!");
|
assertNotNull("No result!", tmResult);
|
||||||
assertEquals(jreResult.getType(), tmResult.getType(), "Bad type");
|
assertEquals("Bad type", jreResult.getType(), tmResult.getType());
|
||||||
assertEquals(jreResult.getColorModel(), tmResult.getColorModel(), "Incorrect color model");
|
assertEquals("Incorrect color model", jreResult.getColorModel(), tmResult.getColorModel());
|
||||||
|
|
||||||
assertEquals(jreResult.getWidth(), tmResult.getWidth(), "Incorrect width");
|
assertEquals("Incorrect width", jreResult.getWidth(), tmResult.getWidth());
|
||||||
assertEquals(jreResult.getHeight(), tmResult.getHeight(), "Incorrect height");
|
assertEquals("Incorrect height", jreResult.getHeight(), tmResult.getHeight());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFilterRotateBICustom() {
|
public void testFilterRotateBICustom() {
|
||||||
BufferedImageOp jreOp = new java.awt.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, anchor, anchor), 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) {
|
for (ImageTypeSpecifier spec : SPECS) {
|
||||||
BufferedImage image = spec.createBufferedImage(width, height);
|
BufferedImage image = spec.createBufferedImage(width, height);
|
||||||
|
|
||||||
BufferedImage tmResult = tmOp.filter(image, null);
|
BufferedImage tmResult = tmOp.filter(image, null);
|
||||||
assertNotNull(tmResult, "No result!");
|
assertNotNull("No result!", tmResult);
|
||||||
|
|
||||||
BufferedImage jreResult = null;
|
BufferedImage jreResult = null;
|
||||||
|
|
||||||
@@ -174,18 +166,18 @@ public class AffineTransformOpTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (jreResult != null) {
|
if (jreResult != null) {
|
||||||
assertEquals(jreResult.getType(), tmResult.getType(), "Bad type");
|
assertEquals("Bad type", jreResult.getType(), tmResult.getType());
|
||||||
assertEquals(jreResult.getColorModel(), tmResult.getColorModel(), "Incorrect color model");
|
assertEquals("Incorrect color model", jreResult.getColorModel(), tmResult.getColorModel());
|
||||||
|
|
||||||
assertEquals(jreResult.getWidth(), tmResult.getWidth(), "Incorrect width");
|
assertEquals("Incorrect width", jreResult.getWidth(), tmResult.getWidth());
|
||||||
assertEquals(jreResult.getHeight(), tmResult.getHeight(), "Incorrect height");
|
assertEquals("Incorrect height", jreResult.getHeight(), tmResult.getHeight());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
assertEquals(spec.getBufferedImageType(), tmResult.getType(), "Bad type");
|
assertEquals("Bad type", spec.getBufferedImageType(), tmResult.getType());
|
||||||
assertEquals(spec.getColorModel(), tmResult.getColorModel(), "Incorrect color model");
|
assertEquals("Incorrect color model", spec.getColorModel(), tmResult.getColorModel());
|
||||||
|
|
||||||
assertEquals(height, tmResult.getWidth(), "Incorrect width");
|
assertEquals("Incorrect width", height, tmResult.getWidth());
|
||||||
assertEquals(width, tmResult.getHeight(), "Incorrect height");
|
assertEquals("Incorrect height", width, tmResult.getHeight());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -205,8 +197,8 @@ public class AffineTransformOpTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFilterRotateRasterStandard() {
|
public void testFilterRotateRasterStandard() {
|
||||||
RasterOp jreOp = new java.awt.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, anchor, anchor), 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) {
|
for (Integer type : TYPES) {
|
||||||
Raster raster = new BufferedImage(width, height, type).getRaster();
|
Raster raster = new BufferedImage(width, height, type).getRaster();
|
||||||
@@ -229,25 +221,27 @@ public class AffineTransformOpTest {
|
|||||||
fail("No result!");
|
fail("No result!");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
System.err.println("AffineTransformOpTest.testFilterRotateRasterStandard");
|
||||||
|
System.err.println("type: " + type);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (jreResult != null) {
|
if (jreResult != null) {
|
||||||
assertEquals(jreResult.getWidth(), tmResult.getWidth(), "Incorrect width");
|
assertEquals("Incorrect width", jreResult.getWidth(), tmResult.getWidth());
|
||||||
assertEquals(jreResult.getHeight(), tmResult.getHeight(), "Incorrect height");
|
assertEquals("Incorrect height", jreResult.getHeight(), tmResult.getHeight());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
assertEquals(height, tmResult.getWidth(), "Incorrect width");
|
assertEquals("Incorrect width", height, tmResult.getWidth());
|
||||||
assertEquals(width, tmResult.getHeight(), "Incorrect height");
|
assertEquals("Incorrect height", width, tmResult.getHeight());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFilterRotateRasterCustom() {
|
public void testFilterRotateRasterCustom() {
|
||||||
RasterOp jreOp = new java.awt.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, anchor, anchor), 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) {
|
for (ImageTypeSpecifier spec : SPECS) {
|
||||||
Raster raster = spec.createBufferedImage(width, height).getRaster();
|
Raster raster = spec.createBufferedImage(width, height).getRaster();
|
||||||
@@ -270,17 +264,19 @@ public class AffineTransformOpTest {
|
|||||||
fail("No result!");
|
fail("No result!");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
System.err.println("AffineTransformOpTest.testFilterRotateRasterCustom");
|
||||||
|
System.err.println("spec: " + spec);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (jreResult != null) {
|
if (jreResult != null) {
|
||||||
assertEquals(jreResult.getWidth(), tmResult.getWidth(), "Incorrect width");
|
assertEquals("Incorrect width", jreResult.getWidth(), tmResult.getWidth());
|
||||||
assertEquals(jreResult.getHeight(), tmResult.getHeight(), "Incorrect height");
|
assertEquals("Incorrect height", jreResult.getHeight(), tmResult.getHeight());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
assertEquals(height, tmResult.getWidth(), "Incorrect width");
|
assertEquals("Incorrect width", height, tmResult.getWidth());
|
||||||
assertEquals(width, tmResult.getHeight(), "Incorrect height");
|
assertEquals("Incorrect height", width, tmResult.getHeight());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+42
-48
@@ -30,16 +30,18 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.image;
|
package com.twelvemonkeys.image;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Disabled;
|
import org.junit.Ignore;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.color.*;
|
import java.awt.color.ColorSpace;
|
||||||
import java.awt.image.*;
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.awt.image.ColorModel;
|
||||||
|
import java.awt.image.ImageProducer;
|
||||||
|
import java.awt.image.IndexColorModel;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.time.Duration;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BufferedImageFactoryTestCase
|
* BufferedImageFactoryTestCase
|
||||||
@@ -49,58 +51,50 @@ import static org.junit.jupiter.api.Assertions.*;
|
|||||||
* @version $Id: BufferedImageFactoryTestCase.java,v 1.0 May 7, 2010 12:40:08 PM haraldk Exp$
|
* @version $Id: BufferedImageFactoryTestCase.java,v 1.0 May 7, 2010 12:40:08 PM haraldk Exp$
|
||||||
*/
|
*/
|
||||||
public class BufferedImageFactoryTest {
|
public class BufferedImageFactoryTest {
|
||||||
@Test
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void testCreateNullImage() {
|
public void testCreateNullImage() {
|
||||||
assertThrows(IllegalArgumentException.class, () -> {
|
new BufferedImageFactory((Image) null);
|
||||||
new BufferedImageFactory((Image) null);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void testCreateNullProducer() {
|
public void testCreateNullProducer() {
|
||||||
assertThrows(IllegalArgumentException.class, () -> {
|
new BufferedImageFactory((ImageProducer) null);
|
||||||
new BufferedImageFactory((ImageProducer) null);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NPE in Toolkit, ok
|
// NPE in Toolkit, ok
|
||||||
@Test
|
@Test(expected = RuntimeException.class)
|
||||||
public void testGetBufferedImageErrorSourceByteArray() {
|
public void testGetBufferedImageErrorSourceByteArray() {
|
||||||
assertThrows(RuntimeException.class, () -> {
|
Image source = Toolkit.getDefaultToolkit().createImage((byte[]) null);
|
||||||
Image source = Toolkit.getDefaultToolkit().createImage((byte[]) null);
|
|
||||||
new BufferedImageFactory(source);
|
new BufferedImageFactory(source);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void testGetBufferedImageErrorSourceImageProducer() {
|
public void testGetBufferedImageErrorSourceImageProducer() {
|
||||||
Image source = Toolkit.getDefaultToolkit().createImage((ImageProducer) null);
|
Image source = Toolkit.getDefaultToolkit().createImage((ImageProducer) null);
|
||||||
assertThrows(IllegalArgumentException.class, () -> {
|
|
||||||
new BufferedImageFactory(source);
|
new BufferedImageFactory(source);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This is a quite serious bug, however, the bug is in the Toolkit, allowing such images in the first place...
|
// TODO: This is a quite serious bug, however, the bug is in the Toolkit, allowing such images in the first place...
|
||||||
// In any case, there's not much we can do, except until someone is bored and kills the app/thread... :-P
|
// In any case, there's not much we can do, except until someone is bored and kills the app/thread... :-P
|
||||||
@Disabled("Bug in Toolkit")
|
@Ignore("Bug in Toolkit")
|
||||||
@Test
|
@Test(timeout = 1000, expected = ImageConversionException.class)
|
||||||
public void testGetBufferedImageErrorSourceString() {
|
public void testGetBufferedImageErrorSourceString() {
|
||||||
assertTimeoutPreemptively(Duration.ofMillis(1000), () -> {
|
Image source = Toolkit.getDefaultToolkit().createImage((String) null);
|
||||||
Image source = Toolkit.getDefaultToolkit().createImage((String) null);
|
|
||||||
BufferedImageFactory factory = new BufferedImageFactory(source);
|
BufferedImageFactory factory = new BufferedImageFactory(source);
|
||||||
assertThrows(ImageConversionException.class, factory::getBufferedImage);
|
factory.getBufferedImage();
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a little random, and it would be nicer if we could throw an IllegalArgumentException on create.
|
// This is a little random, and it would be nicer if we could throw an IllegalArgumentException on create.
|
||||||
// Unfortunately, the API doesn't allow this...
|
// Unfortunately, the API doesn't allow this...
|
||||||
@Test
|
@Test(timeout = 1000, expected = ImageConversionException.class)
|
||||||
public void testGetBufferedImageErrorSourceURL() {
|
public void testGetBufferedImageErrorSourceURL() {
|
||||||
assertTimeoutPreemptively(Duration.ofMillis(1000), () -> {
|
Image source = Toolkit.getDefaultToolkit().createImage(getClass().getResource("/META-INF/MANIFEST.MF"));
|
||||||
Image source = Toolkit.getDefaultToolkit().createImage((String) null);
|
|
||||||
BufferedImageFactory factory = new BufferedImageFactory(source);
|
BufferedImageFactory factory = new BufferedImageFactory(source);
|
||||||
assertThrows(ImageConversionException.class, factory::getBufferedImage);
|
factory.getBufferedImage();
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -172,7 +166,7 @@ public class BufferedImageFactoryTest {
|
|||||||
|
|
||||||
assertEquals(3, colorModel.getNumColorComponents());
|
assertEquals(3, colorModel.getNumColorComponents());
|
||||||
assertEquals(ColorSpace.getInstance(ColorSpace.CS_sRGB), colorModel.getColorSpace());
|
assertEquals(ColorSpace.getInstance(ColorSpace.CS_sRGB), colorModel.getColorSpace());
|
||||||
assertInstanceOf(IndexColorModel.class, colorModel);
|
assertTrue(colorModel instanceof IndexColorModel);
|
||||||
|
|
||||||
assertTrue(colorModel.hasAlpha());
|
assertTrue(colorModel.hasAlpha());
|
||||||
assertEquals(4, colorModel.getNumComponents());
|
assertEquals(4, colorModel.getNumComponents());
|
||||||
@@ -203,7 +197,7 @@ public class BufferedImageFactoryTest {
|
|||||||
|
|
||||||
for (int y = 0; y < image.getHeight(); y++) {
|
for (int y = 0; y < image.getHeight(); y++) {
|
||||||
for (int x = 0; x < image.getWidth(); x++) {
|
for (int x = 0; x < image.getWidth(); x++) {
|
||||||
assertEquals(original.getRGB(x * 2, y * 2), image.getRGB(x, y), "RGB[" + x + ", " + y + "]");
|
assertEquals("RGB[" + x + ", " + y + "]", original.getRGB(x * 2, y * 2), image.getRGB(x, y));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -226,7 +220,7 @@ public class BufferedImageFactoryTest {
|
|||||||
|
|
||||||
for (int y = 0; y < image.getHeight(); y++) {
|
for (int y = 0; y < image.getHeight(); y++) {
|
||||||
for (int x = 0; x < image.getWidth(); x++) {
|
for (int x = 0; x < image.getWidth(); x++) {
|
||||||
assertEquals(original.getRGB(40 + x, 40 + y), image.getRGB(x, y), "RGB[" + x + ", " + y + "]");
|
assertEquals("RGB[" + x + ", " + y + "]", original.getRGB(40 + x, 40 + y), image.getRGB(x, y));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -250,7 +244,7 @@ public class BufferedImageFactoryTest {
|
|||||||
|
|
||||||
for (int y = 0; y < image.getHeight(); y++) {
|
for (int y = 0; y < image.getHeight(); y++) {
|
||||||
for (int x = 0; x < image.getWidth(); x++) {
|
for (int x = 0; x < image.getWidth(); x++) {
|
||||||
assertEquals(original.getRGB(40 + x * 2, 40 + y * 2), image.getRGB(x, y), "RGB[" + x + ", " + y + "]");
|
assertEquals("RGB[" + x + ", " + y + "]", original.getRGB(40 + x * 2, 40 + y * 2), image.getRGB(x, y));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -266,9 +260,9 @@ public class BufferedImageFactoryTest {
|
|||||||
|
|
||||||
// Listener should abort ASAP
|
// Listener should abort ASAP
|
||||||
factory.addProgressListener(new BufferedImageFactory.ProgressListener() {
|
factory.addProgressListener(new BufferedImageFactory.ProgressListener() {
|
||||||
public void progress(BufferedImageFactory factory, float percentage) {
|
public void progress(BufferedImageFactory pFactory, float pPercentage) {
|
||||||
if (percentage > 5) {
|
if (pPercentage > 5) {
|
||||||
factory.abort();
|
pFactory.abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -349,7 +343,7 @@ public class BufferedImageFactoryTest {
|
|||||||
VerifyingListener listener = new VerifyingListener(factory);
|
VerifyingListener listener = new VerifyingListener(factory);
|
||||||
factory.addProgressListener(listener);
|
factory.addProgressListener(listener);
|
||||||
factory.removeProgressListener(new BufferedImageFactory.ProgressListener() {
|
factory.removeProgressListener(new BufferedImageFactory.ProgressListener() {
|
||||||
public void progress(BufferedImageFactory factory, float percentage) {
|
public void progress(BufferedImageFactory pFactory, float pPercentage) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
factory.getBufferedImage();
|
factory.getBufferedImage();
|
||||||
@@ -386,11 +380,11 @@ public class BufferedImageFactoryTest {
|
|||||||
this.factory = factory;
|
this.factory = factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void progress(BufferedImageFactory factory, float percentage) {
|
public void progress(BufferedImageFactory pFactory, float pPercentage) {
|
||||||
assertEquals(this.factory, factory);
|
assertEquals(factory, pFactory);
|
||||||
assertTrue(percentage >= progress && percentage <= 100f);
|
assertTrue(pPercentage >= progress && pPercentage <= 100f);
|
||||||
|
|
||||||
progress = percentage;
|
progress = pPercentage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.image;
|
package com.twelvemonkeys.image;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
@@ -39,7 +39,7 @@ import java.awt.image.IndexColorModel;
|
|||||||
import java.awt.image.RenderedImage;
|
import java.awt.image.RenderedImage;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
public class ImageUtilTest {
|
public class ImageUtilTest {
|
||||||
|
|
||||||
@@ -116,8 +116,8 @@ public class ImageUtilTest {
|
|||||||
public void testImageIsNotBufferedImage() {
|
public void testImageIsNotBufferedImage() {
|
||||||
// Should not be a buffered image
|
// Should not be a buffered image
|
||||||
assertFalse(
|
assertFalse(
|
||||||
scaled instanceof BufferedImage,
|
"FOR SOME IMPLEMENTATIONS THIS MIGHT FAIL!\nIn that case, testToBufferedImage() will fail too.",
|
||||||
"FOR SOME IMPLEMENTATIONS THIS MIGHT FAIL!\nIn that case, testToBufferedImage() will fail too."
|
scaled instanceof BufferedImage
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,7 +247,7 @@ public class ImageUtilTest {
|
|||||||
if (original != notContrasted) { // Don't care to test if images are same
|
if (original != notContrasted) { // Don't care to test if images are same
|
||||||
for (int y = 0; y < original.getHeight(); y++) {
|
for (int y = 0; y < original.getHeight(); y++) {
|
||||||
for (int x = 0; x < original.getWidth(); x++) {
|
for (int x = 0; x < original.getWidth(); x++) {
|
||||||
assertEquals(original.getRGB(x, y), notContrasted.getRGB(x, y), "0 constrast should not change image");
|
assertEquals("0 constrast should not change image", original.getRGB(x, y), notContrasted.getRGB(x, y));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -275,24 +275,24 @@ public class ImageUtilTest {
|
|||||||
|
|
||||||
// RED
|
// RED
|
||||||
if (oR < 127) {
|
if (oR < 127) {
|
||||||
assertTrue(oR >= cR && cR >= dR, "Contrast should be decreased or same");
|
assertTrue("Contrast should be decreased or same", oR >= cR && cR >= dR);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
assertTrue(oR <= cR && cR <= dR, "Contrast should be increased or same");
|
assertTrue("Contrast should be increased or same", oR <= cR && cR <= dR);
|
||||||
}
|
}
|
||||||
// GREEN
|
// GREEN
|
||||||
if (oG < 127) {
|
if (oG < 127) {
|
||||||
assertTrue(oG >= cG && cG >= dG, "Contrast should be decreased or same");
|
assertTrue("Contrast should be decreased or same", oG >= cG && cG >= dG);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
assertTrue(oG <= cG && cG <= dG, "Contrast should be increased or same");
|
assertTrue("Contrast should be increased or same", oG <= cG && cG <= dG);
|
||||||
}
|
}
|
||||||
// BLUE
|
// BLUE
|
||||||
if (oB < 127) {
|
if (oB < 127) {
|
||||||
assertTrue(oB >= cB && cB >= dB, "Contrast should be decreased or same");
|
assertTrue("Contrast should be decreased or same", oB >= cB && cB >= dB);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
assertTrue(oB <= cB && cB <= dB, "Contrast should be increased or same");
|
assertTrue("Contrast should be increased or same", oB <= cB && cB <= dB);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -304,9 +304,9 @@ public class ImageUtilTest {
|
|||||||
int r = rgb >> 16 & 0xFF;
|
int r = rgb >> 16 & 0xFF;
|
||||||
int g = rgb >> 8 & 0xFF;
|
int g = rgb >> 8 & 0xFF;
|
||||||
int b = rgb & 0xFF;
|
int b = rgb & 0xFF;
|
||||||
assertTrue(r == 0 || r == 255, "Max contrast should only produce primary colors");
|
assertTrue("Max contrast should only produce primary colors", r == 0 || r == 255);
|
||||||
assertTrue(g == 0 || g == 255, "Max contrast should only produce primary colors");
|
assertTrue("Max contrast should only produce primary colors", g == 0 || g == 255);
|
||||||
assertTrue(b == 0 || b == 255, "Max contrast should only produce primary colors");
|
assertTrue("Max contrast should only produce primary colors", b == 0 || b == 255);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -327,24 +327,24 @@ public class ImageUtilTest {
|
|||||||
|
|
||||||
// RED
|
// RED
|
||||||
if (oR >= 127) {
|
if (oR >= 127) {
|
||||||
assertTrue(oR >= cR, "Contrast should be decreased or same");
|
assertTrue("Contrast should be decreased or same", oR >= cR);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
assertTrue(oR <= cR, "Contrast should be increased or same");
|
assertTrue("Contrast should be increased or same", oR <= cR);
|
||||||
}
|
}
|
||||||
// GREEN
|
// GREEN
|
||||||
if (oG >= 127) {
|
if (oG >= 127) {
|
||||||
assertTrue(oG >= cG, "Contrast should be decreased or same");
|
assertTrue("Contrast should be decreased or same", oG >= cG);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
assertTrue(oG <= cG, "Contrast should be increased or same");
|
assertTrue("Contrast should be increased or same", oG <= cG);
|
||||||
}
|
}
|
||||||
// BLUE
|
// BLUE
|
||||||
if (oB >= 127) {
|
if (oB >= 127) {
|
||||||
assertTrue(oB >= cB, "Contrast should be decreased or same");
|
assertTrue("Contrast should be decreased or same", oB >= cB);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
assertTrue(oB <= cB, "Contrast should be increased or same");
|
assertTrue("Contrast should be increased or same", oB <= cB);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -357,7 +357,7 @@ public class ImageUtilTest {
|
|||||||
int r = rgb >> 16 & 0xFF;
|
int r = rgb >> 16 & 0xFF;
|
||||||
int g = rgb >> 8 & 0xFF;
|
int g = rgb >> 8 & 0xFF;
|
||||||
int b = rgb & 0xFF;
|
int b = rgb & 0xFF;
|
||||||
assertTrue(r == 127 && g == 127 && b == 127, "Minimum contrast should be all gray");
|
assertTrue("Minimum contrast should be all gray", r == 127 && g == 127 && b == 127);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -400,7 +400,7 @@ public class ImageUtilTest {
|
|||||||
if (original != notSharpened) { // Don't care to test if images are same
|
if (original != notSharpened) { // Don't care to test if images are same
|
||||||
for (int y = 0; y < original.getHeight(); y++) {
|
for (int y = 0; y < original.getHeight(); y++) {
|
||||||
for (int x = 0; x < original.getWidth(); x++) {
|
for (int x = 0; x < original.getWidth(); x++) {
|
||||||
assertEquals(original.getRGB(x, y), notSharpened.getRGB(x, y), "0 sharpen should not change image");
|
assertEquals("0 sharpen should not change image", original.getRGB(x, y), notSharpened.getRGB(x, y));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -446,13 +446,13 @@ public class ImageUtilTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// assertEquals("Difference should not change", diffOriginal, diffSharpened);
|
// assertEquals("Difference should not change", diffOriginal, diffSharpened);
|
||||||
assertTrue(absDiffOriginal < absDiffSharpened, "Abs difference should increase");
|
assertTrue("Abs difference should increase", absDiffOriginal < absDiffSharpened);
|
||||||
// assertEquals("Difference should not change", diffOriginal, diffDefault);
|
// assertEquals("Difference should not change", diffOriginal, diffDefault);
|
||||||
assertTrue(absDiffOriginal < absDiffDefault, "Abs difference should increase");
|
assertTrue("Abs difference should increase", absDiffOriginal < absDiffDefault);
|
||||||
// assertEquals("Difference should not change", diffOriginal, diffMore);
|
// assertEquals("Difference should not change", diffOriginal, diffMore);
|
||||||
assertTrue(absDiffOriginal < absDiffMore, "Abs difference should increase");
|
assertTrue("Abs difference should increase", absDiffOriginal < absDiffMore);
|
||||||
// assertEquals("Difference should not change", diffSharpened, diffMore);
|
// assertEquals("Difference should not change", diffSharpened, diffMore);
|
||||||
assertTrue(absDiffSharpened < absDiffMore, "Abs difference should increase");
|
assertTrue("Abs difference should increase", absDiffSharpened < absDiffMore);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -466,7 +466,7 @@ public class ImageUtilTest {
|
|||||||
if (original != notBlurred) { // Don't care to test if images are same
|
if (original != notBlurred) { // Don't care to test if images are same
|
||||||
for (int y = 0; y < original.getHeight(); y++) {
|
for (int y = 0; y < original.getHeight(); y++) {
|
||||||
for (int x = 0; x < original.getWidth(); x++) {
|
for (int x = 0; x < original.getWidth(); x++) {
|
||||||
assertEquals(original.getRGB(x, y), notBlurred.getRGB(x, y), "0 blur should not change image");
|
assertEquals("0 blur should not change image", original.getRGB(x, y), notBlurred.getRGB(x, y));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -512,13 +512,13 @@ public class ImageUtilTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// assertEquals("Difference should not change", diffOriginal, diffBlurred);
|
// assertEquals("Difference should not change", diffOriginal, diffBlurred);
|
||||||
assertTrue(absDiffOriginal > absDiffBlurred, String.format("Abs difference should decrease: %s <= %s", absDiffOriginal, absDiffBlurred));
|
assertTrue(String.format("Abs difference should decrease: %s <= %s", absDiffOriginal, absDiffBlurred), absDiffOriginal > absDiffBlurred);
|
||||||
// assertEquals("Difference should not change", diffOriginal, diffDefault);
|
// assertEquals("Difference should not change", diffOriginal, diffDefault);
|
||||||
assertTrue(absDiffOriginal > absDiffDefault, "Abs difference should decrease");
|
assertTrue("Abs difference should decrease", absDiffOriginal > absDiffDefault);
|
||||||
// assertEquals("Difference should not change", diffOriginal, diffMore);
|
// assertEquals("Difference should not change", diffOriginal, diffMore);
|
||||||
assertTrue(absDiffOriginal > absDiffMore, "Abs difference should decrease");
|
assertTrue("Abs difference should decrease", absDiffOriginal > absDiffMore);
|
||||||
// assertEquals("Difference should not change", diffBlurred, diffMore);
|
// assertEquals("Difference should not change", diffBlurred, diffMore);
|
||||||
assertTrue(absDiffBlurred > absDiffMore, "Abs difference should decrease");
|
assertTrue("Abs difference should decrease", absDiffBlurred > absDiffMore);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -528,7 +528,7 @@ public class ImageUtilTest {
|
|||||||
assertNotNull(sunflower);
|
assertNotNull(sunflower);
|
||||||
|
|
||||||
BufferedImage image = ImageUtil.createIndexed(sunflower);
|
BufferedImage image = ImageUtil.createIndexed(sunflower);
|
||||||
assertNotNull(image, "Image was null");
|
assertNotNull("Image was null", image);
|
||||||
assertInstanceOf(IndexColorModel.class, image.getColorModel());
|
assertTrue(image.getColorModel() instanceof IndexColorModel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,8 +30,8 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.image;
|
package com.twelvemonkeys.image;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Disabled;
|
import org.junit.Ignore;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
@@ -40,7 +40,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ResampleOpTestCase
|
* ResampleOpTestCase
|
||||||
@@ -124,7 +124,7 @@ public class ResampleOpTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assertEquals(Collections.EMPTY_LIST, exceptions, "Filter threw exceptions: ");
|
assertEquals("Filter threw exceptions: ", Collections.EMPTY_LIST, exceptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1x1
|
// 1x1
|
||||||
@@ -358,7 +358,7 @@ public class ResampleOpTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Disabled("Not for general unit testing")
|
@Ignore("Not for general unit testing")
|
||||||
@Test
|
@Test
|
||||||
public void testTime() {
|
public void testTime() {
|
||||||
int iterations = 1000;
|
int iterations = 1000;
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
TODO:
|
||||||
|
Remove compile-time dependency on JMagick:
|
||||||
|
- Extract interface for MagickAccelerator
|
||||||
|
- Move implementation to separate module
|
||||||
|
- Instantiate impl via reflection
|
||||||
|
DONE:
|
||||||
@@ -4,19 +4,15 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.common</groupId>
|
<groupId>com.twelvemonkeys.common</groupId>
|
||||||
<artifactId>common</artifactId>
|
<artifactId>common</artifactId>
|
||||||
<version>3.13.2-SNAPSHOT</version>
|
<version>3.4.1</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>common-io</artifactId>
|
<artifactId>common-io</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
<name>TwelveMonkeys » Common » IO</name>
|
<name>TwelveMonkeys :: Common :: IO</name>
|
||||||
<description>
|
<description>
|
||||||
TwelveMonkeys Common I/O support classes.
|
The TwelveMonkeys Common IO support
|
||||||
</description>
|
</description>
|
||||||
|
|
||||||
<properties>
|
|
||||||
<project.jpms.module.name>com.twelvemonkeys.common.io</project.jpms.module.name>
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>${project.groupId}</groupId>
|
<groupId>${project.groupId}</groupId>
|
||||||
@@ -31,12 +27,4 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.felix</groupId>
|
|
||||||
<artifactId>maven-bundle-plugin</artifactId>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
+7
-13
@@ -186,7 +186,7 @@ abstract class AbstractCachedSeekableStream extends SeekableInputStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void closeImpl() throws IOException {
|
protected void closeImpl() throws IOException {
|
||||||
cache.close();
|
cache.flush(position);
|
||||||
cache = null;
|
cache = null;
|
||||||
stream.close();
|
stream.close();
|
||||||
}
|
}
|
||||||
@@ -198,12 +198,12 @@ abstract class AbstractCachedSeekableStream extends SeekableInputStream {
|
|||||||
* @author last modified by $Author: haku $
|
* @author last modified by $Author: haku $
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/AbstractCachedSeekableStream.java#2 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/AbstractCachedSeekableStream.java#2 $
|
||||||
*/
|
*/
|
||||||
static abstract class StreamCache {
|
public static abstract class StreamCache {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@code StreamCache}.
|
* Creates a {@code StreamCache}.
|
||||||
*/
|
*/
|
||||||
StreamCache() {
|
protected StreamCache() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -218,10 +218,9 @@ abstract class AbstractCachedSeekableStream extends SeekableInputStream {
|
|||||||
/**
|
/**
|
||||||
* Writes a series of bytes at the current read/write position. The read/write position will be increased by
|
* Writes a series of bytes at the current read/write position. The read/write position will be increased by
|
||||||
* {@code pLength}.
|
* {@code pLength}.
|
||||||
* <p>
|
* <p/>
|
||||||
* This implementation invokes {@link #write(int)} {@code pLength} times.
|
* This implementation invokes {@link #write(int)} {@code pLength} times.
|
||||||
* Subclasses may override this method for performance.
|
* Subclasses may override this method for performance.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @param pBuffer the bytes to write.
|
* @param pBuffer the bytes to write.
|
||||||
* @param pOffset the starting offset into the buffer.
|
* @param pOffset the starting offset into the buffer.
|
||||||
@@ -247,10 +246,9 @@ abstract class AbstractCachedSeekableStream extends SeekableInputStream {
|
|||||||
/**
|
/**
|
||||||
* Writes a series of bytes at the current read/write position. The read/write position will be increased by
|
* Writes a series of bytes at the current read/write position. The read/write position will be increased by
|
||||||
* {@code pLength}.
|
* {@code pLength}.
|
||||||
* <p>
|
* <p/>
|
||||||
* This implementation invokes {@link #read()} {@code pLength} times.
|
* This implementation invokes {@link #read()} {@code pLength} times.
|
||||||
* Subclasses may override this method for performance.
|
* Subclasses may override this method for performance.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @param pBuffer the bytes to write
|
* @param pBuffer the bytes to write
|
||||||
* @param pOffset the starting offset into the buffer.
|
* @param pOffset the starting offset into the buffer.
|
||||||
@@ -285,14 +283,12 @@ abstract class AbstractCachedSeekableStream extends SeekableInputStream {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Optionally flushes any data prior to the given position.
|
* Optionally flushes any data prior to the given position.
|
||||||
* <p>
|
* <p/>
|
||||||
* Attempting to perform a seek operation, and/or a read or write operation to a position equal to or before
|
* Attempting to perform a seek operation, and/or a read or write operation to a position equal to or before
|
||||||
* the flushed position may result in exceptions or undefined behaviour.
|
* the flushed position may result in exceptions or undefined behaviour.
|
||||||
* </p>
|
* <p/>
|
||||||
* <p>
|
|
||||||
* Subclasses should override this method for performance reasons, to avoid holding on to unnecessary resources.
|
* Subclasses should override this method for performance reasons, to avoid holding on to unnecessary resources.
|
||||||
* This implementation does nothing.
|
* This implementation does nothing.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @param pPosition the last position to flush.
|
* @param pPosition the last position to flush.
|
||||||
*/
|
*/
|
||||||
@@ -307,7 +303,5 @@ abstract class AbstractCachedSeekableStream extends SeekableInputStream {
|
|||||||
* @throws IOException if the position can't be determined because of a problem in the cache backing mechanism.
|
* @throws IOException if the position can't be determined because of a problem in the cache backing mechanism.
|
||||||
*/
|
*/
|
||||||
abstract long getPosition() throws IOException;
|
abstract long getPosition() throws IOException;
|
||||||
|
|
||||||
abstract void close() throws IOException;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import java.util.List;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A Reader implementation that can read from multiple sources.
|
* A Reader implementation that can read from multiple sources.
|
||||||
|
* <p/>
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @author last modified by $Author: haku $
|
* @author last modified by $Author: haku $
|
||||||
@@ -56,8 +57,8 @@ public class CompoundReader extends Reader {
|
|||||||
|
|
||||||
private int currentReader;
|
private int currentReader;
|
||||||
private int markedReader;
|
private int markedReader;
|
||||||
private long mark;
|
private int mark;
|
||||||
private long next;
|
private int mNext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new compound reader.
|
* Create a new compound reader.
|
||||||
@@ -76,7 +77,7 @@ public class CompoundReader extends Reader {
|
|||||||
finalLock = pReaders; // NOTE: It's ok to sync on pReaders, as the
|
finalLock = pReaders; // NOTE: It's ok to sync on pReaders, as the
|
||||||
// reference can't change, only it's elements
|
// reference can't change, only it's elements
|
||||||
|
|
||||||
readers = new ArrayList<>();
|
readers = new ArrayList<Reader>();
|
||||||
|
|
||||||
boolean markSupported = true;
|
boolean markSupported = true;
|
||||||
while (pReaders.hasNext()) {
|
while (pReaders.hasNext()) {
|
||||||
@@ -101,7 +102,7 @@ public class CompoundReader extends Reader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Reset mNext for every reader, and record marked reader in mark/reset methods!
|
// NOTE: Reset mNext for every reader, and record marked reader in mark/reset methods!
|
||||||
next = 0;
|
mNext = 0;
|
||||||
return current;
|
return current;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,7 +136,7 @@ public class CompoundReader extends Reader {
|
|||||||
|
|
||||||
synchronized (finalLock) {
|
synchronized (finalLock) {
|
||||||
ensureOpen();
|
ensureOpen();
|
||||||
mark = next;
|
mark = mNext;
|
||||||
markedReader = currentReader;
|
markedReader = currentReader;
|
||||||
|
|
||||||
current.mark(pReadLimit);
|
current.mark(pReadLimit);
|
||||||
@@ -158,7 +159,7 @@ public class CompoundReader extends Reader {
|
|||||||
}
|
}
|
||||||
current.reset();
|
current.reset();
|
||||||
|
|
||||||
next = mark;
|
mNext = mark;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,13 +178,13 @@ public class CompoundReader extends Reader {
|
|||||||
return read(); // In case of 0-length readers
|
return read(); // In case of 0-length readers
|
||||||
}
|
}
|
||||||
|
|
||||||
next++;
|
mNext++;
|
||||||
|
|
||||||
return read;
|
return read;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int read(char[] pBuffer, int pOffset, int pLength) throws IOException {
|
public int read(char pBuffer[], int pOffset, int pLength) throws IOException {
|
||||||
synchronized (finalLock) {
|
synchronized (finalLock) {
|
||||||
int read = current.read(pBuffer, pOffset, pLength);
|
int read = current.read(pBuffer, pOffset, pLength);
|
||||||
|
|
||||||
@@ -192,7 +193,7 @@ public class CompoundReader extends Reader {
|
|||||||
return read(pBuffer, pOffset, pLength); // In case of 0-length readers
|
return read(pBuffer, pOffset, pLength); // In case of 0-length readers
|
||||||
}
|
}
|
||||||
|
|
||||||
next += read;
|
mNext += read;
|
||||||
|
|
||||||
return read;
|
return read;
|
||||||
}
|
}
|
||||||
@@ -213,7 +214,7 @@ public class CompoundReader extends Reader {
|
|||||||
return skip(pChars); // In case of 0-length readers
|
return skip(pChars); // In case of 0-length readers
|
||||||
}
|
}
|
||||||
|
|
||||||
next += skipped;
|
mNext += skipped;
|
||||||
|
|
||||||
return skipped;
|
return skipped;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import java.io.StringReader;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* EmptyReader
|
* EmptyReader
|
||||||
|
* <p/>
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @author last modified by $Author: haku $
|
* @author last modified by $Author: haku $
|
||||||
|
|||||||
@@ -34,17 +34,20 @@ import java.io.ByteArrayInputStream;
|
|||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An unsynchronized {@code ByteArrayOutputStream} implementation. This version
|
* An unsynchronized {@code ByteArrayOutputStream} implementation. This version
|
||||||
* also has a constructor that lets you create a stream with initial content.
|
* also has a constructor that lets you create a stream with initial content.
|
||||||
|
* <p/>
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @version $Id: FastByteArrayOutputStream.java#2 $
|
* @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 {
|
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
|
* Creates a {@code ByteArrayOutputStream} with the given initial buffer
|
||||||
* size.
|
* size.
|
||||||
@@ -57,9 +60,8 @@ public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@code ByteArrayOutputStream} with the given initial content.
|
* Creates a {@code ByteArrayOutputStream} with the given initial content.
|
||||||
* <p>
|
* <p/>
|
||||||
* Note that the buffer is not cloned, for maximum performance.
|
* Note that the buffer is not cloned, for maximum performance.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @param pBuffer initial buffer
|
* @param pBuffer initial buffer
|
||||||
*/
|
*/
|
||||||
@@ -70,7 +72,7 @@ public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(byte[] pBytes, int pOffset, int pLength) {
|
public void write(byte pBytes[], int pOffset, int pLength) {
|
||||||
if ((pOffset < 0) || (pOffset > pBytes.length) || (pLength < 0) ||
|
if ((pOffset < 0) || (pOffset > pBytes.length) || (pLength < 0) ||
|
||||||
((pOffset + pLength) > pBytes.length) || ((pOffset + pLength) < 0)) {
|
((pOffset + pLength) > pBytes.length) || ((pOffset + pLength) < 0)) {
|
||||||
throw new IndexOutOfBoundsException();
|
throw new IndexOutOfBoundsException();
|
||||||
@@ -95,8 +97,10 @@ public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
|
|||||||
|
|
||||||
private void growIfNeeded(int pNewCount) {
|
private void growIfNeeded(int pNewCount) {
|
||||||
if (pNewCount > buf.length) {
|
if (pNewCount > buf.length) {
|
||||||
int newSize = Math.max(buf.length << 1, pNewCount);
|
int newSize = Math.max(Math.min(buf.length << 1, buf.length + maxGrowSize), pNewCount);
|
||||||
buf = Arrays.copyOf(buf, newSize);
|
byte newBuf[] = new byte[newSize];
|
||||||
|
System.arraycopy(buf, 0, newBuf, 0, count);
|
||||||
|
buf = newBuf;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,18 +113,20 @@ public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
|
|||||||
// Non-synchronized version of toByteArray
|
// Non-synchronized version of toByteArray
|
||||||
@Override
|
@Override
|
||||||
public byte[] toByteArray() {
|
public byte[] toByteArray() {
|
||||||
return Arrays.copyOf(buf, count);
|
byte newBuf[] = new byte[count];
|
||||||
|
System.arraycopy(buf, 0, newBuf, 0, count);
|
||||||
|
|
||||||
|
return newBuf;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@code ByteArrayInputStream} that reads directly from this
|
* Creates a {@code ByteArrayInputStream} that reads directly from this
|
||||||
* {@code FastByteArrayOutputStream}'s byte buffer.
|
* {@code FastByteArrayOutputStream}'s byte buffer.
|
||||||
* The buffer is not cloned, for maximum performance.
|
* The buffer is not cloned, for maximum performance.
|
||||||
* <p>
|
* <p/>
|
||||||
* Note that care needs to be taken to avoid writes to
|
* Note that care needs to be taken to avoid writes to
|
||||||
* this output stream after the input stream is created.
|
* this output stream after the input stream is created.
|
||||||
* Failing to do so, may result in unpredictable behaviour.
|
* Failing to do so, may result in unpredictable behaviour.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @return a new {@code ByteArrayInputStream}, reading from this stream's buffer.
|
* @return a new {@code ByteArrayInputStream}, reading from this stream's buffer.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -36,9 +36,8 @@ import java.io.*;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@code SeekableInputStream} implementation that caches data in a temporary {@code File}.
|
* A {@code SeekableInputStream} implementation that caches data in a temporary {@code File}.
|
||||||
* <p>
|
* <p/>
|
||||||
* Temporary files are created as specified in {@link File#createTempFile(String, String, java.io.File)}.
|
* Temporary files are created as specified in {@link File#createTempFile(String, String, java.io.File)}.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @see MemoryCacheSeekableStream
|
* @see MemoryCacheSeekableStream
|
||||||
* @see FileSeekableStream
|
* @see FileSeekableStream
|
||||||
@@ -125,9 +124,7 @@ public final class FileCacheSeekableStream extends AbstractCachedSeekableStream
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void closeImpl() throws IOException {
|
protected void closeImpl() throws IOException {
|
||||||
// TODO: Close cache file
|
|
||||||
super.closeImpl();
|
super.closeImpl();
|
||||||
|
|
||||||
buffer = null;
|
buffer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,45 +194,39 @@ public final class FileCacheSeekableStream extends AbstractCachedSeekableStream
|
|||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: We need to close the cache file!!! Otherwise we are leaking file descriptors
|
|
||||||
|
|
||||||
final static class FileCache extends StreamCache {
|
final static class FileCache extends StreamCache {
|
||||||
private RandomAccessFile cacheFile;
|
private RandomAccessFile mCacheFile;
|
||||||
|
|
||||||
public FileCache(final File pFile) throws FileNotFoundException {
|
public FileCache(final File pFile) throws FileNotFoundException {
|
||||||
Validate.notNull(pFile, "file");
|
Validate.notNull(pFile, "file");
|
||||||
cacheFile = new RandomAccessFile(pFile, "rw");
|
mCacheFile = new RandomAccessFile(pFile, "rw");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void write(final int pByte) throws IOException {
|
public void write(final int pByte) throws IOException {
|
||||||
cacheFile.write(pByte);
|
mCacheFile.write(pByte);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(final byte[] pBuffer, final int pOffset, final int pLength) throws IOException {
|
public void write(final byte[] pBuffer, final int pOffset, final int pLength) throws IOException {
|
||||||
cacheFile.write(pBuffer, pOffset, pLength);
|
mCacheFile.write(pBuffer, pOffset, pLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int read() throws IOException {
|
public int read() throws IOException {
|
||||||
return cacheFile.read();
|
return mCacheFile.read();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int read(final byte[] pBuffer, final int pOffset, final int pLength) throws IOException {
|
public int read(final byte[] pBuffer, final int pOffset, final int pLength) throws IOException {
|
||||||
return cacheFile.read(pBuffer, pOffset, pLength);
|
return mCacheFile.read(pBuffer, pOffset, pLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void seek(final long pPosition) throws IOException {
|
public void seek(final long pPosition) throws IOException {
|
||||||
cacheFile.seek(pPosition);
|
mCacheFile.seek(pPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getPosition() throws IOException {
|
public long getPosition() throws IOException {
|
||||||
return cacheFile.getFilePointer();
|
return mCacheFile.getFilePointer();
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void close() throws IOException {
|
|
||||||
cacheFile.close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ import java.io.RandomAccessFile;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@code SeekableInputStream} implementation that uses random access directly to a {@code File}.
|
* A {@code SeekableInputStream} implementation that uses random access directly to a {@code File}.
|
||||||
|
* <p/>
|
||||||
* @see FileCacheSeekableStream
|
* @see FileCacheSeekableStream
|
||||||
* @see MemoryCacheSeekableStream
|
* @see MemoryCacheSeekableStream
|
||||||
* @see RandomAccessFile
|
* @see RandomAccessFile
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import java.io.InputStreamReader;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* FileSystem
|
* FileSystem
|
||||||
|
* <p/>
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @version $Id: FileSystem.java#1 $
|
* @version $Id: FileSystem.java#1 $
|
||||||
|
|||||||
@@ -171,6 +171,7 @@ public final class FileUtil {
|
|||||||
* {@code false}. In all other cases, an
|
* {@code false}. In all other cases, an
|
||||||
* {@code IOExceptio}n is thrown, and the method does not return.
|
* {@code IOExceptio}n is thrown, and the method does not return.
|
||||||
* @throws IOException if an i/o error occurs during copy
|
* @throws IOException if an i/o error occurs during copy
|
||||||
|
* @todo Test copyDir functionality!
|
||||||
*/
|
*/
|
||||||
public static boolean copy(File pFromFile, File pToFile, boolean pOverWrite) throws IOException {
|
public static boolean copy(File pFromFile, File pToFile, boolean pOverWrite) throws IOException {
|
||||||
// Copy all directory structure
|
// Copy all directory structure
|
||||||
@@ -346,7 +347,7 @@ public final class FileUtil {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the file (type) extension of the given file.
|
* Gets the file (type) extension of the given file.
|
||||||
* A file extension is the part of the filename, after the last occurrence
|
* A file extension is the part of the filename, after the last occurence
|
||||||
* of a period {@code '.'}.
|
* of a period {@code '.'}.
|
||||||
* If the filename contains no period, {@code null} is returned.
|
* If the filename contains no period, {@code null} is returned.
|
||||||
*
|
*
|
||||||
@@ -922,7 +923,7 @@ public final class FileUtil {
|
|||||||
// A URL should never be able to represent an opaque URI, test anyway
|
// A URL should never be able to represent an opaque URI, test anyway
|
||||||
throw new IllegalArgumentException("URI is not hierarchical");
|
throw new IllegalArgumentException("URI is not hierarchical");
|
||||||
}
|
}
|
||||||
if (path.isEmpty()) {
|
if (path.equals("")) {
|
||||||
throw new IllegalArgumentException("URI path component is empty");
|
throw new IllegalArgumentException("URI path component is empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,23 +41,18 @@ import java.io.FilenameFilter;
|
|||||||
* The mask is given as a well-known DOS filename format, with '*' and '?' as
|
* The mask is given as a well-known DOS filename format, with '*' and '?' as
|
||||||
* wildcards.
|
* wildcards.
|
||||||
* All other characters counts as ordinary characters.
|
* All other characters counts as ordinary characters.
|
||||||
* <p>
|
* <p/>
|
||||||
* The file name masks are used as a filter input and is given to the class via
|
* The file name masks are used as a filter input and is given to the class via
|
||||||
* the string array property:
|
* the string array property:<br>
|
||||||
* </p>
|
* <dd>{@code filenameMasksForInclusion} - Filename mask for exclusion of
|
||||||
* <dl>
|
* files (default if both properties are defined)
|
||||||
* <dt>{@code filenameMasksForInclusion}</dt>
|
* <dd>{@code filenameMasksForExclusion} - Filename mask for exclusion of
|
||||||
* <dd>Filename mask for exclusion of
|
* files.
|
||||||
* files (default if both properties are defined).</dd>
|
* <p/>
|
||||||
* <dt>{@code filenameMasksForExclusion}</dt>
|
|
||||||
* <dd>Filename mask for exclusion of files.</dd>
|
|
||||||
* </dl>
|
|
||||||
* <p>
|
|
||||||
* A recommended way of doing this is by referencing to the component which uses
|
* A recommended way of doing this is by referencing to the component which uses
|
||||||
* this class for file listing. In this way all properties are set in the same
|
* this class for file listing. In this way all properties are set in the same
|
||||||
* component and this utility component is kept in the background with only
|
* component and this utility component is kept in the background with only
|
||||||
* initial configuration necessary.
|
* initial configuration necessary.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:eirik.torske@iconmedialab.no">Eirik Torske</a>
|
* @author <a href="mailto:eirik.torske@iconmedialab.no">Eirik Torske</a>
|
||||||
* @see File#list(java.io.FilenameFilter) java.io.File.list
|
* @see File#list(java.io.FilenameFilter) java.io.File.list
|
||||||
@@ -65,7 +60,6 @@ import java.io.FilenameFilter;
|
|||||||
* @see WildcardStringParser
|
* @see WildcardStringParser
|
||||||
* @deprecated
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
|
||||||
public class FilenameMaskFilter implements FilenameFilter {
|
public class FilenameMaskFilter implements FilenameFilter {
|
||||||
|
|
||||||
// TODO: Rewrite to use regexp, or create new class
|
// TODO: Rewrite to use regexp, or create new class
|
||||||
|
|||||||
+16
-13
@@ -48,19 +48,18 @@ import java.io.*;
|
|||||||
* A little endian input stream reads two's complement,
|
* A little endian input stream reads two's complement,
|
||||||
* little endian integers, floating point numbers, and characters
|
* little endian integers, floating point numbers, and characters
|
||||||
* and returns them as Java primitive types.
|
* and returns them as Java primitive types.
|
||||||
* <p>
|
* <p/>
|
||||||
* The standard {@code java.io.DataInputStream} class
|
* The standard {@code java.io.DataInputStream} class
|
||||||
* which this class imitates reads big endian quantities.
|
* which this class imitates reads big endian quantities.
|
||||||
* </p>
|
* <p/>
|
||||||
* <p>
|
|
||||||
* <em>Warning:
|
* <em>Warning:
|
||||||
|
* <!-- Beware of little indians! -->
|
||||||
* The {@code DataInput} and {@code DataOutput} interfaces
|
* The {@code DataInput} and {@code DataOutput} interfaces
|
||||||
* specifies big endian byte order in their documentation.
|
* specifies big endian byte order in their documentation.
|
||||||
* This means that this class is, strictly speaking, not a proper
|
* This means that this class is, strictly speaking, not a proper
|
||||||
* implementation. However, I don't see a reason for the these interfaces to
|
* implementation. However, I don't see a reason for the these interfaces to
|
||||||
* specify the byte order of their underlying representations.
|
* specify the byte order of their underlying representations.
|
||||||
* </em>
|
* </em>
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @see com.twelvemonkeys.io.LittleEndianRandomAccessFile
|
* @see com.twelvemonkeys.io.LittleEndianRandomAccessFile
|
||||||
* @see java.io.DataInputStream
|
* @see java.io.DataInputStream
|
||||||
@@ -382,10 +381,12 @@ public class LittleEndianDataInputStream extends FilterInputStream implements Da
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See the general contract of the {@code readFully} method of {@code DataInput}.
|
* See the general contract of the {@code readFully}
|
||||||
* <p>
|
* method of {@code DataInput}.
|
||||||
* Bytes for this operation are read from the contained input stream.
|
* <p/>
|
||||||
* </p>
|
* Bytes
|
||||||
|
* for this operation are read from the contained
|
||||||
|
* input stream.
|
||||||
*
|
*
|
||||||
* @param pBytes the buffer into which the data is read.
|
* @param pBytes the buffer into which the data is read.
|
||||||
* @throws EOFException if this input stream reaches the end before
|
* @throws EOFException if this input stream reaches the end before
|
||||||
@@ -398,10 +399,12 @@ public class LittleEndianDataInputStream extends FilterInputStream implements Da
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See the general contract of the {@code readFully} method of {@code DataInput}.
|
* See the general contract of the {@code readFully}
|
||||||
* <p>
|
* method of {@code DataInput}.
|
||||||
* Bytes for this operation are read from the contained input stream.
|
* <p/>
|
||||||
* </p>
|
* Bytes
|
||||||
|
* for this operation are read from the contained
|
||||||
|
* input stream.
|
||||||
*
|
*
|
||||||
* @param pBytes the buffer into which the data is read.
|
* @param pBytes the buffer into which the data is read.
|
||||||
* @param pOffset the start offset of the data.
|
* @param pOffset the start offset of the data.
|
||||||
@@ -441,8 +444,8 @@ public class LittleEndianDataInputStream extends FilterInputStream implements Da
|
|||||||
* @exception IOException if an I/O error occurs.
|
* @exception IOException if an I/O error occurs.
|
||||||
* @see java.io.BufferedReader#readLine()
|
* @see java.io.BufferedReader#readLine()
|
||||||
* @see java.io.DataInputStream#readLine()
|
* @see java.io.DataInputStream#readLine()
|
||||||
|
* @noinspection deprecation
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
|
||||||
public String readLine() throws IOException {
|
public String readLine() throws IOException {
|
||||||
DataInputStream ds = new DataInputStream(in);
|
DataInputStream ds = new DataInputStream(in);
|
||||||
return ds.readLine();
|
return ds.readLine();
|
||||||
|
|||||||
+3
-4
@@ -47,19 +47,18 @@ import java.io.*;
|
|||||||
/**
|
/**
|
||||||
* A little endian output stream writes primitive Java numbers
|
* A little endian output stream writes primitive Java numbers
|
||||||
* and characters to an output stream in a little endian format.
|
* and characters to an output stream in a little endian format.
|
||||||
* <p>
|
* <p/>
|
||||||
* The standard {@code java.io.DataOutputStream} class which this class
|
* The standard {@code java.io.DataOutputStream} class which this class
|
||||||
* imitates uses big endian integers.
|
* imitates uses big endian integers.
|
||||||
* </p>
|
* <p/>
|
||||||
* <p>
|
|
||||||
* <em>Warning:
|
* <em>Warning:
|
||||||
|
* <!-- Beware of little indians! -->
|
||||||
* The {@code DataInput} and {@code DataOutput} interfaces
|
* The {@code DataInput} and {@code DataOutput} interfaces
|
||||||
* specifies big endian byte order in their documentation.
|
* specifies big endian byte order in their documentation.
|
||||||
* This means that this class is, strictly speaking, not a proper
|
* This means that this class is, strictly speaking, not a proper
|
||||||
* implementation. However, I don't see a reason for the these interfaces to
|
* implementation. However, I don't see a reason for the these interfaces to
|
||||||
* specify the byte order of their underlying representations.
|
* specify the byte order of their underlying representations.
|
||||||
* </em>
|
* </em>
|
||||||
* <p>
|
|
||||||
*
|
*
|
||||||
* @see com.twelvemonkeys.io.LittleEndianRandomAccessFile
|
* @see com.twelvemonkeys.io.LittleEndianRandomAccessFile
|
||||||
* @see java.io.DataOutputStream
|
* @see java.io.DataOutputStream
|
||||||
|
|||||||
+2
-2
@@ -36,15 +36,15 @@ import java.nio.channels.FileChannel;
|
|||||||
/**
|
/**
|
||||||
* A replacement for {@link java.io.RandomAccessFile} that is capable of reading
|
* A replacement for {@link java.io.RandomAccessFile} that is capable of reading
|
||||||
* and writing data in little endian byte order.
|
* and writing data in little endian byte order.
|
||||||
* <p>
|
* <p/>
|
||||||
* <em>Warning:
|
* <em>Warning:
|
||||||
|
* <!-- Beware of little indians! -->
|
||||||
* The {@code DataInput} and {@code DataOutput} interfaces
|
* The {@code DataInput} and {@code DataOutput} interfaces
|
||||||
* specifies big endian byte order in their documentation.
|
* specifies big endian byte order in their documentation.
|
||||||
* This means that this class is, strictly speaking, not a proper
|
* This means that this class is, strictly speaking, not a proper
|
||||||
* implementation. However, I don't see a reason for the these interfaces to
|
* implementation. However, I don't see a reason for the these interfaces to
|
||||||
* specify the byte order of their underlying representations.
|
* specify the byte order of their underlying representations.
|
||||||
* </em>
|
* </em>
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @see com.twelvemonkeys.io.LittleEndianDataInputStream
|
* @see com.twelvemonkeys.io.LittleEndianDataInputStream
|
||||||
* @see com.twelvemonkeys.io.LittleEndianDataOutputStream
|
* @see com.twelvemonkeys.io.LittleEndianDataOutputStream
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import java.util.List;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@code SeekableInputStream} implementation that caches data in memory.
|
* A {@code SeekableInputStream} implementation that caches data in memory.
|
||||||
|
* <p/>
|
||||||
*
|
*
|
||||||
* @see FileCacheSeekableStream
|
* @see FileCacheSeekableStream
|
||||||
*
|
*
|
||||||
@@ -66,7 +67,7 @@ public final class MemoryCacheSeekableStream extends AbstractCachedSeekableStrea
|
|||||||
final static class MemoryCache extends StreamCache {
|
final static class MemoryCache extends StreamCache {
|
||||||
final static int BLOCK_SIZE = 1 << 13;
|
final static int BLOCK_SIZE = 1 << 13;
|
||||||
|
|
||||||
private final List<byte[]> cache = new ArrayList<>();
|
private final List<byte[]> cache = new ArrayList<byte[]>();
|
||||||
private long length;
|
private long length;
|
||||||
private long position;
|
private long position;
|
||||||
private long start;
|
private long start;
|
||||||
@@ -185,11 +186,6 @@ public final class MemoryCacheSeekableStream extends AbstractCachedSeekableStrea
|
|||||||
start = pPosition;
|
start = pPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
void close() throws IOException {
|
|
||||||
cache.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getPosition() {
|
public long getPosition() {
|
||||||
return position;
|
return position;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import java.io.InputStream;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@code InputStream} that contains no bytes.
|
* An {@code InputStream} that contains no bytes.
|
||||||
|
* <p/>
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/NullInputStream.java#2 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/NullInputStream.java#2 $
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import java.io.OutputStream;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@code OutputStream} implementation that works as a sink.
|
* An {@code OutputStream} implementation that works as a sink.
|
||||||
|
* <p/>
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/NullOutputStream.java#2 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/NullOutputStream.java#2 $
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import java.io.IOException;
|
|||||||
/**
|
/**
|
||||||
* A data stream that is both readable and writable, much like a
|
* A data stream that is both readable and writable, much like a
|
||||||
* {@code RandomAccessFile}, except it may be backed by something other than a file.
|
* {@code RandomAccessFile}, except it may be backed by something other than a file.
|
||||||
|
* <p/>
|
||||||
*
|
*
|
||||||
* @see java.io.RandomAccessFile
|
* @see java.io.RandomAccessFile
|
||||||
*
|
*
|
||||||
@@ -113,9 +114,8 @@ public abstract class RandomAccessStream implements Seekable, DataInput, DataOut
|
|||||||
/**
|
/**
|
||||||
* Returns an input view of this {@code RandomAccessStream}.
|
* Returns an input view of this {@code RandomAccessStream}.
|
||||||
* Invoking this method several times, will return the same object.
|
* Invoking this method several times, will return the same object.
|
||||||
* <p>
|
* <p/>
|
||||||
* <em>Note that read access is NOT synchronized.</em>
|
* <em>Note that read access is NOT synchronized.</em>
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @return a {@code SeekableInputStream} reading from this stream
|
* @return a {@code SeekableInputStream} reading from this stream
|
||||||
*/
|
*/
|
||||||
@@ -129,9 +129,8 @@ public abstract class RandomAccessStream implements Seekable, DataInput, DataOut
|
|||||||
/**
|
/**
|
||||||
* Returns an output view of this {@code RandomAccessStream}.
|
* Returns an output view of this {@code RandomAccessStream}.
|
||||||
* Invoking this method several times, will return the same object.
|
* Invoking this method several times, will return the same object.
|
||||||
* <p>
|
* <p/>
|
||||||
* <em>Note that write access is NOT synchronized.</em>
|
* <em>Note that write access is NOT synchronized.</em>
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @return a {@code SeekableOutputStream} writing to this stream
|
* @return a {@code SeekableOutputStream} writing to this stream
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ import java.io.IOException;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for seekable streams.
|
* Interface for seekable streams.
|
||||||
*
|
* <p/>
|
||||||
* @see SeekableInputStream
|
* @see SeekableInputStream
|
||||||
* @see SeekableOutputStream
|
* @see SeekableOutputStream
|
||||||
*
|
*
|
||||||
@@ -55,14 +55,12 @@ public interface Seekable {
|
|||||||
/**
|
/**
|
||||||
* Sets the current stream position to the desired location.
|
* Sets the current stream position to the desired location.
|
||||||
* The next read will occur at this location.
|
* The next read will occur at this location.
|
||||||
* <p>
|
* <p/>
|
||||||
* An {@code IndexOutOfBoundsException} will be thrown if pPosition is smaller
|
* An {@code IndexOutOfBoundsException} will be thrown if pPosition is smaller
|
||||||
* than the flushed position (as returned by {@link #getFlushedPosition()}).
|
* than the flushed position (as returned by {@link #getFlushedPosition()}).
|
||||||
* </p>
|
* <p/>
|
||||||
* <p>
|
|
||||||
* It is legal to seek past the end of the file; an {@code EOFException}
|
* It is legal to seek past the end of the file; an {@code EOFException}
|
||||||
* will be thrown only if a read is performed.
|
* will be thrown only if a read is performed.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @param pPosition a long containing the desired file pointer position.
|
* @param pPosition a long containing the desired file pointer position.
|
||||||
*
|
*
|
||||||
@@ -78,29 +76,25 @@ public interface Seekable {
|
|||||||
* Unlike a standard {@code InputStream}, all {@code Seekable}
|
* Unlike a standard {@code InputStream}, all {@code Seekable}
|
||||||
* streams upport marking. Additionally, calls to {@code mark} and
|
* streams upport marking. Additionally, calls to {@code mark} and
|
||||||
* {@code reset} may be nested arbitrarily.
|
* {@code reset} may be nested arbitrarily.
|
||||||
* <p>
|
* <p/>
|
||||||
* Unlike the {@code mark} methods declared by the {@code Reader} or
|
* Unlike the {@code mark} methods declared by the {@code Reader} or
|
||||||
* {@code InputStream}
|
* {@code InputStream}
|
||||||
* interfaces, no {@code readLimit} parameter is used. An arbitrary amount
|
* interfaces, no {@code readLimit} parameter is used. An arbitrary amount
|
||||||
* of data may be read following the call to {@code mark}.
|
* of data may be read following the call to {@code mark}.
|
||||||
* </p>
|
|
||||||
*/
|
*/
|
||||||
void mark();
|
void mark();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the file pointer to its previous position,
|
* Returns the file pointer to its previous position,
|
||||||
* at the time of the most recent unmatched call to mark.
|
* at the time of the most recent unmatched call to mark.
|
||||||
* <p>
|
* <p/>
|
||||||
* Calls to reset without a corresponding call to mark will either:
|
* Calls to reset without a corresponding call to mark will either:
|
||||||
* </p>
|
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>throw an {@code IOException}</li>
|
* <li>throw an {@code IOException}</li>
|
||||||
* <li>or, reset to the beginning of the stream.</li>
|
* <li>or, reset to the beginning of the stream.</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* <p>
|
|
||||||
* An {@code IOException} will be thrown if the previous marked position
|
* An {@code IOException} will be thrown if the previous marked position
|
||||||
* lies in the discarded portion of the stream.
|
* lies in the discarded portion of the stream.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @throws IOException if an I/O error occurs.
|
* @throws IOException if an I/O error occurs.
|
||||||
* @see java.io.InputStream#reset()
|
* @see java.io.InputStream#reset()
|
||||||
@@ -111,11 +105,10 @@ public interface Seekable {
|
|||||||
* Discards the initial portion of the stream prior to the indicated
|
* Discards the initial portion of the stream prior to the indicated
|
||||||
* postion. Attempting to seek to an offset within the flushed portion of
|
* postion. Attempting to seek to an offset within the flushed portion of
|
||||||
* the stream will result in an {@code IndexOutOfBoundsException}.
|
* the stream will result in an {@code IndexOutOfBoundsException}.
|
||||||
* <p>
|
* <p/>
|
||||||
* Calling {@code flushBefore} may allow classes implementing this
|
* Calling {@code flushBefore} may allow classes implementing this
|
||||||
* interface to free up resources such as memory or disk space that are
|
* interface to free up resources such as memory or disk space that are
|
||||||
* being used to store data from the stream.
|
* being used to store data from the stream.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @param pPosition a long containing the length of the file prefix that
|
* @param pPosition a long containing the length of the file prefix that
|
||||||
* may be flushed.
|
* may be flushed.
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ import java.util.Stack;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract base class for {@code InputStream}s implementing the {@code Seekable} interface.
|
* Abstract base class for {@code InputStream}s implementing the {@code Seekable} interface.
|
||||||
*
|
* <p/>
|
||||||
* @see SeekableOutputStream
|
* @see SeekableOutputStream
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ import java.util.Stack;
|
|||||||
/**
|
/**
|
||||||
* Abstract base class for {@code OutputStream}s implementing the
|
* Abstract base class for {@code OutputStream}s implementing the
|
||||||
* {@code Seekable} interface.
|
* {@code Seekable} interface.
|
||||||
*
|
* <p/>
|
||||||
* @see SeekableInputStream
|
* @see SeekableInputStream
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import java.io.StringReader;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* StringArrayReader
|
* StringArrayReader
|
||||||
|
* <p/>
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @author last modified by $Author: haku $
|
* @author last modified by $Author: haku $
|
||||||
@@ -50,8 +51,8 @@ public class StringArrayReader extends StringReader {
|
|||||||
protected final Object finalLock;
|
protected final Object finalLock;
|
||||||
private int currentSting;
|
private int currentSting;
|
||||||
private int markedString;
|
private int markedString;
|
||||||
private long mark;
|
private int mark;
|
||||||
private long next;
|
private int next;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new string array reader.
|
* Create a new string array reader.
|
||||||
@@ -151,7 +152,7 @@ public class StringArrayReader extends StringReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int read(char[] pBuffer, int pOffset, int pLength) throws IOException {
|
public int read(char pBuffer[], int pOffset, int pLength) throws IOException {
|
||||||
synchronized (finalLock) {
|
synchronized (finalLock) {
|
||||||
int read = current.read(pBuffer, pOffset, pLength);
|
int read = current.read(pBuffer, pOffset, pLength);
|
||||||
|
|
||||||
|
|||||||
@@ -39,22 +39,24 @@ import java.io.InputStream;
|
|||||||
/**
|
/**
|
||||||
* An {@code InputStream} reading up to a specified number of bytes from an
|
* An {@code InputStream} reading up to a specified number of bytes from an
|
||||||
* underlying stream.
|
* underlying stream.
|
||||||
|
* <p/>
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/SubStream.java#2 $
|
||||||
*/
|
*/
|
||||||
public final class SubStream extends FilterInputStream {
|
public final class SubStream extends FilterInputStream {
|
||||||
private long bytesLeft;
|
private long bytesLeft;
|
||||||
private int markLimit;
|
private int markLimit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@code SubStream} of the given {@code stream}.
|
* Creates a {@code SubStream} of the given {@code pStream}.
|
||||||
*
|
*
|
||||||
* @param stream the underlying input stream
|
* @param pStream the underlying input stream
|
||||||
* @param length maximum number of bytes to read from this stream
|
* @param pLength maximum number of bytes to read drom this stream
|
||||||
*/
|
*/
|
||||||
public SubStream(final InputStream stream, final long length) {
|
public SubStream(final InputStream pStream, final long pLength) {
|
||||||
super(Validate.notNull(stream, "stream"));
|
super(Validate.notNull(pStream, "stream"));
|
||||||
bytesLeft = Validate.isTrue(length >= 0, length, "length < 0: %s");
|
bytesLeft = pLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -63,23 +65,22 @@ public final class SubStream extends FilterInputStream {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
// NOTE: Do not close the underlying stream, but consume it
|
// NOTE: Do not close the underlying stream
|
||||||
while (bytesLeft > 0) {
|
while (bytesLeft > 0) {
|
||||||
if (skip(bytesLeft) <= 0 && read() < 0) {
|
//noinspection ResultOfMethodCallIgnored
|
||||||
break;
|
skip(bytesLeft);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int available() throws IOException {
|
public int available() throws IOException {
|
||||||
return (int) findMaxLen(super.available());
|
return (int) Math.min(super.available(), bytesLeft);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mark(int readLimit) {
|
public void mark(int pReadLimit) {
|
||||||
super.mark(readLimit);// This either succeeds or does nothing...
|
super.mark(pReadLimit);// This either succeeds or does nothing...
|
||||||
markLimit = readLimit;
|
markLimit = pReadLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -93,42 +94,44 @@ public final class SubStream extends FilterInputStream {
|
|||||||
if (bytesLeft-- <= 0) {
|
if (bytesLeft-- <= 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.read();
|
return super.read();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int read(byte[] bytes) throws IOException {
|
public final int read(byte[] pBytes) throws IOException {
|
||||||
return read(bytes, 0, bytes.length);
|
return read(pBytes, 0, pBytes.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int read(final byte[] bytes, final int off, final int len) throws IOException {
|
public int read(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
|
||||||
if (bytesLeft <= 0) {
|
if (bytesLeft <= 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int read = super.read(bytes, off, (int) findMaxLen(len));
|
int read = super.read(pBytes, pOffset, (int) findMaxLen(pLength));
|
||||||
bytesLeft = read < 0 ? 0 : bytesLeft - read;
|
bytesLeft = read < 0 ? 0 : bytesLeft - read;
|
||||||
|
|
||||||
return read;
|
return read;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public long skip(long length) throws IOException {
|
|
||||||
long skipped = super.skip(findMaxLen(length)); // Skips 0 or more, never -1
|
|
||||||
bytesLeft -= skipped;
|
|
||||||
|
|
||||||
return skipped;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds the maximum number of bytes we can read or skip, from this stream.
|
* Finds the maximum number of bytes we can read or skip, from this stream.
|
||||||
*
|
*
|
||||||
* @param length the requested length
|
* @param pLength the requested length
|
||||||
* @return the maximum number of bytes to read
|
* @return the maximum number of bytes to read
|
||||||
*/
|
*/
|
||||||
private long findMaxLen(long length) {
|
private long findMaxLen(long pLength) {
|
||||||
return bytesLeft < length ? Math.max(bytesLeft, 0) : length;
|
if (bytesLeft < pLength) {
|
||||||
|
return (int) Math.max(bytesLeft, 0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return pLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long skip(long pLength) throws IOException {
|
||||||
|
long skipped = super.skip(findMaxLen(pLength));// Skips 0 or more, never -1
|
||||||
|
bytesLeft -= skipped;
|
||||||
|
return skipped;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import java.io.IOException;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* UnixFileSystem
|
* UnixFileSystem
|
||||||
|
* <p/>
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/UnixFileSystem.java#1 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/UnixFileSystem.java#1 $
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import java.io.IOException;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Win32File
|
* Win32File
|
||||||
|
* <p/>
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/Win32File.java#2 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/Win32File.java#2 $
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import java.io.IOException;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* WindowsFileSystem
|
* WindowsFileSystem
|
||||||
|
* <p/>
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/Win32FileSystem.java#2 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/Win32FileSystem.java#2 $
|
||||||
|
|||||||
@@ -35,11 +35,10 @@ import java.util.Arrays;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@code File} implementation that resolves the Windows {@code .lnk} files as symbolic links.
|
* A {@code File} implementation that resolves the Windows {@code .lnk} files as symbolic links.
|
||||||
* <p>
|
* <p/>
|
||||||
* This class is based on example code from
|
* This class is based on example code from
|
||||||
* <a href="http://www.oreilly.com/catalog/swinghks/index.html">Swing Hacks</a>,
|
* <a href="http://www.oreilly.com/catalog/swinghks/index.html">Swing Hacks</a>,
|
||||||
* By Joshua Marinacci, Chris Adamson (O'Reilly, ISBN: 0-596-00907-0), Hack 30.
|
* By Joshua Marinacci, Chris Adamson (O'Reilly, ISBN: 0-596-00907-0), Hack 30.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/Win32Lnk.java#2 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/Win32Lnk.java#2 $
|
||||||
@@ -325,10 +324,9 @@ final class Win32Lnk extends File {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts two bytes into a short.
|
* Converts two bytes into a short.
|
||||||
* <p>
|
* <p/>
|
||||||
* NOTE: this is little endian because it's for an
|
* NOTE: this is little endian because it's for an
|
||||||
* Intel only OS
|
* Intel only OS
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @ param bytes
|
* @ param bytes
|
||||||
* @ param off
|
* @ param off
|
||||||
|
|||||||
@@ -39,16 +39,15 @@ import java.nio.charset.Charset;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps a {@code Writer} in an {@code OutputStream}.
|
* Wraps a {@code Writer} in an {@code OutputStream}.
|
||||||
* <p>
|
* <p/>
|
||||||
* <em>Instances of this class are not thread-safe.</em>
|
* <em>Instances of this class are not thread-safe.</em>
|
||||||
* </p>
|
* <p/>
|
||||||
* <p>
|
|
||||||
* <em>NOTE: This class is probably not the right way of solving your problem,
|
* <em>NOTE: This class is probably not the right way of solving your problem,
|
||||||
* however it might prove useful in JSPs etc.
|
* however it might prove useful in JSPs etc.
|
||||||
* If possible, it's always better to use the {@code Writer}'s underlying
|
* If possible, it's always better to use the {@code Writer}'s underlying
|
||||||
* {@code OutputStream}, or wrap it's native backing.
|
* {@code OutputStream}, or wrap it's native backing.
|
||||||
* </em>
|
* </em>
|
||||||
* </p>
|
* <p/>
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/WriterOutputStream.java#2 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/WriterOutputStream.java#2 $
|
||||||
|
|||||||
@@ -36,9 +36,9 @@ import java.nio.ByteBuffer;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* {@code Decoder} implementation for standard base64 encoding.
|
* {@code Decoder} implementation for standard base64 encoding.
|
||||||
*
|
* <p/>
|
||||||
* @see <a href="http://tools.ietf.org/html/rfc1421">RFC 1421</a>
|
* @see <a href="http://tools.ietf.org/html/rfc1421">RFC 1421</a>
|
||||||
* @see <a href="http://tools.ietf.org/html/rfc2045">RFC 2045</a>
|
* @see <a href="http://tools.ietf.org/html/rfc2045"RFC 2045</a>
|
||||||
*
|
*
|
||||||
* @see Base64Encoder
|
* @see Base64Encoder
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -36,9 +36,9 @@ import java.nio.ByteBuffer;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* {@code Encoder} implementation for standard base64 encoding.
|
* {@code Encoder} implementation for standard base64 encoding.
|
||||||
*
|
* <p/>
|
||||||
* @see <a href="http://tools.ietf.org/html/rfc1421">RFC 1421</a>
|
* @see <a href="http://tools.ietf.org/html/rfc1421">RFC 1421</a>
|
||||||
* @see <a href="http://tools.ietf.org/html/rfc2045">RFC 2045</a>
|
* @see <a href="http://tools.ietf.org/html/rfc2045"RFC 2045</a>
|
||||||
*
|
*
|
||||||
* @see Base64Decoder
|
* @see Base64Decoder
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import java.io.IOException;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Thrown by {@code Decoder}s when encoded data can not be decoded.
|
* Thrown by {@code Decoder}s when encoded data can not be decoded.
|
||||||
|
* <p/>
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/DecodeException.java#2 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/DecodeException.java#2 $
|
||||||
|
|||||||
@@ -38,10 +38,9 @@ import java.nio.ByteBuffer;
|
|||||||
* Interface for decoders.
|
* Interface for decoders.
|
||||||
* A {@code Decoder} may be used with a {@code DecoderStream}, to perform
|
* A {@code Decoder} may be used with a {@code DecoderStream}, to perform
|
||||||
* on-the-fly decoding from an {@code InputStream}.
|
* on-the-fly decoding from an {@code InputStream}.
|
||||||
* <p>
|
* <p/>
|
||||||
* Important note: Decoder implementations are typically not synchronized.
|
* Important note: Decoder implementations are typically not synchronized.
|
||||||
* </p>
|
* <p/>
|
||||||
*
|
|
||||||
* @see Encoder
|
* @see Encoder
|
||||||
* @see DecoderStream
|
* @see DecoderStream
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -36,8 +36,9 @@ import java.io.InputStream;
|
|||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@code InputStream} that provides on-the-fly decoding from an underlying stream.
|
* An {@code InputStream} that provides on-the-fly decoding from an underlying
|
||||||
*
|
* stream.
|
||||||
|
* <p/>
|
||||||
* @see EncoderStream
|
* @see EncoderStream
|
||||||
* @see Decoder
|
* @see Decoder
|
||||||
*
|
*
|
||||||
@@ -45,39 +46,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 $
|
* @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 {
|
public final class DecoderStream extends FilterInputStream {
|
||||||
private final ByteBuffer buffer;
|
protected final ByteBuffer buffer;
|
||||||
private final Decoder decoder;
|
protected final Decoder decoder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new decoder stream and chains it to the
|
* 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.
|
* The stream will use a default decode buffer size.
|
||||||
*
|
*
|
||||||
* @param stream the underlying input stream.
|
* @param pStream the underlying input stream.
|
||||||
* @param decoder the decoder that will be used to decode the underlying stream
|
* @param pDecoder the decoder that will be used to decode the underlying stream
|
||||||
*
|
*
|
||||||
* @see java.io.FilterInputStream#in
|
* @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
|
// 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
|
* 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 pStream the underlying input stream.
|
||||||
* @param decoder the decoder that will be used to decode the underlying stream
|
* @param pDecoder the decoder that will be used to decode the underlying stream
|
||||||
* @param bufferSize the size of the decode buffer
|
* @param pBufferSize the size of the decode buffer
|
||||||
*
|
*
|
||||||
* @see java.io.FilterInputStream#in
|
* @see java.io.FilterInputStream#in
|
||||||
*/
|
*/
|
||||||
public DecoderStream(final InputStream stream, final Decoder decoder, final int bufferSize) {
|
public DecoderStream(final InputStream pStream, final Decoder pDecoder, final int pBufferSize) {
|
||||||
super(stream);
|
super(pStream);
|
||||||
|
|
||||||
this.decoder = decoder;
|
decoder = pDecoder;
|
||||||
buffer = ByteBuffer.allocate(bufferSize); // TODO: Allow decoder to specify minimum buffer size
|
buffer = ByteBuffer.allocate(pBufferSize);
|
||||||
buffer.flip();
|
buffer.flip();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,15 +96,15 @@ public final class DecoderStream extends FilterInputStream {
|
|||||||
return buffer.get() & 0xff;
|
return buffer.get() & 0xff;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int read(final byte[] bytes, final int offset, final int length) throws IOException {
|
public int read(final byte pBytes[], final int pOffset, final int pLength) throws IOException {
|
||||||
if (bytes == null) {
|
if (pBytes == null) {
|
||||||
throw new NullPointerException();
|
throw new NullPointerException();
|
||||||
}
|
}
|
||||||
else if ((offset < 0) || (offset > bytes.length) || (length < 0) ||
|
else if ((pOffset < 0) || (pOffset > pBytes.length) || (pLength < 0) ||
|
||||||
((offset + length) > bytes.length) || ((offset + length) < 0)) {
|
((pOffset + pLength) > pBytes.length) || ((pOffset + pLength) < 0)) {
|
||||||
throw new IndexOutOfBoundsException("bytes.length=" + bytes.length + " offset=" + offset + " length=" + length);
|
throw new IndexOutOfBoundsException("bytes.length=" + pBytes.length + " offset=" + pOffset + " length=" + pLength);
|
||||||
}
|
}
|
||||||
else if (length == 0) {
|
else if (pLength == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,11 +115,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 count = 0;
|
||||||
int off = offset;
|
int off = pOffset;
|
||||||
|
|
||||||
while (length > count) {
|
while (pLength > count) {
|
||||||
if (!buffer.hasRemaining()) {
|
if (!buffer.hasRemaining()) {
|
||||||
if (fill() < 0) {
|
if (fill() < 0) {
|
||||||
break;
|
break;
|
||||||
@@ -126,8 +127,8 @@ public final class DecoderStream extends FilterInputStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Copy as many bytes as possible
|
// Copy as many bytes as possible
|
||||||
int dstLen = Math.min(length - count, buffer.remaining());
|
int dstLen = Math.min(pLength - count, buffer.remaining());
|
||||||
buffer.get(bytes, off, dstLen);
|
buffer.get(pBytes, off, dstLen);
|
||||||
|
|
||||||
// Update offset (rest)
|
// Update offset (rest)
|
||||||
off += dstLen;
|
off += dstLen;
|
||||||
@@ -139,7 +140,7 @@ public final class DecoderStream extends FilterInputStream {
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long skip(final long length) throws IOException {
|
public long skip(final long pLength) throws IOException {
|
||||||
// End of file?
|
// End of file?
|
||||||
if (!buffer.hasRemaining()) {
|
if (!buffer.hasRemaining()) {
|
||||||
if (fill() < 0) {
|
if (fill() < 0) {
|
||||||
@@ -147,10 +148,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;
|
long total = 0;
|
||||||
|
|
||||||
while (total < length) {
|
while (total < pLength) {
|
||||||
if (!buffer.hasRemaining()) {
|
if (!buffer.hasRemaining()) {
|
||||||
if (fill() < 0) {
|
if (fill() < 0) {
|
||||||
break;
|
break;
|
||||||
@@ -158,7 +159,7 @@ public final class DecoderStream extends FilterInputStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Skipped can never be more than avail, which is an int, so the cast is safe
|
// 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);
|
buffer.position(buffer.position() + skipped);
|
||||||
total += skipped;
|
total += skipped;
|
||||||
}
|
}
|
||||||
@@ -174,7 +175,7 @@ public final class DecoderStream extends FilterInputStream {
|
|||||||
*
|
*
|
||||||
* @throws IOException if an I/O error occurs
|
* @throws IOException if an I/O error occurs
|
||||||
*/
|
*/
|
||||||
private int fill() throws IOException {
|
protected int fill() throws IOException {
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
int read = decoder.decode(in, buffer);
|
int read = decoder.decode(in, buffer);
|
||||||
|
|
||||||
|
|||||||
@@ -38,9 +38,8 @@ import java.nio.ByteBuffer;
|
|||||||
* Interface for encoders.
|
* Interface for encoders.
|
||||||
* An {@code Encoder} may be used with an {@code EncoderStream}, to perform
|
* An {@code Encoder} may be used with an {@code EncoderStream}, to perform
|
||||||
* on-the-fly encoding to an {@code OutputStream}.
|
* on-the-fly encoding to an {@code OutputStream}.
|
||||||
* <p>
|
* <p/>
|
||||||
* Important note: Encoder implementations are typically not synchronized.
|
* Important note: Encoder implementations are typically not synchronized.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @see Decoder
|
* @see Decoder
|
||||||
* @see EncoderStream
|
* @see EncoderStream
|
||||||
|
|||||||
@@ -36,8 +36,9 @@ import java.io.OutputStream;
|
|||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@code OutputStream} that provides on-the-fly encoding to an underlying stream.
|
* An {@code OutputStream} that provides on-the-fly encoding to an underlying
|
||||||
*
|
* stream.
|
||||||
|
* <p/>
|
||||||
* @see DecoderStream
|
* @see DecoderStream
|
||||||
* @see Encoder
|
* @see Encoder
|
||||||
*
|
*
|
||||||
@@ -45,39 +46,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 $
|
* @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 {
|
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 boolean flushOnWrite;
|
||||||
|
|
||||||
private final ByteBuffer buffer;
|
protected final ByteBuffer buffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an output stream filter built on top of the specified
|
* Creates an output stream filter built on top of the specified
|
||||||
* underlying output stream.
|
* underlying output stream.
|
||||||
*
|
*
|
||||||
* @param stream the underlying output stream
|
* @param pStream the underlying output stream
|
||||||
* @param encoder the encoder to use
|
* @param pEncoder the encoder to use
|
||||||
*/
|
*/
|
||||||
public EncoderStream(final OutputStream stream, final Encoder encoder) {
|
public EncoderStream(final OutputStream pStream, final Encoder pEncoder) {
|
||||||
this(stream, encoder, false);
|
this(pStream, pEncoder, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an output stream filter built on top of the specified
|
* Creates an output stream filter built on top of the specified
|
||||||
* underlying output stream.
|
* underlying output stream.
|
||||||
*
|
*
|
||||||
* @param stream the underlying output stream
|
* @param pStream the underlying output stream
|
||||||
* @param encoder the encoder to use
|
* @param pEncoder the encoder to use
|
||||||
* @param flushOnWrite if {@code true}, calls to the byte-array
|
* @param pFlushOnWrite if {@code true}, calls to the byte-array
|
||||||
* {@code write} methods will automatically flush the buffer.
|
* {@code write} methods will automatically flush the buffer.
|
||||||
*/
|
*/
|
||||||
public EncoderStream(final OutputStream stream, final Encoder encoder, final boolean flushOnWrite) {
|
public EncoderStream(final OutputStream pStream, final Encoder pEncoder, final boolean pFlushOnWrite) {
|
||||||
super(stream);
|
super(pStream);
|
||||||
|
|
||||||
this.encoder = encoder;
|
encoder = pEncoder;
|
||||||
this.flushOnWrite = flushOnWrite;
|
flushOnWrite = pFlushOnWrite;
|
||||||
|
|
||||||
buffer = ByteBuffer.allocate(1024);
|
buffer = ByteBuffer.allocate(1024);
|
||||||
|
buffer.flip();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
@@ -102,33 +105,33 @@ public final class EncoderStream extends FilterOutputStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void write(final byte[] bytes) throws IOException {
|
public final void write(final byte[] pBytes) throws IOException {
|
||||||
write(bytes, 0, bytes.length);
|
write(pBytes, 0, pBytes.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Verify that this works for the general case (it probably won't)...
|
// 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
|
// 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
|
// that the encoder can't buffer. In that case, the encoder should probably
|
||||||
// tell the EncoderStream how large buffer it prefers...
|
// tell the EncoderStream how large buffer it prefers...
|
||||||
public void write(final byte[] values, final int offset, final int length) throws IOException {
|
public void write(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
|
||||||
if (!flushOnWrite && length < buffer.remaining()) {
|
if (!flushOnWrite && pLength < buffer.remaining()) {
|
||||||
// Buffer data
|
// Buffer data
|
||||||
buffer.put(values, offset, length);
|
buffer.put(pBytes, pOffset, pLength);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Encode data already in the buffer
|
// Encode data already in the buffer
|
||||||
encodeBuffer();
|
encodeBuffer();
|
||||||
|
|
||||||
// Encode rest without buffering
|
// 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()) {
|
if (!buffer.hasRemaining()) {
|
||||||
encodeBuffer(); // Resets bufferPos to 0
|
encodeBuffer(); // Resets bufferPos to 0
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.put((byte) value);
|
buffer.put((byte) pByte);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,36 +37,30 @@ import java.nio.ByteBuffer;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Decoder implementation for Apple PackBits run-length encoding.
|
* Decoder implementation for Apple PackBits run-length encoding.
|
||||||
* <p>
|
* <p/>
|
||||||
* <small>From Wikipedia, the free encyclopedia</small>
|
* <small>From Wikipedia, the free encyclopedia</small><br/>
|
||||||
* <br>
|
|
||||||
* PackBits is a fast, simple compression scheme for run-length encoding of
|
* PackBits is a fast, simple compression scheme for run-length encoding of
|
||||||
* data.
|
* data.
|
||||||
* </p>
|
* <p/>
|
||||||
* <p>
|
|
||||||
* Apple introduced the PackBits format with the release of MacPaint on the
|
* Apple introduced the PackBits format with the release of MacPaint on the
|
||||||
* Macintosh computer. This compression scheme is one of the types of
|
* Macintosh computer. This compression scheme is one of the types of
|
||||||
* compression that can be used in TIFF-files.
|
* compression that can be used in TIFF-files.
|
||||||
* </p>
|
* <p/>
|
||||||
* <p>
|
|
||||||
* A PackBits data stream consists of packets of one byte of header followed by
|
* A PackBits data stream consists of packets of one byte of header followed by
|
||||||
* data. The header is a signed byte; the data can be signed, unsigned, or
|
* data. The header is a signed byte; the data can be signed, unsigned, or
|
||||||
* packed (such as MacPaint pixels).
|
* packed (such as MacPaint pixels).
|
||||||
* </p>
|
* <p/>
|
||||||
* <table>
|
* <table><tr><th>Header byte</th><th>Data</th></tr>
|
||||||
* <caption>PackBits</caption>
|
* <tr><td>0 to 127</td> <td>1 + <i>n</i> literal bytes of data</td></tr>
|
||||||
* <tr><th>Header byte</th><th>Data</th></tr>
|
* <tr><td>0 to -127</td> <td>One byte of data, repeated 1 - <i>n</i> times in
|
||||||
* <tr><td>0 to 127</td> <td>1 + <i>n</i> literal bytes of data</td></tr>
|
* the decompressed output</td></tr>
|
||||||
* <tr><td>0 to -127</td> <td>One byte of data, repeated 1 - <i>n</i> times in the decompressed output</td></tr>
|
* <tr><td>-128</td> <td>No operation</td></tr></table>
|
||||||
* <tr><td>-128</td> <td>No operation</td></tr>
|
* <p/>
|
||||||
* </table>
|
|
||||||
* <p>
|
|
||||||
* Note that interpreting 0 as positive or negative makes no difference in the
|
* Note that interpreting 0 as positive or negative makes no difference in the
|
||||||
* output. Runs of two bytes adjacent to non-runs are typically written as
|
* output. Runs of two bytes adjacent to non-runs are typically written as
|
||||||
* literal data.
|
* literal data.
|
||||||
* </p>
|
* <p/>
|
||||||
*
|
* See <a href="http://developer.apple.com/technotes/tn/tn1023.html">Understanding PackBits</a>
|
||||||
* @see <a href="http://developer.apple.com/technotes/tn/tn1023.html">Understanding PackBits</a>
|
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/PackBitsDecoder.java#1 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/PackBitsDecoder.java#1 $
|
||||||
@@ -77,6 +71,8 @@ public final class PackBitsDecoder implements Decoder {
|
|||||||
private final boolean disableNoOp;
|
private final boolean disableNoOp;
|
||||||
private final byte[] sample;
|
private final byte[] sample;
|
||||||
|
|
||||||
|
private int leftOfRun;
|
||||||
|
private boolean splitRun;
|
||||||
private boolean reachedEOF;
|
private boolean reachedEOF;
|
||||||
|
|
||||||
/** Creates a {@code PackBitsDecoder}. */
|
/** Creates a {@code PackBitsDecoder}. */
|
||||||
@@ -86,11 +82,10 @@ public final class PackBitsDecoder implements Decoder {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@code PackBitsDecoder}, with optional compatibility mode.
|
* Creates a {@code PackBitsDecoder}, with optional compatibility mode.
|
||||||
* <p>
|
* <p/>
|
||||||
* As some implementations of PackBits-like encoders treat {@code -128} as length of
|
* As some implementations of PackBits-like encoders treat {@code -128} as length of
|
||||||
* a compressed run, instead of a no-op, it's possible to disable no-ops for compatibility.
|
* a compressed run, instead of a no-op, it's possible to disable no-ops for compatibility.
|
||||||
* Should be used with caution, even though, most known encoders never write no-ops in the compressed streams.
|
* Should be used with caution, even though, most known encoders never write no-ops in the compressed streams.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @param disableNoOp {@code true} if {@code -128} should be treated as a compressed run, and not a no-op
|
* @param disableNoOp {@code true} if {@code -128} should be treated as a compressed run, and not a no-op
|
||||||
*/
|
*/
|
||||||
@@ -100,11 +95,10 @@ public final class PackBitsDecoder implements Decoder {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@code PackBitsDecoder}, with optional compatibility mode.
|
* Creates a {@code PackBitsDecoder}, with optional compatibility mode.
|
||||||
* <p>
|
* <p/>
|
||||||
* As some implementations of PackBits-like encoders treat {@code -128} as length of
|
* As some implementations of PackBits-like encoders treat {@code -128} as length of
|
||||||
* a compressed run, instead of a no-op, it's possible to disable no-ops for compatibility.
|
* a compressed run, instead of a no-op, it's possible to disable no-ops for compatibility.
|
||||||
* Should be used with caution, even though, most known encoders never write no-ops in the compressed streams.
|
* Should be used with caution, even though, most known encoders never write no-ops in the compressed streams.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @param disableNoOp {@code true} if {@code -128} should be treated as a compressed run, and not a no-op
|
* @param disableNoOp {@code true} if {@code -128} should be treated as a compressed run, and not a no-op
|
||||||
*/
|
*/
|
||||||
@@ -120,43 +114,65 @@ public final class PackBitsDecoder implements Decoder {
|
|||||||
* @param buffer a byte array, minimum 128 (or 129 if no-op is disabled) bytes long
|
* @param buffer a byte array, minimum 128 (or 129 if no-op is disabled) bytes long
|
||||||
* @return The number of bytes decoded
|
* @return The number of bytes decoded
|
||||||
*
|
*
|
||||||
* @throws java.io.IOException if a problem occurs during decoding.
|
* @throws java.io.IOException
|
||||||
*/
|
*/
|
||||||
public int decode(final InputStream stream, final ByteBuffer buffer) throws IOException {
|
public int decode(final InputStream stream, final ByteBuffer buffer) throws IOException {
|
||||||
if (reachedEOF) {
|
if (reachedEOF) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: We don't decode more than single runs, because some writers add pad bytes inside the stream...
|
// TODO: Don't decode more than single runs, because some writers add pad bytes inside the stream...
|
||||||
// Start new run
|
while (buffer.hasRemaining()) {
|
||||||
int b = stream.read();
|
int n;
|
||||||
if (b < 0) {
|
|
||||||
reachedEOF = true;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int n = (byte) b;
|
if (splitRun) {
|
||||||
|
// Continue run
|
||||||
try {
|
n = leftOfRun;
|
||||||
if (n >= 0) {
|
splitRun = false;
|
||||||
// Copy next n + 1 bytes literally
|
|
||||||
readFully(stream, buffer, sample.length * (n + 1));
|
|
||||||
}
|
}
|
||||||
// Allow -128 for compatibility, see above
|
else {
|
||||||
else if (disableNoOp || n != -128) {
|
// Start new run
|
||||||
// Replicate the next byte -n + 1 times
|
int b = stream.read();
|
||||||
for (int s = 0; s < sample.length; s++) {
|
if (b < 0) {
|
||||||
sample[s] = readByte(stream);
|
reachedEOF = true;
|
||||||
}
|
break;
|
||||||
|
|
||||||
for (int i = -n + 1; i > 0; i--) {
|
|
||||||
buffer.put(sample);
|
|
||||||
}
|
}
|
||||||
|
n = (byte) b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split run at or before max
|
||||||
|
if (n >= 0 && n + 1 > buffer.remaining()) {
|
||||||
|
leftOfRun = n;
|
||||||
|
splitRun = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (n < 0 && -n + 1 > buffer.remaining()) {
|
||||||
|
leftOfRun = n;
|
||||||
|
splitRun = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (n >= 0) {
|
||||||
|
// Copy next n + 1 bytes literally
|
||||||
|
readFully(stream, buffer, sample.length * (n + 1));
|
||||||
|
}
|
||||||
|
// Allow -128 for compatibility, see above
|
||||||
|
else if (disableNoOp || n != -128) {
|
||||||
|
// Replicate the next byte -n + 1 times
|
||||||
|
for (int s = 0; s < sample.length; s++) {
|
||||||
|
sample[s] = readByte(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = -n + 1; i > 0; i--) {
|
||||||
|
buffer.put(sample);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// else NOOP (-128)
|
||||||
|
}
|
||||||
|
catch (IndexOutOfBoundsException e) {
|
||||||
|
throw new DecodeException("Error in PackBits decompression, data seems corrupt", e);
|
||||||
}
|
}
|
||||||
// else NOOP (-128)
|
|
||||||
}
|
|
||||||
catch (IndexOutOfBoundsException e) {
|
|
||||||
throw new DecodeException("Error in PackBits decompression, data seems corrupt", e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return buffer.position();
|
return buffer.position();
|
||||||
|
|||||||
@@ -36,36 +36,30 @@ import java.nio.ByteBuffer;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Encoder implementation for Apple PackBits run-length encoding.
|
* Encoder implementation for Apple PackBits run-length encoding.
|
||||||
* <p>
|
* <p/>
|
||||||
* From Wikipedia, the free encyclopedia
|
* From Wikipedia, the free encyclopedia<br/>
|
||||||
* <br>
|
|
||||||
* PackBits is a fast, simple compression scheme for run-length encoding of
|
* PackBits is a fast, simple compression scheme for run-length encoding of
|
||||||
* data.
|
* data.
|
||||||
* </p>
|
* <p/>
|
||||||
* <p>
|
|
||||||
* Apple introduced the PackBits format with the release of MacPaint on the
|
* Apple introduced the PackBits format with the release of MacPaint on the
|
||||||
* Macintosh computer. This compression scheme is one of the types of
|
* Macintosh computer. This compression scheme is one of the types of
|
||||||
* compression that can be used in TIFF-files.
|
* compression that can be used in TIFF-files.
|
||||||
* </p>
|
* <p/>
|
||||||
* <p>
|
|
||||||
* A PackBits data stream consists of packets of one byte of header followed by
|
* A PackBits data stream consists of packets of one byte of header followed by
|
||||||
* data. The header is a signed byte; the data can be signed, unsigned, or
|
* data. The header is a signed byte; the data can be signed, unsigned, or
|
||||||
* packed (such as MacPaint pixels).
|
* packed (such as MacPaint pixels).
|
||||||
* </p>
|
* <p/>
|
||||||
* <table>
|
* <table><tr><th>Header byte</th><th>Data</th></tr>
|
||||||
* <caption>PackBits</caption>
|
* <tr><td>0 to 127</td> <td>1 + <i>n</i> literal bytes of data</td></tr>
|
||||||
* <tr><th>Header byte</th><th>Data</th></tr>
|
* <tr><td>0 to -127</td> <td>One byte of data, repeated 1 - <i>n</i> times in
|
||||||
* <tr><td>0 to 127</td> <td>1 + <i>n</i> literal bytes of data</td></tr>
|
* the decompressed output</td></tr>
|
||||||
* <tr><td>0 to -127</td> <td>One byte of data, repeated 1 - <i>n</i> times in the decompressed output</td></tr>
|
* <tr><td>-128</td> <td>No operation</td></tr></table>
|
||||||
* <tr><td>-128</td> <td>No operation</td></tr>
|
* <p/>
|
||||||
* </table>
|
|
||||||
* <p>
|
|
||||||
* Note that interpreting 0 as positive or negative makes no difference in the
|
* Note that interpreting 0 as positive or negative makes no difference in the
|
||||||
* output. Runs of two bytes adjacent to non-runs are typically written as
|
* output. Runs of two bytes adjacent to non-runs are typically written as
|
||||||
* literal data.
|
* literal data.
|
||||||
* </p>
|
* <p/>
|
||||||
*
|
* See <a href="http://developer.apple.com/technotes/tn/tn1023.html">Understanding PackBits</a>
|
||||||
* @see <a href="http://developer.apple.com/technotes/tn/tn1023.html">Understanding PackBits</a>
|
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/PackBitsEncoder.java#1 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/PackBitsEncoder.java#1 $
|
||||||
|
|||||||
@@ -35,28 +35,24 @@ import com.twelvemonkeys.lang.StringUtil;
|
|||||||
|
|
||||||
import javax.imageio.stream.ImageInputStream;
|
import javax.imageio.stream.ImageInputStream;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.ByteOrder;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.SortedSet;
|
import java.util.SortedSet;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import static com.twelvemonkeys.lang.Validate.notNull;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a read-only OLE2 compound document.
|
* Represents a read-only OLE2 compound document.
|
||||||
* <p>
|
* <p/>
|
||||||
* <!-- TODO: Consider really detaching the entries, as this is hard for users to enforce... -->
|
* <!-- TODO: Consider really detaching the entries, as this is hard for users to enforce... -->
|
||||||
* <em>NOTE: This class is not synchronized. Accessing the document or its
|
* <em>NOTE: This class is not synchronized. Accessing the document or its
|
||||||
* entries from different threads, will need synchronization on the document
|
* entries from different threads, will need synchronization on the document
|
||||||
* instance.</em>
|
* instance.</em>
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.no">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.no">Harald Kuhr</a>
|
||||||
* @author last modified by $Author: haku $
|
* @author last modified by $Author: haku $
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/ole2/CompoundDocument.java#4 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/ole2/CompoundDocument.java#4 $
|
||||||
*/
|
*/
|
||||||
public final class CompoundDocument implements AutoCloseable {
|
public final class CompoundDocument {
|
||||||
// TODO: Write support...
|
// TODO: Write support...
|
||||||
// TODO: Properties: http://support.microsoft.com/kb/186898
|
// TODO: Properties: http://support.microsoft.com/kb/186898
|
||||||
|
|
||||||
@@ -100,19 +96,13 @@ public final class CompoundDocument implements AutoCloseable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a (for now) read only {@code CompoundDocument}.
|
* Creates a (for now) read only {@code CompoundDocument}.
|
||||||
* <p>
|
|
||||||
* <em>Warning! You must invoke {@link #close()} on the compound document
|
|
||||||
* created from this constructor when done, to avoid leaking file
|
|
||||||
* descriptors.</em>
|
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @param file the file to read from
|
* @param pFile the file to read from
|
||||||
*
|
*
|
||||||
* @throws IOException if an I/O exception occurs while reading the header
|
* @throws IOException if an I/O exception occurs while reading the header
|
||||||
*/
|
*/
|
||||||
public CompoundDocument(final File file) throws IOException {
|
public CompoundDocument(final File pFile) throws IOException {
|
||||||
// TODO: We need to close this (or it's underlying RAF)! Otherwise we're leaking file descriptors!
|
input = new LittleEndianRandomAccessFile(FileUtil.resolve(pFile), "r");
|
||||||
input = new LittleEndianRandomAccessFile(FileUtil.resolve(file), "r");
|
|
||||||
|
|
||||||
// TODO: Might be better to read header on first read operation?!
|
// TODO: Might be better to read header on first read operation?!
|
||||||
// OTOH: It's also good to be fail-fast, so at least we should make
|
// OTOH: It's also good to be fail-fast, so at least we should make
|
||||||
@@ -123,17 +113,17 @@ public final class CompoundDocument implements AutoCloseable {
|
|||||||
/**
|
/**
|
||||||
* Creates a read only {@code CompoundDocument}.
|
* Creates a read only {@code CompoundDocument}.
|
||||||
*
|
*
|
||||||
* @param pInput the input to read from.
|
* @param pInput the input to read from
|
||||||
*
|
*
|
||||||
* @throws IOException if an I/O exception occurs while reading the header
|
* @throws IOException if an I/O exception occurs while reading the header
|
||||||
*/
|
*/
|
||||||
public CompoundDocument(final InputStream pInput) throws IOException {
|
public CompoundDocument(final InputStream pInput) throws IOException {
|
||||||
this(new MemoryCacheSeekableStream(pInput));
|
this(new FileCacheSeekableStream(pInput));
|
||||||
}
|
}
|
||||||
|
|
||||||
// For testing only, consider exposing later
|
// For testing only, consider exposing later
|
||||||
CompoundDocument(final SeekableInputStream stream) throws IOException {
|
CompoundDocument(final SeekableInputStream pInput) throws IOException {
|
||||||
input = new SeekableLittleEndianDataInputStream(stream);
|
input = new SeekableLittleEndianDataInputStream(pInput);
|
||||||
|
|
||||||
// TODO: Might be better to read header on first read operation?!
|
// TODO: Might be better to read header on first read operation?!
|
||||||
// OTOH: It's also good to be fail-fast, so at least we should make
|
// OTOH: It's also good to be fail-fast, so at least we should make
|
||||||
@@ -144,15 +134,12 @@ public final class CompoundDocument implements AutoCloseable {
|
|||||||
/**
|
/**
|
||||||
* Creates a read only {@code CompoundDocument}.
|
* Creates a read only {@code CompoundDocument}.
|
||||||
*
|
*
|
||||||
* @param input the input to read from
|
* @param pInput the input to read from
|
||||||
*
|
*
|
||||||
* @throws IOException if an I/O exception occurs while reading the header
|
* @throws IOException if an I/O exception occurs while reading the header
|
||||||
*/
|
*/
|
||||||
public CompoundDocument(final ImageInputStream input) throws IOException {
|
public CompoundDocument(final ImageInputStream pInput) throws IOException {
|
||||||
this.input = notNull(input, "input");
|
input = pInput;
|
||||||
|
|
||||||
// This implementation only supports little endian (Intel) CompoundDocuments
|
|
||||||
input.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
|
||||||
|
|
||||||
// TODO: Might be better to read header on first read operation?!
|
// TODO: Might be better to read header on first read operation?!
|
||||||
// OTOH: It's also good to be fail-fast, so at least we should make
|
// OTOH: It's also good to be fail-fast, so at least we should make
|
||||||
@@ -160,27 +147,6 @@ public final class CompoundDocument implements AutoCloseable {
|
|||||||
readHeader();
|
readHeader();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This method will close the underlying {@link RandomAccessFile} if any,
|
|
||||||
* but will leave any stream created outside the document open.
|
|
||||||
*
|
|
||||||
* @see #CompoundDocument(File)
|
|
||||||
* @see RandomAccessFile#close()
|
|
||||||
*
|
|
||||||
* @throws IOException if an I/O error occurs.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void close() throws IOException {
|
|
||||||
if (input instanceof RandomAccessFile) {
|
|
||||||
((RandomAccessFile) input).close();
|
|
||||||
}
|
|
||||||
else if (input instanceof LittleEndianRandomAccessFile) {
|
|
||||||
((LittleEndianRandomAccessFile) input).close();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Other streams are left open
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean canRead(final DataInput pInput) {
|
public static boolean canRead(final DataInput pInput) {
|
||||||
return canRead(pInput, true);
|
return canRead(pInput, true);
|
||||||
}
|
}
|
||||||
@@ -643,10 +609,9 @@ public final class CompoundDocument implements AutoCloseable {
|
|||||||
* milliseconds since January 1, 1970.
|
* milliseconds since January 1, 1970.
|
||||||
* The time stamp parameter is assumed to be in units of
|
* The time stamp parameter is assumed to be in units of
|
||||||
* 100 nano seconds since January 1, 1601.
|
* 100 nano seconds since January 1, 1601.
|
||||||
* <p>
|
* <p/>
|
||||||
* If the timestamp is {@code 0L} (meaning not specified), no conversion
|
* If the timestamp is {@code 0L} (meaning not specified), no conversion
|
||||||
* is done, to behave like {@code java.io.File}.
|
* is done, to behave like {@code java.io.File}.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @param pMSTime an unsigned long value representing the time stamp (in
|
* @param pMSTime an unsigned long value representing the time stamp (in
|
||||||
* units of 100 nano seconds since January 1, 1601).
|
* units of 100 nano seconds since January 1, 1601).
|
||||||
|
|||||||
@@ -214,9 +214,8 @@ public final class Entry implements Comparable<Entry> {
|
|||||||
* The time is converted from its internal representation to standard Java
|
* The time is converted from its internal representation to standard Java
|
||||||
* representation, milliseconds since the epoch
|
* representation, milliseconds since the epoch
|
||||||
* (00:00:00 GMT, January 1, 1970).
|
* (00:00:00 GMT, January 1, 1970).
|
||||||
* <p>
|
* <p/>
|
||||||
* Note that most applications leaves this value empty ({@code 0L}).
|
* Note that most applications leaves this value empty ({@code 0L}).
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @return A {@code long} value representing the time this entry was
|
* @return A {@code long} value representing the time this entry was
|
||||||
* created, measured in milliseconds since the epoch
|
* created, measured in milliseconds since the epoch
|
||||||
@@ -232,9 +231,8 @@ public final class Entry implements Comparable<Entry> {
|
|||||||
* The time is converted from its internal representation to standard Java
|
* The time is converted from its internal representation to standard Java
|
||||||
* representation, milliseconds since the epoch
|
* representation, milliseconds since the epoch
|
||||||
* (00:00:00 GMT, January 1, 1970).
|
* (00:00:00 GMT, January 1, 1970).
|
||||||
* <p>
|
* <p/>
|
||||||
* Note that many applications leaves this value empty ({@code 0L}).
|
* Note that many applications leaves this value empty ({@code 0L}).
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @return A {@code long} value representing the time this entry was
|
* @return A {@code long} value representing the time this entry was
|
||||||
* last modified, measured in milliseconds since the epoch
|
* last modified, measured in milliseconds since the epoch
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import java.util.*;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains mappings from file extension to mime-types and from mime-type to file-types.
|
* Contains mappings from file extension to mime-types and from mime-type to file-types.
|
||||||
|
* <p/>
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/net/MIMEUtil.java#5 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/net/MIMEUtil.java#5 $
|
||||||
|
|||||||
@@ -29,9 +29,6 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.xml;
|
package com.twelvemonkeys.xml;
|
||||||
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.io.Writer;
|
|
||||||
|
|
||||||
import org.w3c.dom.DOMConfiguration;
|
import org.w3c.dom.DOMConfiguration;
|
||||||
import org.w3c.dom.DOMImplementationList;
|
import org.w3c.dom.DOMImplementationList;
|
||||||
import org.w3c.dom.Document;
|
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.LSOutput;
|
||||||
import org.w3c.dom.ls.LSSerializer;
|
import org.w3c.dom.ls.LSSerializer;
|
||||||
|
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.Writer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@code DOMImplementationLS} backed implementation.
|
* {@code DOMImplementationLS} backed implementation.
|
||||||
*
|
*
|
||||||
@@ -88,12 +88,22 @@ public final class DOMSerializer {
|
|||||||
output.setCharacterStream(pStream);
|
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
|
* Specifies wether the serializer should use indentation and optimize for
|
||||||
* readability.
|
* readability.
|
||||||
* <p>
|
* <p/>
|
||||||
* Note: This is a hint, and may be ignored by DOM implementations.
|
* Note: This is a hint, and may be ignored by DOM implemenations.
|
||||||
* </p>
|
|
||||||
*
|
*
|
||||||
* @param pPrettyPrint {@code true} to enable pretty printing
|
* @param pPrettyPrint {@code true} to enable pretty printing
|
||||||
*/
|
*/
|
||||||
@@ -158,7 +168,13 @@ public final class DOMSerializer {
|
|||||||
try {
|
try {
|
||||||
return DOMImplementationRegistry.newInstance();
|
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);
|
throw new IllegalStateException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Regular → Executable
+15
-23
@@ -30,23 +30,16 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.xml;
|
package com.twelvemonkeys.xml;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import com.twelvemonkeys.lang.StringUtil;
|
||||||
import java.io.ByteArrayOutputStream;
|
import org.w3c.dom.*;
|
||||||
import java.io.IOException;
|
import org.xml.sax.SAXException;
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.io.OutputStreamWriter;
|
|
||||||
import java.io.PrintWriter;
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
import javax.xml.parsers.DocumentBuilder;
|
import javax.xml.parsers.DocumentBuilder;
|
||||||
import javax.xml.parsers.DocumentBuilderFactory;
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
import javax.xml.parsers.ParserConfigurationException;
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
|
import java.io.*;
|
||||||
import org.w3c.dom.*;
|
import java.nio.charset.Charset;
|
||||||
import org.xml.sax.SAXException;
|
import java.util.Date;
|
||||||
|
|
||||||
import com.twelvemonkeys.lang.StringUtil;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* XMLSerializer
|
* 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) {
|
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);
|
pBuilder.append(pEntity);
|
||||||
return pEnd + 1;
|
return pEnd + 1;
|
||||||
}
|
}
|
||||||
@@ -396,10 +389,9 @@ public class XMLSerializer {
|
|||||||
private void writeDocument(final PrintWriter pOut, final Node pNode, final SerializationContext pContext) {
|
private void writeDocument(final PrintWriter pOut, final Node pNode, final SerializationContext pContext) {
|
||||||
// Document fragments might not have child nodes...
|
// Document fragments might not have child nodes...
|
||||||
if (pNode.hasChildNodes()) {
|
if (pNode.hasChildNodes()) {
|
||||||
Node child = pNode.getFirstChild();
|
NodeList nodes = pNode.getChildNodes();
|
||||||
while (child != null) {
|
for (int i = 0; i < nodes.getLength(); i++) {
|
||||||
writeNodeRecursive(pOut, child, pContext);
|
writeNodeRecursive(pOut, nodes.item(i), pContext);
|
||||||
child = child.getNextSibling();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -456,10 +448,9 @@ public class XMLSerializer {
|
|||||||
pOut.println();
|
pOut.println();
|
||||||
}
|
}
|
||||||
|
|
||||||
Node child = pNode.getFirstChild();
|
NodeList children = pNode.getChildNodes();
|
||||||
while (child != null) {
|
for (int i = 0; i < children.getLength(); i++) {
|
||||||
writeNodeRecursive(pOut, child, pContext.push());
|
writeNodeRecursive(pOut, children.item(i), pContext.push());
|
||||||
child = child.getNextSibling();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pContext.preserveSpace) {
|
if (!pContext.preserveSpace) {
|
||||||
@@ -534,7 +525,8 @@ public class XMLSerializer {
|
|||||||
builder = factory.newDocumentBuilder();
|
builder = factory.newDocumentBuilder();
|
||||||
}
|
}
|
||||||
catch (ParserConfigurationException e) {
|
catch (ParserConfigurationException e) {
|
||||||
throw new IOException(e);
|
//noinspection ThrowableInstanceNeverThrown BOGUS
|
||||||
|
throw (IOException) new IOException(e.getMessage()).initCause(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
DOMImplementation dom = builder.getDOMImplementation();
|
DOMImplementation dom = builder.getDOMImplementation();
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ package com.twelvemonkeys.io;
|
|||||||
|
|
||||||
import com.twelvemonkeys.lang.StringUtil;
|
import com.twelvemonkeys.lang.StringUtil;
|
||||||
import com.twelvemonkeys.util.CollectionUtil;
|
import com.twelvemonkeys.util.CollectionUtil;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
@@ -39,8 +40,7 @@ import java.io.StringReader;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import static org.junit.Assert.*;
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CompoundReaderTestCase
|
* CompoundReaderTestCase
|
||||||
|
|||||||
+3
-2
@@ -30,11 +30,12 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FastByteArrayOutputStreamTestCase
|
* FastByteArrayOutputStreamTestCase
|
||||||
|
|||||||
@@ -30,10 +30,11 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import static org.junit.Assert.*;
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MemoryCacheSeekableStreamTestCase
|
* MemoryCacheSeekableStreamTestCase
|
||||||
@@ -91,13 +92,13 @@ public class FileSeekableStreamTest extends SeekableInputStreamAbstractTest {
|
|||||||
try {
|
try {
|
||||||
FileUtil.read(stream); // Read until EOF
|
FileUtil.read(stream); // Read until EOF
|
||||||
|
|
||||||
assertEquals(-1, stream.read(), "EOF not reached (test case broken)");
|
assertEquals("EOF not reached (test case broken)", -1, stream.read());
|
||||||
assertFalse(closed[0], "Underlying stream closed before close");
|
assertFalse("Underlying stream closed before close", closed[0]);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
stream.close();
|
stream.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue(closed[0], "Underlying stream not closed");
|
assertTrue("Underlying stream not closed", closed[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,14 +46,14 @@
|
|||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
import com.twelvemonkeys.lang.ObjectAbstractTest;
|
import com.twelvemonkeys.lang.ObjectAbstractTest;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import static org.junit.Assert.*;
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* InputStreamAbstractTestCase
|
* InputStreamAbstractTestCase
|
||||||
@@ -104,15 +104,15 @@ public abstract class InputStreamAbstractTest extends ObjectAbstractTest {
|
|||||||
int size = 5;
|
int size = 5;
|
||||||
InputStream input = makeInputStream(makeOrderedArray(size));
|
InputStream input = makeInputStream(makeOrderedArray(size));
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
assertTrue((size - i) >= input.available(), "Check Size [" + i + "]");
|
assertTrue("Check Size [" + i + "]", (size - i) >= input.available());
|
||||||
assertEquals(i, input.read(), "Check Value [" + i + "]");
|
assertEquals("Check Value [" + i + "]", i, input.read());
|
||||||
}
|
}
|
||||||
assertEquals(0, input.available(), "Available after contents all read");
|
assertEquals("Available after contents all read", 0, input.available());
|
||||||
|
|
||||||
// Test reading after the end of file
|
// Test reading after the end of file
|
||||||
try {
|
try {
|
||||||
int result = input.read();
|
int result = input.read();
|
||||||
assertEquals( -1, result, "Wrong value read after end of file");
|
assertEquals("Wrong value read after end of file", -1, result);
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
fail("Should not have thrown an IOException: " + e.getMessage());
|
fail("Should not have thrown an IOException: " + e.getMessage());
|
||||||
@@ -122,12 +122,12 @@ public abstract class InputStreamAbstractTest extends ObjectAbstractTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testAvailable() throws Exception {
|
public void testAvailable() throws Exception {
|
||||||
InputStream input = makeInputStream(1);
|
InputStream input = makeInputStream(1);
|
||||||
assertFalse(input.read() < 0, "Unexpected EOF");
|
assertFalse("Unexpected EOF", input.read() < 0);
|
||||||
assertEquals(0, input.available(), "Available after contents all read");
|
assertEquals("Available after contents all read", 0, input.available());
|
||||||
|
|
||||||
// Check availbale is zero after End of file
|
// Check availbale is zero after End of file
|
||||||
assertEquals(-1, input.read(), "End of File");
|
assertEquals("End of File", -1, input.read());
|
||||||
assertEquals( 0, input.available(), "Available after End of File");
|
assertEquals("Available after End of File", 0, input.available());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -138,26 +138,26 @@ public abstract class InputStreamAbstractTest extends ObjectAbstractTest {
|
|||||||
|
|
||||||
// Read into array
|
// Read into array
|
||||||
int count1 = input.read(bytes);
|
int count1 = input.read(bytes);
|
||||||
assertEquals(bytes.length, count1, "Read 1");
|
assertEquals("Read 1", bytes.length, count1);
|
||||||
for (int i = 0; i < count1; i++) {
|
for (int i = 0; i < count1; i++) {
|
||||||
assertEquals(i, bytes[i], "Check Bytes 1");
|
assertEquals("Check Bytes 1", i, bytes[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read into array
|
// Read into array
|
||||||
int count2 = input.read(bytes);
|
int count2 = input.read(bytes);
|
||||||
assertEquals(5, count2, "Read 2");
|
assertEquals("Read 2", 5, count2);
|
||||||
for (int i = 0; i < count2; i++) {
|
for (int i = 0; i < count2; i++) {
|
||||||
assertEquals(count1 + i, bytes[i], "Check Bytes 2");
|
assertEquals("Check Bytes 2", count1 + i, bytes[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// End of File
|
// End of File
|
||||||
int count3 = input.read(bytes);
|
int count3 = input.read(bytes);
|
||||||
assertEquals(-1, count3, "Read 3 (EOF)");
|
assertEquals("Read 3 (EOF)", -1, count3);
|
||||||
|
|
||||||
// Test reading after the end of file
|
// Test reading after the end of file
|
||||||
try {
|
try {
|
||||||
int result = input.read(bytes);
|
int result = input.read(bytes);
|
||||||
assertEquals(-1, result, "Wrong value read after end of file");
|
assertEquals("Wrong value read after end of file", -1, result);
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
fail("Should not have thrown an IOException: " + e.getMessage());
|
fail("Should not have thrown an IOException: " + e.getMessage());
|
||||||
@@ -170,20 +170,20 @@ public abstract class InputStreamAbstractTest extends ObjectAbstractTest {
|
|||||||
int offset = 2;
|
int offset = 2;
|
||||||
int lth = 4;
|
int lth = 4;
|
||||||
int count5 = input.read(bytes, offset, lth);
|
int count5 = input.read(bytes, offset, lth);
|
||||||
assertEquals(lth, count5, "Read 5");
|
assertEquals("Read 5", lth, count5);
|
||||||
for (int i = offset; i < lth; i++) {
|
for (int i = offset; i < lth; i++) {
|
||||||
assertEquals(i - offset, bytes[i], "Check Bytes 2");
|
assertEquals("Check Bytes 2", i - offset, bytes[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEOF() throws Exception {
|
public void testEOF() throws Exception {
|
||||||
InputStream input = makeInputStream(makeOrderedArray(2));
|
InputStream input = makeInputStream(makeOrderedArray(2));
|
||||||
assertEquals(0, input.read(), "Read 1");
|
assertEquals("Read 1", 0, input.read());
|
||||||
assertEquals(1, input.read(), "Read 2");
|
assertEquals("Read 2", 1, input.read());
|
||||||
assertEquals(-1, input.read(), "Read 3");
|
assertEquals("Read 3", -1, input.read());
|
||||||
assertEquals(-1, input.read(), "Read 4");
|
assertEquals("Read 4", -1, input.read());
|
||||||
assertEquals(-1, input.read(), "Read 5");
|
assertEquals("Read 5", -1, input.read());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -205,7 +205,7 @@ public abstract class InputStreamAbstractTest extends ObjectAbstractTest {
|
|||||||
fail("Should throw IOException");
|
fail("Should throw IOException");
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
assertTrue(e.getMessage().contains("reset"), "Wrong messge: " + e.getMessage());
|
assertTrue("Wrong messge: " + e.getMessage(), e.getMessage().contains("reset"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,10 +223,10 @@ public abstract class InputStreamAbstractTest extends ObjectAbstractTest {
|
|||||||
// No mark may either throw exception, or reset to beginning of stream.
|
// No mark may either throw exception, or reset to beginning of stream.
|
||||||
try {
|
try {
|
||||||
input.reset();
|
input.reset();
|
||||||
assertEquals(0, input.read(), "Re-read of reset data should be same");
|
assertEquals("Re-read of reset data should be same", 0, input.read());
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
assertTrue(e.getMessage().contains("mark"), "Wrong no mark IOException message");
|
assertTrue("Wrong no mark IOException message", e.getMessage().contains("mark"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,7 +249,7 @@ public abstract class InputStreamAbstractTest extends ObjectAbstractTest {
|
|||||||
|
|
||||||
// Read further
|
// Read further
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
assertEquals((position + i), input.read(), "Read After Mark [" + i + "]");
|
assertEquals("Read After Mark [" + i + "]", (position + i), input.read());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset
|
// Reset
|
||||||
@@ -257,7 +257,7 @@ public abstract class InputStreamAbstractTest extends ObjectAbstractTest {
|
|||||||
|
|
||||||
// Read from marked position
|
// Read from marked position
|
||||||
for (int i = 0; i < readlimit + 1; i++) {
|
for (int i = 0; i < readlimit + 1; i++) {
|
||||||
assertEquals((position + i), input.read(), "Read After Reset [" + i + "]");
|
assertEquals("Read After Reset [" + i + "]", (position + i), input.read());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -280,16 +280,16 @@ public abstract class InputStreamAbstractTest extends ObjectAbstractTest {
|
|||||||
|
|
||||||
// Read past marked position
|
// Read past marked position
|
||||||
for (int i = 0; i < readlimit + 1; i++) {
|
for (int i = 0; i < readlimit + 1; i++) {
|
||||||
assertEquals((position + i), input.read(), "Read After Reset [" + i + "]");
|
assertEquals("Read After Reset [" + i + "]", (position + i), input.read());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset after read limit passed, may either throw exception, or reset to last mark
|
// Reset after read limit passed, may either throw exception, or reset to last mark
|
||||||
try {
|
try {
|
||||||
input.reset();
|
input.reset();
|
||||||
assertEquals(1, input.read(), "Re-read of reset data should be same");
|
assertEquals("Re-read of reset data should be same", 1, input.read());
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
assertTrue(e.getMessage().contains("mark"), "Wrong read-limit IOException message");
|
assertTrue("Wrong read-limit IOException message", e.getMessage().contains("mark"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,29 +302,29 @@ public abstract class InputStreamAbstractTest extends ObjectAbstractTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int first = input.read();
|
int first = input.read();
|
||||||
assertTrue(first >= 0, "Expected to read positive value");
|
assertTrue("Expected to read positive value", first >= 0);
|
||||||
|
|
||||||
int readlimit = 5;
|
int readlimit = 5;
|
||||||
|
|
||||||
// Mark
|
// Mark
|
||||||
input.mark(readlimit);
|
input.mark(readlimit);
|
||||||
int read = input.read();
|
int read = input.read();
|
||||||
assertTrue(read >= 0, "Expected to read positive value");
|
assertTrue("Expected to read positive value", read >= 0);
|
||||||
|
|
||||||
assertTrue(input.read() >= 0);
|
assertTrue(input.read() >= 0);
|
||||||
assertTrue(input.read() >= 0);
|
assertTrue(input.read() >= 0);
|
||||||
|
|
||||||
input.reset();
|
input.reset();
|
||||||
assertEquals(read, input.read(), "Expected value read differs from actual");
|
assertEquals("Expected value read differs from actual", read, input.read());
|
||||||
|
|
||||||
// Reset after read limit passed, may either throw exception, or reset to last good mark
|
// Reset after read limit passed, may either throw exception, or reset to last good mark
|
||||||
try {
|
try {
|
||||||
input.reset();
|
input.reset();
|
||||||
int reRead = input.read();
|
int reRead = input.read();
|
||||||
assertTrue(reRead == read || reRead == first, "Re-read of reset data should be same as initially marked or first");
|
assertTrue("Re-read of reset data should be same as initially marked or first", reRead == read || reRead == first);
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
assertTrue(e.getMessage().contains("mark"), "Wrong read-limit IOException message");
|
assertTrue("Wrong read-limit IOException message", e.getMessage().contains("mark"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,17 +332,17 @@ public abstract class InputStreamAbstractTest extends ObjectAbstractTest {
|
|||||||
public void testSkip() throws Exception {
|
public void testSkip() throws Exception {
|
||||||
InputStream input = makeInputStream(makeOrderedArray(10));
|
InputStream input = makeInputStream(makeOrderedArray(10));
|
||||||
|
|
||||||
assertEquals(0, input.read(), "Unexpected value read");
|
assertEquals("Unexpected value read", 0, input.read());
|
||||||
assertEquals(1, input.read(), "Unexpected value read");
|
assertEquals("Unexpected value read", 1, input.read());
|
||||||
assertEquals(5, input.skip(5), "Unexpected number of bytes skipped");
|
assertEquals("Unexpected number of bytes skipped", 5, input.skip(5));
|
||||||
assertEquals(7, input.read(), "Unexpected value read");
|
assertEquals("Unexpected value read", 7, input.read());
|
||||||
|
|
||||||
assertEquals(2, input.skip(5), "Unexpected number of bytes skipped"); // only 2 left to skip
|
assertEquals("Unexpected number of bytes skipped", 2, input.skip(5)); // only 2 left to skip
|
||||||
assertEquals(-1, input.read(), "Unexpected value read after EOF");
|
assertEquals("Unexpected value read after EOF", -1, input.read());
|
||||||
|
|
||||||
// Spec says skip might return 0 or negative after EOF...
|
// Spec says skip might return 0 or negative after EOF...
|
||||||
assertTrue(input.skip(5) <= 0, "Positive value skipped after EOF"); // End of file
|
assertTrue("Positive value skipped after EOF", input.skip(5) <= 0); // End of file
|
||||||
assertEquals(-1, input.read(), "Unexpected value read after EOF");
|
assertEquals("Unexpected value read after EOF", -1, input.read());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
+3
-2
@@ -30,11 +30,12 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import static org.junit.Assert.*;
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LittleEndianDataInputStreamTest
|
* LittleEndianDataInputStreamTest
|
||||||
|
|||||||
@@ -31,12 +31,13 @@
|
|||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
import com.twelvemonkeys.lang.ObjectAbstractTest;
|
import com.twelvemonkeys.lang.ObjectAbstractTest;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* InputStreamAbstractTestCase
|
* InputStreamAbstractTestCase
|
||||||
|
|||||||
@@ -31,12 +31,12 @@
|
|||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
import com.twelvemonkeys.lang.ObjectAbstractTest;
|
import com.twelvemonkeys.lang.ObjectAbstractTest;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import static org.junit.Assert.*;
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ReaderAbstractTestCase
|
* ReaderAbstractTestCase
|
||||||
@@ -112,7 +112,7 @@ public abstract class ReaderAbstractTest extends ObjectAbstractTest {
|
|||||||
int toSkip = mInput.length();
|
int toSkip = mInput.length();
|
||||||
while (toSkip > 0) {
|
while (toSkip > 0) {
|
||||||
long skipped = reader.skip(toSkip);
|
long skipped = reader.skip(toSkip);
|
||||||
assertFalse(skipped < 0, "Skipped < 0");
|
assertFalse("Skipped < 0", skipped < 0);
|
||||||
toSkip -= skipped;
|
toSkip -= skipped;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,8 +30,10 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.Test;
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SeekableAbstractTestCase
|
* SeekableAbstractTestCase
|
||||||
|
|||||||
+23
-22
@@ -30,13 +30,14 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.EOFException;
|
import java.io.EOFException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import static org.junit.Assert.*;
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SeekableInputStreamAbstractTest
|
* SeekableInputStreamAbstractTest
|
||||||
@@ -78,25 +79,25 @@ public abstract class SeekableInputStreamAbstractTest extends InputStreamAbstrac
|
|||||||
return; // Not supported, skip test
|
return; // Not supported, skip test
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue(input.read() >= 0, "Expected to read positive value");
|
assertTrue("Expected to read positive value", input.read() >= 0);
|
||||||
|
|
||||||
int readlimit = 5;
|
int readlimit = 5;
|
||||||
|
|
||||||
// Mark
|
// Mark
|
||||||
input.mark(readlimit);
|
input.mark(readlimit);
|
||||||
int read = input.read();
|
int read = input.read();
|
||||||
assertTrue(read >= 0, "Expected to read positive value");
|
assertTrue("Expected to read positive value", read >= 0);
|
||||||
|
|
||||||
input.reset();
|
input.reset();
|
||||||
assertEquals(read, input.read(), "Expected value read differs from actual");
|
assertEquals("Expected value read differs from actual", read, input.read());
|
||||||
|
|
||||||
// Reset after read limit passed, may either throw exception, or reset to last good mark
|
// Reset after read limit passed, may either throw exception, or reset to last good mark
|
||||||
try {
|
try {
|
||||||
input.reset();
|
input.reset();
|
||||||
assertEquals(0, input.read(), "Re-read of reset data should be first");
|
assertEquals("Re-read of reset data should be first", 0, input.read());
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
assertTrue(e.getMessage().contains("mark"), "Wrong read-limit IOException message");
|
assertTrue("Wrong read-limit IOException message", e.getMessage().contains("mark"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,7 +127,7 @@ public abstract class SeekableInputStreamAbstractTest extends InputStreamAbstrac
|
|||||||
|
|
||||||
seekable.seek(pos);
|
seekable.seek(pos);
|
||||||
long streamPos = seekable.getStreamPosition();
|
long streamPos = seekable.getStreamPosition();
|
||||||
assertEquals(pos, streamPos, "Stream positon should match seeked position");
|
assertEquals("Stream positon should match seeked position", pos, streamPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -136,7 +137,7 @@ public abstract class SeekableInputStreamAbstractTest extends InputStreamAbstrac
|
|||||||
seekable.seek(pos);
|
seekable.seek(pos);
|
||||||
seekable.flushBefore(pos);
|
seekable.flushBefore(pos);
|
||||||
long flushedPos = seekable.getFlushedPosition();
|
long flushedPos = seekable.getFlushedPosition();
|
||||||
assertEquals(pos, flushedPos, "Flushed positon should match position");
|
assertEquals("Flushed positon should match position", pos, flushedPos);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
seekable.seek(pos - 1);
|
seekable.seek(pos - 1);
|
||||||
@@ -381,13 +382,13 @@ public abstract class SeekableInputStreamAbstractTest extends InputStreamAbstrac
|
|||||||
int val;
|
int val;
|
||||||
|
|
||||||
val = stream.read();
|
val = stream.read();
|
||||||
assertFalse(val == -1, "Unexepected EOF");
|
assertFalse("Unexepected EOF", val == -1);
|
||||||
val = stream.read();
|
val = stream.read();
|
||||||
assertFalse(val == -1, "Unexepected EOF");
|
assertFalse("Unexepected EOF", val == -1);
|
||||||
val = stream.read();
|
val = stream.read();
|
||||||
assertFalse(val == -1, "Unexepected EOF");
|
assertFalse("Unexepected EOF", val == -1);
|
||||||
val = stream.read();
|
val = stream.read();
|
||||||
assertFalse(val == -1, "Unexepected EOF");
|
assertFalse("Unexepected EOF", val == -1);
|
||||||
|
|
||||||
stream.seek(0);
|
stream.seek(0);
|
||||||
|
|
||||||
@@ -421,19 +422,19 @@ public abstract class SeekableInputStreamAbstractTest extends InputStreamAbstrac
|
|||||||
|
|
||||||
stream.seek(0);
|
stream.seek(0);
|
||||||
for (int i = 0; i < bytes.length; i += 2) {
|
for (int i = 0; i < bytes.length; i += 2) {
|
||||||
assertEquals(i, stream.getStreamPosition(), "Wrong stream position");
|
assertEquals("Wrong stream position", i, stream.getStreamPosition());
|
||||||
int count = stream.read(buffer, 0, 2);
|
int count = stream.read(buffer, 0, 2);
|
||||||
assertEquals(2, count);
|
assertEquals(2, count);
|
||||||
assertEquals(bytes[i], buffer[0], String.format("Wrong value read at pos %d", stream.getStreamPosition()));
|
assertEquals(String.format("Wrong value read at pos %d", stream.getStreamPosition()), bytes[i], buffer[0]);
|
||||||
assertEquals(bytes[i + 1], buffer[1], String.format("Wrong value read at pos %d", stream.getStreamPosition()));
|
assertEquals(String.format("Wrong value read at pos %d", stream.getStreamPosition()), bytes[i + 1], buffer[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
stream.seek(0);
|
stream.seek(0);
|
||||||
for (int i = 0; i < bytes.length; i++) {
|
for (int i = 0; i < bytes.length; i++) {
|
||||||
assertEquals(i, stream.getStreamPosition(), "Wrong stream position");
|
assertEquals("Wrong stream position", i, stream.getStreamPosition());
|
||||||
int actual = stream.read();
|
int actual = stream.read();
|
||||||
assertEquals(bytes[i] & 0xff, actual, String.format("Wrong value read at pos %d", stream.getStreamPosition()));
|
assertEquals(String.format("Wrong value read at pos %d", stream.getStreamPosition()), bytes[i] & 0xff, actual);
|
||||||
assertEquals(bytes[i], (byte) actual, String.format("Wrong value read at pos %d", stream.getStreamPosition()));
|
assertEquals(String.format("Wrong value read at pos %d", stream.getStreamPosition()), bytes[i], (byte) actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -455,14 +456,14 @@ public abstract class SeekableInputStreamAbstractTest extends InputStreamAbstrac
|
|||||||
try {
|
try {
|
||||||
FileUtil.read(stream); // Read until EOF
|
FileUtil.read(stream); // Read until EOF
|
||||||
|
|
||||||
assertEquals(-1, stream.read(), "EOF not reached (test case broken)");
|
assertEquals("EOF not reached (test case broken)", -1, stream.read());
|
||||||
assertFalse(closed[0], "Underlying stream closed before close");
|
assertFalse("Underlying stream closed before close", closed[0]);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
stream.close();
|
stream.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue(closed[0], "Underlying stream not closed");
|
assertTrue("Underlying stream not closed", closed[0]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,12 +31,12 @@
|
|||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
import com.twelvemonkeys.lang.StringUtil;
|
import com.twelvemonkeys.lang.StringUtil;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import static org.junit.Assert.*;
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* StringArrayReaderTestCase
|
* StringArrayReaderTestCase
|
||||||
|
|||||||
@@ -1,119 +0,0 @@
|
|||||||
package com.twelvemonkeys.io;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SubStreamTest.
|
|
||||||
*
|
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
|
||||||
* @author last modified by $Author: haraldk$
|
|
||||||
* @version $Id: SubStreamTest.java,v 1.0 07/11/2023 haraldk Exp$
|
|
||||||
*/
|
|
||||||
public class SubStreamTest {
|
|
||||||
|
|
||||||
private final Random rng = new Random(2918475687L);
|
|
||||||
|
|
||||||
@SuppressWarnings("resource")
|
|
||||||
@Test
|
|
||||||
public void testCreateNullStream() {
|
|
||||||
assertThrows(IllegalArgumentException.class, () -> {
|
|
||||||
new SubStream(null, 42);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreateNegativeLength() {
|
|
||||||
assertThrows(IllegalArgumentException.class, () -> {
|
|
||||||
new SubStream(new ByteArrayInputStream(new byte[1]), -1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testReadAll() throws IOException {
|
|
||||||
byte[] buf = new byte[128];
|
|
||||||
rng.nextBytes(buf);
|
|
||||||
|
|
||||||
try (InputStream stream = new SubStream(new ByteArrayInputStream(buf), buf.length)) {
|
|
||||||
for (byte b : buf) {
|
|
||||||
assertEquals(b, (byte) stream.read());
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEquals(-1, stream.read());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testReadAllArray() throws IOException {
|
|
||||||
byte[] buf = new byte[128];
|
|
||||||
rng.nextBytes(buf);
|
|
||||||
|
|
||||||
try (InputStream stream = new SubStream(new ByteArrayInputStream(buf), buf.length)) {
|
|
||||||
byte[] temp = new byte[buf.length / 4];
|
|
||||||
for (int i = 0; i < 4; i++) {
|
|
||||||
assertEquals(temp.length, stream.read(temp)); // Depends on ByteArrayInputStream specifics...
|
|
||||||
assertArrayEquals(Arrays.copyOfRange(buf, i * temp.length, (i + 1) * temp.length), temp);
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEquals(-1, stream.read());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSkipAll() throws IOException {
|
|
||||||
byte[] buf = new byte[128];
|
|
||||||
|
|
||||||
try (InputStream stream = new SubStream(new ByteArrayInputStream(buf), buf.length)) {
|
|
||||||
assertEquals(128, stream.skip(buf.length)); // Depends on ByteArrayInputStream specifics...
|
|
||||||
assertEquals(-1, stream.read());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("EmptyTryBlock")
|
|
||||||
@Test
|
|
||||||
public void testCloseConsumesAll() throws IOException {
|
|
||||||
ByteArrayInputStream stream = new ByteArrayInputStream(new byte[128]);
|
|
||||||
|
|
||||||
try (InputStream ignore = new SubStream(stream, 128)) {
|
|
||||||
// Nothing here...
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEquals(0, stream.available());
|
|
||||||
assertEquals(-1, stream.read());
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("EmptyTryBlock")
|
|
||||||
@Test
|
|
||||||
public void testCloseConsumesAllLongStream() throws IOException {
|
|
||||||
ByteArrayInputStream stream = new ByteArrayInputStream(new byte[256]);
|
|
||||||
|
|
||||||
try (InputStream ignore = new SubStream(stream, 128)) {
|
|
||||||
// Nothing here...
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEquals(128, stream.available());
|
|
||||||
assertEquals(0, stream.read());
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("EmptyTryBlock")
|
|
||||||
@Test
|
|
||||||
public void testCloseConsumesAllShortStream() throws IOException {
|
|
||||||
assertTimeoutPreemptively(Duration.ofMillis(500), () -> {
|
|
||||||
ByteArrayInputStream stream = new ByteArrayInputStream(new byte[13]);
|
|
||||||
|
|
||||||
try (InputStream ignore = new SubStream(stream, 42)) {
|
|
||||||
// Nothing here...
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEquals(0, stream.available());
|
|
||||||
assertEquals(-1, stream.read());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -31,13 +31,15 @@
|
|||||||
package com.twelvemonkeys.io.enc;
|
package com.twelvemonkeys.io.enc;
|
||||||
|
|
||||||
import com.twelvemonkeys.io.FileUtil;
|
import com.twelvemonkeys.io.FileUtil;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base64DecoderTest
|
* Base64DecoderTest
|
||||||
* <p/>
|
* <p/>
|
||||||
@@ -64,7 +66,7 @@ public class Base64DecoderTest extends DecoderAbstractTest {
|
|||||||
|
|
||||||
FileUtil.copy(in, bytes);
|
FileUtil.copy(in, bytes);
|
||||||
|
|
||||||
assertEquals("", new String(bytes.toByteArray()), "Strings does not match");
|
assertEquals("Strings does not match", "", new String(bytes.toByteArray()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -76,7 +78,7 @@ public class Base64DecoderTest extends DecoderAbstractTest {
|
|||||||
|
|
||||||
FileUtil.copy(in, bytes);
|
FileUtil.copy(in, bytes);
|
||||||
|
|
||||||
assertEquals("test", new String(bytes.toByteArray()), "Strings does not match");
|
assertEquals("Strings does not match", "test", new String(bytes.toByteArray()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -91,12 +93,11 @@ public class Base64DecoderTest extends DecoderAbstractTest {
|
|||||||
|
|
||||||
FileUtil.copy(in, bytes);
|
FileUtil.copy(in, bytes);
|
||||||
|
|
||||||
assertEquals(
|
assertEquals("Strings does not match",
|
||||||
"Lorem ipsum dolor sit amet, consectetuer adipiscing " +
|
"Lorem ipsum dolor sit amet, consectetuer adipiscing " +
|
||||||
"elit. Fusce est. Morbi luctus consectetuer justo. Vivamus " +
|
"elit. Fusce est. Morbi luctus consectetuer justo. Vivamus " +
|
||||||
"dapibus laoreet purus. Nunc viverra dictum nisl. Integer " +
|
"dapibus laoreet purus. Nunc viverra dictum nisl. Integer " +
|
||||||
"ullamcorper, nisi in dictum amet.",
|
"ullamcorper, nisi in dictum amet.",
|
||||||
new String(bytes.toByteArray()),
|
new String(bytes.toByteArray()));
|
||||||
"Strings does not match");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -30,12 +30,13 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.io.enc;
|
package com.twelvemonkeys.io.enc;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base64EncoderTest
|
* Base64EncoderTest
|
||||||
@@ -62,7 +63,7 @@ public class Base64EncoderTest extends EncoderAbstractTest {
|
|||||||
OutputStream out = new EncoderStream(bytes, createEncoder(), true);
|
OutputStream out = new EncoderStream(bytes, createEncoder(), true);
|
||||||
out.write(data.getBytes());
|
out.write(data.getBytes());
|
||||||
|
|
||||||
assertEquals("", new String(bytes.toByteArray()), "Strings does not match");
|
assertEquals("Strings does not match", "", new String(bytes.toByteArray()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -73,7 +74,7 @@ public class Base64EncoderTest extends EncoderAbstractTest {
|
|||||||
OutputStream out = new EncoderStream(bytes, createEncoder(), true);
|
OutputStream out = new EncoderStream(bytes, createEncoder(), true);
|
||||||
out.write(data.getBytes());
|
out.write(data.getBytes());
|
||||||
|
|
||||||
assertEquals("dGVzdA==", new String(bytes.toByteArray()), "Strings does not match");
|
assertEquals("Strings does not match", "dGVzdA==", new String(bytes.toByteArray()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -87,12 +88,11 @@ public class Base64EncoderTest extends EncoderAbstractTest {
|
|||||||
OutputStream out = new EncoderStream(bytes, createEncoder(), true);
|
OutputStream out = new EncoderStream(bytes, createEncoder(), true);
|
||||||
out.write(data.getBytes());
|
out.write(data.getBytes());
|
||||||
|
|
||||||
assertEquals(
|
assertEquals("Strings does not match",
|
||||||
"TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVlciBhZGlwaXNjaW5nIGVsaXQuIEZ1" +
|
"TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVlciBhZGlwaXNjaW5nIGVsaXQuIEZ1" +
|
||||||
"c2NlIGVzdC4gTW9yYmkgbHVjdHVzIGNvbnNlY3RldHVlciBqdXN0by4gVml2YW11cyBkYXBpYnVzIGxh" +
|
"c2NlIGVzdC4gTW9yYmkgbHVjdHVzIGNvbnNlY3RldHVlciBqdXN0by4gVml2YW11cyBkYXBpYnVzIGxh" +
|
||||||
"b3JlZXQgcHVydXMuIE51bmMgdml2ZXJyYSBkaWN0dW0gbmlzbC4gSW50ZWdlciB1bGxhbWNvcnBlciwg" +
|
"b3JlZXQgcHVydXMuIE51bmMgdml2ZXJyYSBkaWN0dW0gbmlzbC4gSW50ZWdlciB1bGxhbWNvcnBlciwg" +
|
||||||
"bmlzaSBpbiBkaWN0dW0gYW1ldC4=",
|
"bmlzaSBpbiBkaWN0dW0gYW1ldC4=",
|
||||||
new String(bytes.toByteArray()),
|
new String(bytes.toByteArray()));
|
||||||
"Strings does not match");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,13 +32,12 @@ package com.twelvemonkeys.io.enc;
|
|||||||
|
|
||||||
import com.twelvemonkeys.io.FileUtil;
|
import com.twelvemonkeys.io.FileUtil;
|
||||||
import com.twelvemonkeys.lang.ObjectAbstractTest;
|
import com.twelvemonkeys.lang.ObjectAbstractTest;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.awt.image.ImageProducer;
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import static org.junit.Assert.*;
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AbstractDecoderTest
|
* AbstractDecoderTest
|
||||||
@@ -56,13 +55,13 @@ public abstract class DecoderAbstractTest extends ObjectAbstractTest {
|
|||||||
return createDecoder();
|
return createDecoder();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test(expected = NullPointerException.class)
|
||||||
public final void testNullDecode() throws IOException {
|
public final void testNullDecode() throws IOException {
|
||||||
Decoder decoder = createDecoder();
|
Decoder decoder = createDecoder();
|
||||||
ByteArrayInputStream bytes = new ByteArrayInputStream(new byte[20]);
|
ByteArrayInputStream bytes = new ByteArrayInputStream(new byte[20]);
|
||||||
assertThrows(NullPointerException.class, () -> {
|
|
||||||
decoder.decode(bytes, null);
|
decoder.decode(bytes, null);
|
||||||
});
|
fail("null should throw NullPointerException");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -72,7 +71,7 @@ public abstract class DecoderAbstractTest extends ObjectAbstractTest {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
int count = decoder.decode(bytes, ByteBuffer.allocate(128));
|
int count = decoder.decode(bytes, ByteBuffer.allocate(128));
|
||||||
assertEquals( 0, count, "Should not be able to read any bytes");
|
assertEquals("Should not be able to read any bytes", 0, count);
|
||||||
}
|
}
|
||||||
catch (EOFException allowed) {
|
catch (EOFException allowed) {
|
||||||
// Okay
|
// Okay
|
||||||
@@ -95,7 +94,7 @@ public abstract class DecoderAbstractTest extends ObjectAbstractTest {
|
|||||||
byte[] encoded = outBytes.toByteArray();
|
byte[] encoded = outBytes.toByteArray();
|
||||||
|
|
||||||
byte[] decoded = FileUtil.read(new DecoderStream(new ByteArrayInputStream(encoded), createDecoder()));
|
byte[] decoded = FileUtil.read(new DecoderStream(new ByteArrayInputStream(encoded), createDecoder()));
|
||||||
assertArrayEquals(data, decoded, String.format("Data %d", pLength));
|
assertArrayEquals(String.format("Data %d", pLength), data, decoded);
|
||||||
|
|
||||||
InputStream in = new DecoderStream(new ByteArrayInputStream(encoded), createDecoder());
|
InputStream in = new DecoderStream(new ByteArrayInputStream(encoded), createDecoder());
|
||||||
outBytes = new ByteArrayOutputStream();
|
outBytes = new ByteArrayOutputStream();
|
||||||
@@ -104,7 +103,7 @@ public abstract class DecoderAbstractTest extends ObjectAbstractTest {
|
|||||||
in.close();
|
in.close();
|
||||||
|
|
||||||
decoded = outBytes.toByteArray();
|
decoded = outBytes.toByteArray();
|
||||||
assertArrayEquals(data, decoded, String.format("Data %d", pLength));
|
assertArrayEquals(String.format("Data %d", pLength), data, decoded);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -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 java.io.ByteArrayInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -32,13 +32,14 @@ package com.twelvemonkeys.io.enc;
|
|||||||
|
|
||||||
import com.twelvemonkeys.io.FileUtil;
|
import com.twelvemonkeys.io.FileUtil;
|
||||||
import com.twelvemonkeys.lang.ObjectAbstractTest;
|
import com.twelvemonkeys.lang.ObjectAbstractTest;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AbstractEncoderTest
|
* AbstractEncoderTest
|
||||||
@@ -72,7 +73,7 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] createData(final int pLength) {
|
private byte[] createData(final int pLength) throws Exception {
|
||||||
byte[] bytes = new byte[pLength];
|
byte[] bytes = new byte[pLength];
|
||||||
RANDOM.nextBytes(bytes);
|
RANDOM.nextBytes(bytes);
|
||||||
return bytes;
|
return bytes;
|
||||||
@@ -81,8 +82,9 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
|
|||||||
private void runStreamTest(final int pLength) throws Exception {
|
private void runStreamTest(final int pLength) throws Exception {
|
||||||
byte[] data = createData(pLength);
|
byte[] data = createData(pLength);
|
||||||
ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
|
ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
|
||||||
|
OutputStream out = new EncoderStream(outBytes, createEncoder(), true);
|
||||||
|
|
||||||
try (OutputStream out = new EncoderStream(outBytes, createEncoder(), true)) {
|
try {
|
||||||
// Provoke failure for encoders that doesn't take array offset properly into account
|
// Provoke failure for encoders that doesn't take array offset properly into account
|
||||||
int off = (data.length + 1) / 2;
|
int off = (data.length + 1) / 2;
|
||||||
out.write(data, 0, off);
|
out.write(data, 0, off);
|
||||||
@@ -90,6 +92,9 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
|
|||||||
out.write(data, off, data.length - off);
|
out.write(data, off, data.length - off);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally {
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
|
||||||
byte[] encoded = outBytes.toByteArray();
|
byte[] encoded = outBytes.toByteArray();
|
||||||
|
|
||||||
@@ -97,7 +102,7 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
|
|||||||
// System.err.println("encoded: " + Arrays.toString(encoded));
|
// System.err.println("encoded: " + Arrays.toString(encoded));
|
||||||
|
|
||||||
byte[] decoded = FileUtil.read(new DecoderStream(new ByteArrayInputStream(encoded), createCompatibleDecoder()));
|
byte[] decoded = FileUtil.read(new DecoderStream(new ByteArrayInputStream(encoded), createCompatibleDecoder()));
|
||||||
assertArrayEquals(data, decoded);
|
assertTrue(Arrays.equals(data, decoded));
|
||||||
|
|
||||||
InputStream in = new DecoderStream(new ByteArrayInputStream(encoded), createCompatibleDecoder());
|
InputStream in = new DecoderStream(new ByteArrayInputStream(encoded), createCompatibleDecoder());
|
||||||
outBytes = new ByteArrayOutputStream();
|
outBytes = new ByteArrayOutputStream();
|
||||||
@@ -111,7 +116,7 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
decoded = outBytes.toByteArray();
|
decoded = outBytes.toByteArray();
|
||||||
assertArrayEquals(data, decoded);
|
assertTrue(Arrays.equals(data, decoded));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -124,6 +129,10 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
|
|||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
fail(e.getMessage() + ": " + i);
|
fail(e.getMessage() + ": " + i);
|
||||||
}
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail(e.getMessage() + ": " + i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 100; i < 2000; i += 250) {
|
for (int i = 100; i < 2000; i += 250) {
|
||||||
@@ -134,6 +143,10 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
|
|||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
fail(e.getMessage() + ": " + i);
|
fail(e.getMessage() + ": " + i);
|
||||||
}
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail(e.getMessage() + ": " + i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 2000; i < 80000; i += 1000) {
|
for (int i = 2000; i < 80000; i += 1000) {
|
||||||
@@ -144,8 +157,14 @@ public abstract class EncoderAbstractTest extends ObjectAbstractTest {
|
|||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
fail(e.getMessage() + ": " + i);
|
fail(e.getMessage() + ": " + i);
|
||||||
}
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
fail(e.getMessage() + ": " + i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Test that the transition from byte[] to ByteBuffer didn't introduce bugs when writing to a wrapped array with offset.
|
// TODO: Test that the transition from byte[] to ByteBuffer didn't introduce bugs when writing to a wrapped array with offset.
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,110 +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 java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+48
-49
@@ -31,9 +31,9 @@
|
|||||||
package com.twelvemonkeys.io.ole2;
|
package com.twelvemonkeys.io.ole2;
|
||||||
|
|
||||||
import com.twelvemonkeys.io.MemoryCacheSeekableStream;
|
import com.twelvemonkeys.io.MemoryCacheSeekableStream;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import javax.imageio.stream.MemoryCacheImageInputStream;
|
import javax.imageio.stream.MemoryCacheImageInputStream;
|
||||||
import java.awt.image.ImageProducer;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@@ -43,8 +43,8 @@ import java.nio.ByteOrder;
|
|||||||
import java.util.SortedSet;
|
import java.util.SortedSet;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import static org.junit.Assert.*;
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
/**
|
/**
|
||||||
* CompoundDocumentTestCase
|
* CompoundDocumentTestCase
|
||||||
*
|
*
|
||||||
@@ -59,8 +59,8 @@ public class CompoundDocumentTest {
|
|||||||
protected final CompoundDocument createTestDocument() throws IOException {
|
protected final CompoundDocument createTestDocument() throws IOException {
|
||||||
URL input = getClass().getResource(SAMPLE_DATA);
|
URL input = getClass().getResource(SAMPLE_DATA);
|
||||||
|
|
||||||
assertNotNull(input, "Missing test resource!");
|
assertNotNull("Missing test resource!", input);
|
||||||
assertEquals( "file", input.getProtocol(), "Test resource not a file:// resource");
|
assertEquals("Test resource not a file:// resource", "file", input.getProtocol());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return new CompoundDocument(new File(input.toURI()));
|
return new CompoundDocument(new File(input.toURI()));
|
||||||
@@ -72,72 +72,71 @@ public class CompoundDocumentTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRoot() throws IOException {
|
public void testRoot() throws IOException {
|
||||||
try (CompoundDocument document = createTestDocument()) {
|
CompoundDocument document = createTestDocument();
|
||||||
Entry root = document.getRootEntry();
|
|
||||||
|
|
||||||
assertNotNull(root);
|
Entry root = document.getRootEntry();
|
||||||
assertEquals("Root Entry", root.getName());
|
|
||||||
assertTrue(root.isRoot());
|
assertNotNull(root);
|
||||||
assertFalse(root.isFile());
|
assertEquals("Root Entry", root.getName());
|
||||||
assertFalse(root.isDirectory());
|
assertTrue(root.isRoot());
|
||||||
assertEquals(0, root.length());
|
assertFalse(root.isFile());
|
||||||
assertNull(root.getInputStream());
|
assertFalse(root.isDirectory());
|
||||||
}
|
assertEquals(0, root.length());
|
||||||
|
assertNull(root.getInputStream());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testContents() throws IOException {
|
public void testContents() throws IOException {
|
||||||
try (CompoundDocument document = createTestDocument()) {
|
CompoundDocument document = createTestDocument();
|
||||||
Entry root = document.getRootEntry();
|
|
||||||
|
|
||||||
assertNotNull(root);
|
Entry root = document.getRootEntry();
|
||||||
|
|
||||||
SortedSet<Entry> children = new TreeSet<Entry>(root.getChildEntries());
|
assertNotNull(root);
|
||||||
assertEquals(25, children.size());
|
|
||||||
|
|
||||||
// Weirdness in the file format, name is *written backwards* 1-24 + Catalog
|
SortedSet<Entry> children = new TreeSet<Entry>(root.getChildEntries());
|
||||||
for (String name : "1,2,3,4,5,6,7,8,9,01,02,11,12,21,22,31,32,41,42,51,61,71,81,91,Catalog".split(",")) {
|
assertEquals(25, children.size());
|
||||||
assertEquals(name, children.first().getName());
|
|
||||||
children.remove(children.first());
|
// Weirdness in the file format, name is *written backwards* 1-24 + Catalog
|
||||||
}
|
for (String name : "1,2,3,4,5,6,7,8,9,01,02,11,12,21,22,31,32,41,42,51,61,71,81,91,Catalog".split(",")) {
|
||||||
|
assertEquals(name, children.first().getName());
|
||||||
|
children.remove(children.first());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test(expected = UnsupportedOperationException.class)
|
||||||
public void testChildEntriesUnmodifiable() throws IOException {
|
public void testChildEntriesUnmodifiable() throws IOException {
|
||||||
try (CompoundDocument document = createTestDocument()) {
|
CompoundDocument document = createTestDocument();
|
||||||
Entry root = document.getRootEntry();
|
|
||||||
|
|
||||||
assertNotNull(root);
|
Entry root = document.getRootEntry();
|
||||||
|
|
||||||
SortedSet<Entry> children = root.getChildEntries();
|
assertNotNull(root);
|
||||||
assertThrows(UnsupportedOperationException.class, () -> {
|
|
||||||
// Should not be allowed, as it modifies the internal structure
|
SortedSet<Entry> children = root.getChildEntries();
|
||||||
children.remove(children.first());
|
|
||||||
});
|
// Should not be allowed, as it modifies the internal structure
|
||||||
}
|
children.remove(children.first());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadThumbsCatalogFile() throws IOException {
|
public void testReadThumbsCatalogFile() throws IOException {
|
||||||
try (CompoundDocument document = createTestDocument()) {
|
CompoundDocument document = createTestDocument();
|
||||||
Entry root = document.getRootEntry();
|
|
||||||
|
|
||||||
assertNotNull(root);
|
Entry root = document.getRootEntry();
|
||||||
assertEquals(25, root.getChildEntries().size());
|
|
||||||
|
|
||||||
Entry catalog = root.getChildEntry("Catalog");
|
assertNotNull(root);
|
||||||
|
assertEquals(25, root.getChildEntries().size());
|
||||||
|
|
||||||
assertNotNull(catalog);
|
Entry catalog = root.getChildEntry("Catalog");
|
||||||
assertNotNull(catalog.getInputStream(), "Input stream may not be null");
|
|
||||||
}
|
assertNotNull(catalog);
|
||||||
|
assertNotNull("Input stream may not be null", catalog.getInputStream());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadCatalogInputStream() throws IOException {
|
public void testReadCatalogInputStream() throws IOException {
|
||||||
InputStream input = getClass().getResourceAsStream(SAMPLE_DATA);
|
InputStream input = getClass().getResourceAsStream(SAMPLE_DATA);
|
||||||
|
|
||||||
assertNotNull(input, "Missing test resource!");
|
assertNotNull("Missing test resource!", input);
|
||||||
|
|
||||||
CompoundDocument document = new CompoundDocument(input);
|
CompoundDocument document = new CompoundDocument(input);
|
||||||
Entry root = document.getRootEntry();
|
Entry root = document.getRootEntry();
|
||||||
@@ -146,14 +145,14 @@ public class CompoundDocumentTest {
|
|||||||
|
|
||||||
Entry catalog = root.getChildEntry("Catalog");
|
Entry catalog = root.getChildEntry("Catalog");
|
||||||
assertNotNull(catalog);
|
assertNotNull(catalog);
|
||||||
assertNotNull(catalog.getInputStream(), "Input stream may not be null");
|
assertNotNull("Input stream may not be null", catalog.getInputStream());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadCatalogSeekableStream() throws IOException {
|
public void testReadCatalogSeekableStream() throws IOException {
|
||||||
InputStream input = getClass().getResourceAsStream(SAMPLE_DATA);
|
InputStream input = getClass().getResourceAsStream(SAMPLE_DATA);
|
||||||
|
|
||||||
assertNotNull(input, "Missing test resource!");
|
assertNotNull("Missing test resource!", input);
|
||||||
|
|
||||||
CompoundDocument document = new CompoundDocument(new MemoryCacheSeekableStream(input));
|
CompoundDocument document = new CompoundDocument(new MemoryCacheSeekableStream(input));
|
||||||
Entry root = document.getRootEntry();
|
Entry root = document.getRootEntry();
|
||||||
@@ -162,14 +161,14 @@ public class CompoundDocumentTest {
|
|||||||
|
|
||||||
Entry catalog = root.getChildEntry("Catalog");
|
Entry catalog = root.getChildEntry("Catalog");
|
||||||
assertNotNull(catalog);
|
assertNotNull(catalog);
|
||||||
assertNotNull(catalog.getInputStream(), "Input stream may not be null");
|
assertNotNull("Input stream may not be null", catalog.getInputStream());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadCatalogImageInputStream() throws IOException {
|
public void testReadCatalogImageInputStream() throws IOException {
|
||||||
InputStream input = getClass().getResourceAsStream(SAMPLE_DATA);
|
InputStream input = getClass().getResourceAsStream(SAMPLE_DATA);
|
||||||
|
|
||||||
assertNotNull(input, "Missing test resource!");
|
assertNotNull("Missing test resource!", input);
|
||||||
|
|
||||||
MemoryCacheImageInputStream stream = new MemoryCacheImageInputStream(input);
|
MemoryCacheImageInputStream stream = new MemoryCacheImageInputStream(input);
|
||||||
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||||
@@ -184,6 +183,6 @@ public class CompoundDocumentTest {
|
|||||||
Entry catalog = root.getChildEntry("Catalog");
|
Entry catalog = root.getChildEntry("Catalog");
|
||||||
|
|
||||||
assertNotNull(catalog);
|
assertNotNull(catalog);
|
||||||
assertNotNull(catalog.getInputStream(), "Input stream may not be null");
|
assertNotNull("Input stream may not be null", catalog.getInputStream());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -31,7 +31,7 @@
|
|||||||
package com.twelvemonkeys.io.ole2;
|
package com.twelvemonkeys.io.ole2;
|
||||||
|
|
||||||
import com.twelvemonkeys.io.*;
|
import com.twelvemonkeys.io.*;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
|
|
||||||
|
|||||||
+39
-9
@@ -33,16 +33,16 @@ package com.twelvemonkeys.io.ole2;
|
|||||||
import com.twelvemonkeys.io.InputStreamAbstractTest;
|
import com.twelvemonkeys.io.InputStreamAbstractTest;
|
||||||
import com.twelvemonkeys.io.LittleEndianDataOutputStream;
|
import com.twelvemonkeys.io.LittleEndianDataOutputStream;
|
||||||
import com.twelvemonkeys.io.MemoryCacheSeekableStream;
|
import com.twelvemonkeys.io.MemoryCacheSeekableStream;
|
||||||
|
import com.twelvemonkeys.io.SeekableInputStream;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.*;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.net.URISyntaxException;
|
||||||
import java.io.IOException;
|
import java.net.URL;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import static org.junit.Assert.*;
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CompoundDocument_StreamTestCase
|
* CompoundDocument_StreamTestCase
|
||||||
@@ -51,7 +51,35 @@ import static org.junit.jupiter.api.Assertions.*;
|
|||||||
* @author last modified by $Author: haraldk$
|
* @author last modified by $Author: haraldk$
|
||||||
* @version $Id: CompoundDocument_StreamTestCase.java,v 1.0 13.10.11 12:01 haraldk Exp$
|
* @version $Id: CompoundDocument_StreamTestCase.java,v 1.0 13.10.11 12:01 haraldk Exp$
|
||||||
*/
|
*/
|
||||||
|
//@Ignore("Need proper in-memory creation of CompoundDocuments")
|
||||||
public class CompoundDocument_StreamTest extends InputStreamAbstractTest {
|
public class CompoundDocument_StreamTest extends InputStreamAbstractTest {
|
||||||
|
private static final String SAMPLE_DATA = "/Thumbs-camera.db";
|
||||||
|
|
||||||
|
protected final CompoundDocument createTestDocument() throws IOException {
|
||||||
|
URL input = getClass().getResource(SAMPLE_DATA);
|
||||||
|
|
||||||
|
assertNotNull("Missing test resource!", input);
|
||||||
|
assertEquals("Test resource not a file:// resource", "file", input.getProtocol());
|
||||||
|
|
||||||
|
try {
|
||||||
|
return new CompoundDocument(new File(input.toURI()));
|
||||||
|
}
|
||||||
|
catch (URISyntaxException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private SeekableInputStream createRealInputStream() {
|
||||||
|
try {
|
||||||
|
Entry first = createTestDocument().getRootEntry().getChildEntries().first();
|
||||||
|
assertNotNull(first);
|
||||||
|
return first.getInputStream();
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected InputStream makeInputStream(byte[] data) {
|
protected InputStream makeInputStream(byte[] data) {
|
||||||
try {
|
try {
|
||||||
@@ -154,19 +182,21 @@ public class CompoundDocument_StreamTest extends InputStreamAbstractTest {
|
|||||||
return pad;
|
return pad;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Ignore
|
||||||
@Test
|
@Test
|
||||||
public void testStreamRead() throws IOException {
|
public void testDev() throws IOException {
|
||||||
InputStream stream = makeInputStream(makeOrderedArray(32));
|
InputStream stream = makeInputStream(makeOrderedArray(32));
|
||||||
|
|
||||||
int read;
|
int read;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
while ((read = stream.read()) >= 0) {
|
while ((read = stream.read()) >= 0) {
|
||||||
|
// System.out.printf("read %02d: 0x%02x%n", count, read & 0xFF);
|
||||||
assertEquals(count, read);
|
assertEquals(count, read);
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
assertFalse(count < 32, "Short stream");
|
assertFalse("Short stream", count < 32);
|
||||||
assertFalse(count > 32, "Stream overrun");
|
assertFalse("Stream overrun", count > 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -30,8 +30,9 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.net;
|
package com.twelvemonkeys.net;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.Test;
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HTTPUtilTest
|
* HTTPUtilTest
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user